/**
* 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/

强悍的javascript手势库的更多相关文章

  1. 实现一个javascript手势库 -- base-gesture.js

    现在移动端这么普及呢,我们在手机上可以操作更多了.对于网页来说实现一些丰富的操作感觉也是非常有必要的,对吧(如果你仅仅需要click,,那就当我没说咯...)~~比如实现上下,左右滑动,点击之类的,加 ...

  2. Javascript触屏手势库-JTouch(更新V1.1)

    作者:痞子|时间:2013-05-21|分类目录:js,javascript,jquery教程|Tag标签: javascript.jTouch|阅读(857) 7 条评论 Javascript触屏手 ...

  3. ♫【异步】短小强悍的JavaScript异步调用库

    短小强悍的JavaScript异步调用库 var queue = function(funcs, scope) { (function next() { if(funcs.length > 0) ...

  4. ABP(现代ASP.NET样板开发框架)系列之21、ABP展现层——Javascript函数库

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之21.ABP展现层——Javascript函数库 ABP是“ASP.NET Boilerplate Project ...

  5. 超小Web手势库AlloyFinger原理

    目前AlloyFinger作为腾讯手机QQ web手势解决方案,在各大项目中都发挥着作用. 感兴趣的同学可以去Github看看:https://github.com/AlloyTeam/AlloyFi ...

  6. Webix JavaScript UI 库可以帮你构建跨平台的HTML5 和 CSS3 程序

    XB 软件公司最近发布了JavaScript UI 库Webix ,其中包含的组件超过45个,用这些组件可以构建跟HTML5 和 CSS3 兼容的程序,这些程序不仅能在个人电脑上运行,还能用在iOS. ...

  7. 移动端手势库hammerJS 2.0.4官方文档翻译

    hammerJS是一个优秀的.轻量级的触屏设备手势库,现在已经更新到2.04版本,跟1.0版本有点天壤地别了,毕竟改写了事件名并新增了许多方法,允许同时监听多个手势.自定义识别器,也可以识别滑动方向. ...

  8. ECharts-基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表

    ECharts http://ecomfe.github.com/echarts 基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表.创新的拖拽重计算 ...

  9. hammer.js手势库使用

    hammer.js是一款移动端手势库组件,支持pan(拖动).swipe(滑动).tap(轻触).press(按压,即长按).doubletap(双击)等很多手势操作,提供比较完善的事件监听机制,但是 ...

随机推荐

  1. Crypt加密函数简介(C语言)

    定义函数 char * crypt (const char *key,const char * salt); 函数说明 crypt是个密码加密函数,它是基于Data Encryption Standa ...

  2. (MHA+MYSQL-5.7增强半同步)高可用架构设计与实现

           架构使用mysql5.7版本基于GTD增强半同步并行复制配置 reploication 一主两从,使用MHA套件管理整个复制架构,实现故障自动切换高可用        优势:       ...

  3. Weex Workshop 挑战赛,等你来战!

    一个颠覆性的移动开发方式,一个匠心打造的跨平台移动开发工具,一个后App时代的生产力解放者—Weex,针对App Native开发频繁发版和多端研发的痛点,H5开发的页面稳定性.性能体验等问题,提供了 ...

  4. css控制单行文本溢出

    1.溢出属性(容器的) overflow:visible/hidden(隐藏)/scroll/auto(自动)/inherit; visible:默认值,内容不会被修剪,会成现在元素框之外: hidd ...

  5. 服务器或普通PC裸机安装 ESXI6.5

    ESXI :安装包 http://pan.baidu.com/s/1c2gM0Xq (包含注册机和其他套件,驱动打包工具) ESXI 6.5 在服务器安装比较方便,一般intel 的网卡都没多大问题, ...

  6. CentOS6.9下安装MariaDB10.2.11

    yum groupinstall -y "Development Tools" yum install -y cmake openssl-devel zlib-devel yum ...

  7. 常用的scrapy setting

    原文请参考    Scrapy 爬虫入门教程十三 Settings(设置), 讲的很详细 官网参考  Settings 设置 Scrapy 设置允许您自定义所有 Scrapy 组件的行为,包括核心,扩 ...

  8. 在Hboxlayout中组件的显示比例

    有两种方法: 在插入布局的时候设置 mainLayout->addWidget(list,1,Qt::AlignCenter); mainLayout->addWidget(stack,3 ...

  9. bzoj 1483 链表 + 启发式合并

    思路:将颜色相同的建成一个链表, 变颜色的时候进行链表的启发式合并.. 因为需要将小的接到大的上边,所以要用个f数组. #include<bits/stdc++.h> #define LL ...

  10. 几道坑人的PHP面试题 试试看看你会不会也中招

    这篇文章主要介绍了几道坑人的PHP面试题,试试看看你会不会也中招,这些题目都用了一些障眼法,需要你有一双火眼金睛哦,需要的朋友可以参考下 这几道题是在德问上看到的,感觉挺有意思,拿来给大家分享其中的陷 ...