针对多点触控设备编程的Web手势组件,快速帮助你的web程序增加手势支持,也不用再担心click 300ms的延迟了。拥有两个版本,无依赖的独立版和react版本。除了Dom对象,也可监听Canvas内元素的手势(需要Canvas引擎内置对象支持addEventListener绑定touch相关事件)。
据不完全统计,目前AlloyFinger服务于:兴趣部落、QQ群、QQ动漫、腾讯学院、TEDxTencent、 AlloyTeam、腾讯CDC等多个部门、团队和项目。
功能清单
极小的文件大小
简洁的API设计
优秀的性能
丰富的手势支持
双版本(react和独立版)
支持pinch缩放
支持rotate旋转
支持pressMove拖拽
支持doubleTap双击
支持swipe滑动
支持longTap长按
支持tap按
支持singleTap单击
快速上手
独立版使用方式:
//element为需要监听手势的dom对象 new AlloyFinger(element, { pointStart: function () { //手指触摸屏幕触发 }, multipointStart: function () { //一个手指以上触摸屏幕触发 }, rotate: function (evt) { //evt.angle代表两个手指旋转的角度 console.log(evt.angle); }, pinch: function (evt) { //evt.scale代表两个手指缩放的比例 console.log(evt.scale); }, multipointEnd: function () { //当手指离开,屏幕只剩一个手指或零个手指触发 }, pressMove: function (evt) { //evt.deltaX和evt.deltaY代表在屏幕上移动的距离 console.log(evt.deltaX); console.log(evt.deltaY); }, tap: function (evt) { //点按触发 }, doubleTap: function (evt) { //双击屏幕触发 }, longTap: function (evt) { //长按屏幕750ms触发 }, swipe: function (evt) { //evt.direction代表滑动的方向 console.log("swipe" + evt.direction); }, singleTap: function (evt) { //单击 } });
官网DEMO
http://alloyteam.github.io/AlloyFinger/
1.必须跟transformjs一起用吗?
不必须。也可以在事件回调里根据evt携带的信息使用js去操作CSS3。但是一起用,会让代码更简洁。
2.pinch、rotate事件怎么在chrome浏览器调试的?
一般用真机调试,但是也可以使用chrome浏览器,传送门 http://www.html5rocks.com/en/mobile/touch/#toc-touchdev
3.缩放的origin点设置,这里是想手在图片哪个区域操作就设置哪里为origin进行缩放?
自己去计算就是两个手指的连线的中点的坐标,
比如中点X:
pinch: function (evt) { console.log((evt.touch[0].pageX+evt.touch[1].pageX)/2); },
然后根据这个坐标和图片的坐标计算图片缩放的origin
4.拖拽位置、缩放大小是否可以限制(始终在屏幕内显示,避免出现缩到很小看不到的情况)
这个不应该有 AlloyFinger 控制。而应该由你的逻辑去控制
https://github.com/AlloyTeam/AlloyFinger
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>AlloyFinger</title> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <link rel="stylesheet" href="style/reset.css"> <link rel="stylesheet" href="style/main.css"> <script type="text/javascript" src="script/jquery-1.9.1.min.js"></script> <style> html, body{100%;height:100%;} .view_page{100%;height:100%;max-640px;position:relative;margin:0 auto;} .small_map_box{100%;} .small_map_box img{100%;display:block;} .big_map_box{position:absolute;left:0;top:0;100%;height:100%;background:rgba(0, 0, 0, 1);max-640px;z-index:100;display:none; -webkit-transform-origin:50% 20%;transform-origin:50% 20%;} .big_map_box.active{ -webkit-animation:globalSca 0.4s ease both; -moz-animation:globalSca 0.4s ease both; -ms-animation:globalSca 0.4s ease both; animation:globalSca 0.4s ease both; } @-webkit-keyframes globalSca{ 0%{-webkit-transform:scale(0);background:rgba(0, 0, 0, 0);} 100%{-webkit-transform:scale(1);background:rgba(0, 0, 0, 1);} } @-moz-keyframes globalSca{ 0%{-moz-transform:scale(0);background:rgba(0, 0, 0, 0);} 100%{-moz-transform:scale(1);background:rgba(0, 0, 0, 1);} } @-ms-keyframes globalSca{ 0%{-ms-transform:scale(0);background:rgba(0, 0, 0, 0);} 100%{-ms-transform:scale(1);background:rgba(0, 0, 0, 1);} } @keyframes globalSca{ 0%{transform:scale(0);background:rgba(0, 0, 0, 0);} 100%{transform:scale(1);background:rgba(0, 0, 0, 1);} } .big_map_box.active2{ -webkit-animation:globalScatoZero 0.4s ease both; -moz-animation:globalScatoZero 0.4s ease both; -ms-animation:globalScatoZero 0.4s ease both; animation:globalScatoZero 0.4s ease both; } @-webkit-keyframes globalScatoZero{ 0%{-webkit-transform:scale(1)} 100%{-webkit-transform:scale(1);opacity:0;} } @-moz-keyframes globalScatoZero{ 0%{-moz-transform:scale(1)} 100%{-moz-transform:scale(1);opacity:0;} } @-ms-keyframes globalScatoZero{ 0%{-ms-transform:scale(1)} 100%{-ms-transform:scale(1);opacity:0;} } @keyframes globalScatoZero{ 0%{transform:scale(1)} 100%{transform:scale(1);opacity:0;} } /*@-webkit-keyframes mymove{ 0% {-webkit-transform:scale(0);} 100% {-webkit-transform:scale(1);} } @keyframes mymove{ 0% {transform:scale(0);} 100% {transform:scale(1);} }*/ .big_map_box img{position:absolute;100%;left:0;top:50%;} #result{position:absolute;left:0;top:0;color:#fff;} </style> </head> <body> <div class="view_page"> <div class="small_map_box" id="small_map_box"> <img src="images/map_small.jpg" class="small_map" id="small_map" alt=""> </div> <div class="big_map_box"> <div id="result">fewfew</div> <img src="images/map_big.jpg" class="big_map" id="big_map" alt=""> </div> </div> </body> </html> <script type="text/javascript" src="AlloyFinger-master/alloy_finger.js"></script> <script type="text/javascript" src="AlloyFinger-master/asset/transform.js"></script> <!-- <script type="text/javascript" src="AlloyFinger-master/asset/image_loaded.js"></script> --> <script type="text/javascript" src="AlloyFinger-master/asset/to.js"></script> <script type="text/javascript"> !(function(){ var winW = window.innerWidth; var smallmap = document.getElementById("small_map_box"); var bigmap = document.getElementById("big_map"); smallmap.addEventListener("touchstart", function(){ smallmap.addEventListener("touchend", touchEnd, false); }, false); smallmap.addEventListener("touchmove", function(){ smallmap.removeEventListener("touchend", touchEnd, false); }, false); smallmap.addEventListener("touchend", touchEnd, false); function touchEnd(){ $(".big_map_box").css({"display": "block"}).removeClass("active2").addClass("active"); bigmap.style.marginTop = -Math.round((winW * 1.0533333333))/ 2 + "px"; bigmap.style.transform = "perspective(500px) matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)"; bigmap.style.webkitTransform = "perspective(500px) matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)"; } Transform(bigmap); var initScale = 1; var handlerMap = new AlloyFinger(bigmap, { //一个手指以上触摸屏幕触发 multipointStart: function () { initScale = this.scaleX; }, //当手指离开,屏幕只剩一个手指或零个手指触发 multipointEnd: function (evt) { // bigmap.onclick = function(){ // $(".big_map_box").removeClass("active"); // } // 缩放小于1的动画 if(this.scaleX < 1){ new To(this, "scaleX", 1, 300); // 绽放归位 new To(this, "scaleY", 1, 300); // 绽放归位 new To(this, "translateX", 0, 300); // 位移归位 new To(this, "translateY", 0, 300); // 位移归位 new To(this, "rotateZ", 0, 300); // 旋转归位 // 自己写的动画 // var that = this; // this.scaleX = this.scaleY = 1; // this.translateX = 0; // this.translateY = 0; // this.style.transition = "all 0.3s"; // setTimeout(function(){ // this.style.transition = "0ms"; // }.bind(this), 300); } // 限制最大的尺寸 if(this.scaleX > 3){ // this.scaleX = this.scaleY = 3; new To(this, "scaleX", 3, 200); // 绽放归位 new To(this, "scaleY", 3, 200); // 绽放归位 } }, // 旋转 // rotate: function (evt) { // evt.preventDefault(); // //evt.angle代表两个手指旋转的角度 // this.rotateZ += evt.angle; // }, // 双指缩放 pinch: function (evt) { handlerMap.isDoubleTap = true; this.scaleX = this.scaleY = initScale * evt.zoom; evt.preventDefault(); }, // 双击放大 // doubleTap: function () { // handlerMap.isDoubleTap = true; // if(this.scaleX === 1){ // new To(this, "scaleX", 3, 300); // new To(this, "scaleY", 3, 300); // }else if(this.scaleX === 3){ // new To(this, "scaleX", 1, 300); // new To(this, "scaleY", 1, 300); // new To(this, "translateX", 0, 300); // 位移归位 // new To(this, "translateY", 0, 300); // 位移归位 // } // /*else if(this.scaleX > 2){ // new To(this, "scaleX", 2, 300); // new To(this, "scaleY", 2, 300); // }*/ // }, singleTap: function (evt) { if(!handlerMap.isDoubleTap){ $(".big_map_box").removeClass("active").addClass("active2"); setTimeout(function(){ $(".big_map_box").css({"display":"none"}); },400) } }, // 移动 pressMove: function (evt) { // 取消单击事件 handlerMap.isDoubleTap = true; this.translateX += evt.deltaX; this.translateY += evt.deltaY; // 边界判断. 不让划出屏幕内 if(this.scaleX === 1){ if(this.translateX !== 0){ // this.scaleX = this.scaleY = 3; new To(this, "translateX", 0, 200); // 绽放归位 } this.translateY = 0; } evt.preventDefault(); } }) }()); function classList(e){ //CSSClassList是一个模拟DOMTokenList的javascript类 function CSSClassList(e){ //系统偷偷替我们做了: //var this = new Object(); this.e = e; } //contains方法 CSSClassList.prototype.contains = function(c){ //权威指南的方法 //检查c是否是合法的类名 if(c.length === 0 || c.indexOf(" ") != -1){ throw new Error("invalid class name:'" + c + "'"); } //首先是常规检查 var classes = this.e.className; if(!classes) return false; //e不含有类名 if(classes === c) return true; //e有一个完全匹配的类名 //否则,把c自身看做一个单词,利用正则表达式搜索c //是单词的边界 return classes.search("\b" + c + "\b") != -1; //网上正则方法 /*var classname = this.e.className, reg = new RegExp("\b" + c + "\b"); return reg.test(classname);*/ } //add()方法 CSSClassList.prototype.add = function(c){ if(this.contains(c)) return; //如果存在,则不添加 var classes = this.e.className; //判断存在class与class类里最后一个类名没有空白格 if(classes && classes[classes.length-1] != " "){ c = " " + c; this.e.className += c; //将c添加到className中 } } //remove()方法 CSSClassList.prototype.remove = function(c){ //检查c是否是合法的类名 if(c.length === 0 || c.indexOf(" ") != -1){ throw new Error("invalid class name:'" + c + "'"); } //将所有作为单词的c和多余的尾随空格全部删除 var pattern = new RegExp("\b" + c + "\b\s*","g"); this.e.className = this.e.className.replace(pattern,"") } //toggle()方法,添加返回true,删除返回false CSSClassList.prototype.toggle = function(c){ if(this.contains(c)){ //如果e.className包含c this.remove(c); //删除它 return false; }else{ this.add(c); //否则,添加它 return true; } } //返回e.className本身 CSSClassList.prototype.toString = function(){ return this.e.className; }; /*//返回在e.className中的类名 (CSSClassList原生中没有toArray方法) CSSClassList.prototype.toArray = function(){ return this.e.className.match(/w+/g) || {}; }*/ if(e.classList){ return e.classList; //如果classList存在,则返回它 }else{ return new CSSClassList(e); //否则就创建一个 } } </script>
有几个问题没有解决
1.IOS图片scale放大模糊问题
2.doubleTap与pinch触摸问题, 事件冲突
3.禁止双指放大doubleTap事件, 和在pinch上"handlerMap.isDoubleTap = true;" 来阻止singleTap事件区分开来