强悍的javascript手势库
/**
* Toucher
* git:https://github.com/cometwo/Toucher-1
*/ "use strict"; (function (root, factory) {
if (typeof define === "function" && define.amd) {
define([], function () {
return factory(root);
});
} else {
root.Toucher = factory(root);
}
}(window, function (root, undefined) { if (!"ontouchstart" in window) {
return;
} var _wrapped; // 获取对象上的类名
function _typeOf(obj) {
return Object.prototype.toString.call(obj).toLowerCase().slice(8, -1);
} // 获取当前时间戳
function getTimeStr() {
return +(new Date());
} // 获取位置信息
function getPosInfo(ev) {
var _touches = ev.touches;
if (!_touches || _touches.length === 0) {
return;
}
return {
pageX: ev.touches[0].pageX,
pageY: ev.touches[0].pageY,
clientX: ev.touches[0].clientX || 0,
clientY: ev.touches[0].clientY || 0
};
} // 绑定事件
function bindEv(el, type, fn) {
if (el.addEventListener) {
el.addEventListener(type, fn, false);
} else {
el["on" + type] = fn;
}
} // 解绑事件
function unBindEv(el, type, fn) {
if (el.removeEventListener) {
el.removeEventListener(type, fn, false);
} else {
el["on" + type] = fn;
}
} // 获得滑动方向
function getDirection(startX, startY, endX, endY) {
var xRes = startX - endX;
var xResAbs = Math.abs(startX - endX);
var yRes = startY - endY;
var yResAbs = Math.abs(startY - endY);
var direction = ""; if (xResAbs >= yResAbs && xResAbs > 25) {
direction = (xRes > 0) ? "Right" : "Left";
} else if (yResAbs > xResAbs && yResAbs > 25) {
direction = (yRes > 0) ? "Down" : "Up";
}
return direction;
} // 取得两点之间直线距离
function getDistance(startX, startY, endX, endY) {
return Math.sqrt(Math.pow((startX - endX), 2) + Math.pow((startY - endY), 2));
} function getLength(pos) {
return Math.sqrt(Math.pow(pos.x, 2) + Math.pow(pos.y, 2));
} function cross(v1, v2) {
return v1.x * v2.y - v2.x * v1.y;
} // 取向量
function getVector(startX, startY, endX, endY) {
return (startX * endX) + (startY * endY);
} // 获取角度
function getAngle(v1, v2) {
var mr = getLength(v1) * getLength(v2);
if (mr === 0) {
return 0
}
;
var r = getVector(v1.x, v1.y, v2.x, v2.y) / mr;
if (r > 1) {
r = 1;
}
return Math.acos(r);
} // 获取旋转的角度
function getRotateAngle(v1, v2) {
var angle = getAngle(v1, v2);
if (cross(v1, v2) > 0) {
angle *= -1;
}
return angle * 180 / Math.PI;
} // 包装一个新的事件对象
function wrapEvent(ev, obj) {
var res = {
touches: ev.touches,
type: ev.type
};
if (_typeOf(obj) === "object") {
for (var i in obj) {
res[i] = obj[i];
}
}
return res;
} // 把伪数组转换成数组
function toArray(list) {
if (list && (typeof list === "object") && isFinite(list.length) && (list.length >= 0) && (list.length === Math.floor(list.length)) && list.length < 4294967296) {
return [].slice.call(list);
}
} // 判断一个元素列表里面是否有多个元素
function isContain(collection, el) {
if (arguments.length === 2) {
return collection.some(function (elItem) {
return el.isEqualNode(elItem);
});
}
return false;
} // 生成一个随机id
function uId() {
return Math.random().toString(16).slice(2);
} // 事件模块
var Event = (function () { var storeEvents = {}; return { // add an event handle
add: function (type, el, handler) {
var selector = el,
len = arguments.length,
finalObject = {}, _type;
/**
* Event.add("swipe", function() {
* // ...
* });
*/ if (_typeOf(el) === "string") {
el = document.querySelectorAll(el);
} if (len === 2 && _typeOf(el) === "function") {
finalObject = {
handler: el
};
} else if (len === 3 && el instanceof HTMLElement || el instanceof NodeList && _typeOf(handler) === "function") {
/**
* Event.add("swipe", "#div", function(ev) {
* // ...
* });
*/
_type = _typeOf(el);
finalObject = {
type: _type,
selector: selector,
el: _type === "nodelist" ? toArray(el) : el,
handler: handler
};
} if (!storeEvents[type]) {
storeEvents[type] = [];
} storeEvents[type].push(finalObject);
}, // remove an event handle
remove: function (type, selector) {
var len = arguments.length;
if (_typeOf(type) === "string" && _typeOf(storeEvents[type]) === "array" && storeEvents[type].length) {
if (len === 1) {
storeEvents[type] = [];
} else if (len === 2) {
storeEvents[type] = storeEvents[type].filter(function (item) {
return !(item.selector === selector || _typeOf(selector) !== "string" && item.selector.isEqualNode(selector));
});
}
}
}, // trigger an event handle
trigger: function (type, el, argument) {
var len = arguments.length; /**
* Event.trigger("swipe", document.querySelector("#div"), {
* // ...
* });
*/
if (len === 3 && _typeOf(storeEvents[type]) === "array" && storeEvents[type].length) {
storeEvents[type].forEach(function (item) {
if (_typeOf(item.handler) === "function") {
if (item.type && item.el) {
argument.target = el;
if (item.type === "nodelist" && isContain(item.el, el)) {
item.handler(argument);
} else if (item.el.isEqualNode && item.el.isEqualNode(el)) {
item.handler(argument);
}
} else {
item.handler(argument);
}
}
});
}
}
};
})(); // 构造函数
function Toucher(selector) {
return new Toucher.fn.init(selector);
} Toucher.fn = Toucher.prototype = { // 修改原型构造器
constructor: Toucher, // 初始化方法
init: function (selector) {
this.el = selector instanceof HTMLElement ? selector :
_typeOf(selector) === "string" ? document.querySelector(selector) : null;
if (_typeOf(this.el) === "null") {
throw new Error("you must specify a particular selector or a particular DOM object");
}
this.scale = 1;
this.pinchStartLen = null;
this.isDoubleTap = false;
this.triggedSwipeStart = false;
this.triggedLongTap = false;
this.delta = null;
this.last = null;
this.now = null;
this.tapTimeout = null;
this.singleTapTimeout = null;
this.longTapTimeout = null;
this.swipeTimeout = null;
this.startPos = {};
this.endPos = {};
this.preTapPosition = {}; this.cfg = {
doubleTapTime: 400,
longTapTime: 700
}; // 绑定4个事件
bindEv(this.el, "touchstart", this._start.bind(this));
bindEv(this.el, "touchmove", this._move.bind(this));
bindEv(this.el, "touchcancel", this._cancel.bind(this));
bindEv(this.el, "touchend", this._end.bind(this));
return this;
}, // 提供config方法进行配置
config: function (option) {
if (_typeOf(option) !== "object") {
throw new Error("method Toucher.config must pass in an anguments which is an instance of Object, but passed in " + option.toString());
}
for (var i in option) {
this.cfg[i] = option[i];
}
return this;
}, // on方法绑定事件
/**
* var toucher = Toucher({...});
*
* toucher.on("swipe", function(ev) {
* // ...
* });
*
* // or
*
* toucher.on("tap", "#id", function(ev) {
* // ...
* });
*
* support events: singleTap,longTap,swipe,swipeStart,swipeEnd,swipeUp,swipeRight,swipeDown,swipeLeft,pinch,rotate
*
*/ on: function (type, el, callback) {
var len = arguments.length;
if(len === 2) {
Event.add(type, el);
} else {
Event.add(type, el, callback);
}
return this;
}, // off 解除绑定
/**
* var toucher = Toucher({...});
* toucher.off(type);
*
* // or
*
* toucher.off(type, selector);
*/
off: function (type, selector) {
Event.remove(type, selector);
return this;
}, // 手指刚触碰到屏幕
_start: function (ev) {
if (!ev.touches || ev.touches.length === 0) {
return;
} var self = this;
var otherToucher, v,
preV = this.preV,
target = ev.target; self.now = getTimeStr();
self.startPos = getPosInfo(ev);
self.delta = self.now - (self.last || self.now);
self.triggedSwipeStart = false;
self.triggedLongTap = false; // 快速双击
if (JSON.stringify(self.preTapPosition).length > 2 && self.delta < self.cfg.doubleTapTime && getDistance(self.preTapPosition.clientX, self.preTapPosition.clientY, self.startPos.clientX, self.startPos.clientY) < 25) {
self.isDoubleTap = true;
} // 长按定时
self.longTapTimeout = setTimeout(function () {
_wrapped = {
el: self.el,
type: "longTap",
timeStr: getTimeStr(),
position: self.startPos
};
Event.trigger("longTap", target, _wrapped);
self.triggedLongTap = true;
}, self.cfg.longTapTime); // 多个手指放到屏幕
if (ev.touches.length > 1) {
self._cancelLongTap();
otherToucher = ev.touches[1];
v = {
x: otherToucher.pageX - self.startPos.pageX,
y: otherToucher.pageY - self.startPos.pageY
};
this.preV = v;
self.pinchStartLen = getLength(v);
self.isDoubleTap = false;
} self.last = self.now;
self.preTapPosition = self.startPos; ev.preventDefault();
}, // 手指在屏幕上移动
_move: function (ev) {
if (!ev.touches || ev.touches.length === 0) {
return;
} var v, otherToucher;
var self = this;
var len = ev.touches.length;
var posNow = getPosInfo(ev);
var preV = self.preV;
var currentX = posNow.pageX;
var currentY = posNow.pageY;
var target = ev.target; // 手指移动取消长按事件和双击
self._cancelLongTap();
self.isDoubleTap = false; // 一次按下抬起只触发一次swipeStart
if (!self.triggedSwipeStart) {
_wrapped = {
el: self.el,
type: "swipeStart",
timeStr: getTimeStr(),
position: posNow
};
Event.trigger("swipeStart", target, _wrapped);
self.triggedSwipeStart = true;
} else {
_wrapped = {
el: self.el,
type: "swipe",
timeStr: getTimeStr(),
position: posNow
};
Event.trigger("swipe", target, _wrapped);
} if (len > 1) {
otherToucher = ev.touches[1];
v = {
x: otherToucher.pageX - currentX,
y: otherToucher.pageY - currentY
}; // 缩放
_wrapped = wrapEvent(ev, {
el: self.el,
type: "pinch",
scale: getLength(v) / this.pinchStartLen,
timeStr: getTimeStr(),
position: posNow
});
Event.trigger("pinch", target, _wrapped); // 旋转
_wrapped = wrapEvent(ev, {
el: self.el,
type: "rotate",
angle: getRotateAngle(v, preV),
timeStr: getTimeStr(),
position: posNow
});
Event.trigger("rotate", target, _wrapped);
ev.preventDefault();
} self.endPos = posNow;
}, // 触碰取消
_cancel: function (ev) {
clearTimeout(this.longTapTimeout);
clearTimeout(this.tapTimeout);
clearTimeout(this.swipeTimeout);
clearTimeout(self.singleTapTimeout);
}, // 手指从屏幕离开
_end: function (ev) {
if (!ev.changedTouches) {
return;
} // 取消长按
this._cancelLongTap(); var self = this;
var direction = getDirection(self.endPos.clientX, self.endPos.clientY, self.startPos.clientX, self.startPos.clientY);
var callback, target = ev.target; if (direction !== "") {
self.swipeTimeout = setTimeout(function () {
_wrapped = wrapEvent(ev, {
el: self.el,
type: "swipe",
timeStr: getTimeStr(),
position: self.endPos
});
Event.trigger("swipe", target, _wrapped); // 获取具体的swipeXyz方向
callback = self["swipe" + direction];
_wrapped = wrapEvent(ev, {
el: self.el,
type: "swipe" + direction,
timeStr: getTimeStr(),
position: self.endPos
});
Event.trigger(("swipe" + direction), target, _wrapped); _wrapped = wrapEvent(ev, {
el: self.el,
type: "swipeEnd",
timeStr: getTimeStr(),
position: self.endPos
});
Event.trigger("swipeEnd", target, _wrapped);
}, 0);
} else if (!self.triggedLongTap) {
self.tapTimeout = setTimeout(function () {
if (self.isDoubleTap) {
_wrapped = wrapEvent(ev, {
el: self.el,
type: "doubleTap",
timeStr: getTimeStr(),
position: self.startPos
});
Event.trigger("doubleTap", target, _wrapped);
clearTimeout(self.singleTapTimeout);
self.isDoubleTap = false;
} else {
self.singleTapTimeout = setTimeout(function () {
_wrapped = wrapEvent(ev, {
el: self.el,
type: "singleTap",
timeStr: getTimeStr(),
position: self.startPos
});
Event.trigger("singleTap", target, _wrapped);
}, 100);
}
}, 0);
} this.startPos = {};
this.endPos = {};
}, // 取消长按定时器
_cancelLongTap: function () {
if (_typeOf(this.longTapTimeout) !== "null") {
clearTimeout(this.longTapTimeout);
}
}
}; Toucher.fn.init.prototype = Toucher.fn; return Toucher; }));
Toucher
移动端手势库
API
Toucher("#node").config(Object).on(Object)
或者
var toucher = Toucher("#node");
toucher.config(Object);
toucher.on(name, callback);
通过
var toucher = Toucher(css selector);
来构造一个Toucher对象
用
toucher.config(Object)
来配置相关事件的触发条件
touch.on(name, callbck);
或
touch.on(name, target, callbck);
来绑定事件
完整示例
// HTML
<div id="toucher">
<div id="event"></div>
<ul>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
<li class="list-item"></li>
</ul>
</div>
// javascript
var toucher = Toucher("#toucher");
// 回调函数会在任何时候被执行,只要event.target为<div>#toucher或者其子元素
toucher.on("singleTap", function(ev) {
// ...
});
// 只有当event.target为<div>#event的时候,才会执行后面的回调函数
toucher.on("singleTap", "#event", function(ev) {
// ...
});
// 只有当event.target为<li>.list-item的时候,才会执行后面的回调函数
toucher.on("singleTap", document.querySelector(".list-item"), function(ev) {
// ...
});
支持的配置项(config)和事件列表(on)
- config
属性 | 含义 | 类型 |
---|---|---|
longTapTime | 触发longTap事件的时间(毫秒),默认700ms | Number |
doubleTapTime | 在多少毫秒内连续点击两次屏幕,触发doubleTap,默认400ms | Number |
- on
两个参数
name属性 | 含义 | callback类型 |
---|---|---|
singleTap | 轻击(单个手指) | Fucntion |
doubleTap | 手指放到屏幕(单个手指) | Fucntion |
longTap | 长按 | Fucntion |
swipe | 手指在屏幕上移动 | Fucntion |
swipeStart | 手指在屏幕上移动(只触发一次) | Fucntion |
swipeEnd | 下滑 | Fucntion |
swipeUp | 左滑 | Fucntion |
swipeRight | 右滑 | Fucntion |
swipeDown | 下滑 | Fucntion |
swipeLeft | 左滑 | Fucntion |
pinch | 缩放 | Fucntion |
rotate | 旋转 | Fucntion |
三个参数
name和callback和上表一样
target可以为具体的css selector, 也可以为具体的DOM元素(document.querySelector(selector)之类方法获取到的)
- off
解除之前绑定过的代理事件(之前怎样绑定的就怎样解除)
例子
var toucher = Toucher("#div");
// 绑定
toucher.on("singleTap", function(ev){});
// 解除绑定
toucher.off("sigleTap");
或者
var toucher = Toucher("#div");
// 绑定
toucher.on("singleTap", ".list-item", function(ev){});
// 解除绑定
toucher.off("sigleTap", ".list-item");
在线体验
扫描下方二维码或者手机直接访问https://rwson.github.io/Toucher/
- 参考腾讯AlloyTeam手势库AlloyFinger实现
强悍的javascript手势库的更多相关文章
- 实现一个javascript手势库 -- base-gesture.js
现在移动端这么普及呢,我们在手机上可以操作更多了.对于网页来说实现一些丰富的操作感觉也是非常有必要的,对吧(如果你仅仅需要click,,那就当我没说咯...)~~比如实现上下,左右滑动,点击之类的,加 ...
- Javascript触屏手势库-JTouch(更新V1.1)
作者:痞子|时间:2013-05-21|分类目录:js,javascript,jquery教程|Tag标签: javascript.jTouch|阅读(857) 7 条评论 Javascript触屏手 ...
- ♫【异步】短小强悍的JavaScript异步调用库
短小强悍的JavaScript异步调用库 var queue = function(funcs, scope) { (function next() { if(funcs.length > 0) ...
- ABP(现代ASP.NET样板开发框架)系列之21、ABP展现层——Javascript函数库
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之21.ABP展现层——Javascript函数库 ABP是“ASP.NET Boilerplate Project ...
- 超小Web手势库AlloyFinger原理
目前AlloyFinger作为腾讯手机QQ web手势解决方案,在各大项目中都发挥着作用. 感兴趣的同学可以去Github看看:https://github.com/AlloyTeam/AlloyFi ...
- Webix JavaScript UI 库可以帮你构建跨平台的HTML5 和 CSS3 程序
XB 软件公司最近发布了JavaScript UI 库Webix ,其中包含的组件超过45个,用这些组件可以构建跟HTML5 和 CSS3 兼容的程序,这些程序不仅能在个人电脑上运行,还能用在iOS. ...
- 移动端手势库hammerJS 2.0.4官方文档翻译
hammerJS是一个优秀的.轻量级的触屏设备手势库,现在已经更新到2.04版本,跟1.0版本有点天壤地别了,毕竟改写了事件名并新增了许多方法,允许同时监听多个手势.自定义识别器,也可以识别滑动方向. ...
- ECharts-基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表
ECharts http://ecomfe.github.com/echarts 基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表.创新的拖拽重计算 ...
- hammer.js手势库使用
hammer.js是一款移动端手势库组件,支持pan(拖动).swipe(滑动).tap(轻触).press(按压,即长按).doubletap(双击)等很多手势操作,提供比较完善的事件监听机制,但是 ...
随机推荐
- SVM资料
解释SMO算法比较好的文档 http://wenku.baidu.com/view/aeba21be960590c69ec3769e.html 参考博客: http://myjuno.blogbus. ...
- go接口及嵌入类型例子
书上看的.慢慢领会.. package main import ( "fmt" ) type notifier interface { notify() } type user s ...
- js复制文字
一.原理分析 浏览器提供了 copy 命令 ,可以复制选中的内容 document.execCommand("copy") 如果是输入框,可以通过 select() 方法,选中输入 ...
- PyCharm中 ImportError: No module named tensorflow
安装完 tensorflow 后在 PyCharm 中导入时显示找不到,可设置如下: PyCharm 中依次打开 File -> Settings -> Project:PycharmPr ...
- 【PAT】1005. 继续(3n+1)猜想 (25)
1005. 继续(3n+1)猜想 (25) 卡拉兹(Callatz)猜想已经在1001中给出了描述.在这个题目里,情况稍微有些复杂. 当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中 ...
- [实战]MVC5+EF6+MySql企业网盘实战(7)——文件上传
写在前面 周末了,在家继续折腾网盘,今天实现网盘文件的上传. 系列文章 [EF]vs15+ef6+mysql code first方式 [实战]MVC5+EF6+MySql企业网盘实战(1) [实战] ...
- 【LOJ】#2006. 「SCOI2015」小凸玩矩阵
题解 又是美好的一天,我今天的小目标是LOJ刷题数名次前进两名(虽然巨佬们都是BZOJ千题啊这样的><,我就在LOJ划划水吧,我永远喜欢LOJ 这道题要求K大值最小,又是什么什么大值最小, ...
- openssl源码目录结构
openssl源代码主要由eay库.ssl库.工具源码.范例源码以及测试源码组成. eay库是基础的库函数,提供了很多功能.源代码放在crypto目录下.包括如下内容: 1) asn.1 DER编码解 ...
- vscode debugger for chrome 调试webpack的配置问题
module.exports = { entry: './app.ts', output: { filename: 'bundle.js', publicPath: '/assets', devtoo ...
- 获取token
获取token 提示:openstack 这个是获取N版的方法 ,主要区别在于这个路径上(http://192.168.0.228:35357/v3/auth/tokens ),以前版本可能会是v2 ...