H5案例分享:JS手势框架 —— Hammer.js

时间:2023-03-08 16:04:17

H5案例分享:JS手势框架 —— Hammer.js

JS手势框架 —— Hammer.js

一、hammer.js简介

hammerJS是一个开源的,轻量级的触屏设备javascript手势库,它可以在不需要依赖其他东西的情况下识别触摸,鼠标事件。允许同时监听多个手势、自定义识别器,也可以识别滑动方向。

二、事件

H5案例分享:JS手势框架 —— Hammer.js

1、Rotate事件:在指定的dom区域内,当两个手指或更多手指成圆型旋转时触发(就像两个手指拧螺丝一样)。该事件分别对以下事件进行监听并处理:

Rotatestart:旋转开始;Rotatemove:旋转过程;Rotateend:旋转结束;Rotatecancel:旋转取消

2、Pinch事件:在指定的dom区域内,两个手指(默认为两个手指,多指触控需要单独设置)或多个手指相对(越来越近)移动或相向(越来越远)移动时事件。该事件事以分别对以下事件进行监听并处理:

Pinchstart:多点触控开始;Pinchmove:多点触控过程;Pinchend:多点触控结束;Pinchcancel:多点触控取消;Pinchin:多点触控时两手指距离越来越近;Pinchout:多点触控时两手指距离越来越远

3、Press事件:在指定的dom区域内触屏版本的点击事件,这个事件相当于PC端的Click事件,该不能包含任何的移动,最小按压时间为500毫秒,常用于我们在手机上用的“复制、粘贴”等功能。该事件分别对以下事件进行监听并处理:

Pressup:点击事件离开时触发

4、Pan事件:在指定的dom区域内,一个手指放下并移动事件,即触屏中的拖动事件。这个事件在屏触开发中比较常用,如:左拖动、右拖动等,如手要上使用QQ时向右滑动出现功能菜单的效果。该事件还可以分别对以下事件进行监听并处理:

Panstart:拖动开始;Panmove:拖动过程;Panend:拖动结束;Pancancel:拖动取消;Panleft:向左拖动;Panright:向右拖动;Panup:向上拖动;Pandown:向下拖动

5、Tap事件:在指定的dom区域内,一个手指轻拍或点击时触发该事件(类似PC端的click)。该事件最大点击时间为250毫秒,如果超过250毫秒则按Press事件进行处理。

6、Swipe事件:在指定的dom区域内,一个手指快速的在触屏上滑动。即我们平时用到最多的滑动事件。

Swipeleft:向左滑动;Swiperight:向右滑动;Swipeup:向上滑动;Swipedown:向下滑动

三、使用方式

//创建一个新的hammer对象并且在初始化时指定要处理的dom元素

var hammertime = new Hammer(document.getElementById("test"));

//为该dom元素指定触屏移动事件

hammertime.on("pan", function (ev) {

//控制台输出

console.log(ev);

});

四、应用实例

对图片进行拖动、缩放和旋转操作(用到hammerJS中的事件Pan、Pinch和Rotate

核心代码:

var reqAnimationFrame = (function () {
  return window[Hammer.prefixed(window, 'requestAnimationFrame')] || function (callback) {
    window.setTimeout(callback, 1000 / 60);
  };
})();

var el = document.querySelector("#imgid");

var START_X = Math.round((window.innerWidth - el.offsetWidth) / 2);
var START_Y = Math.round((window.innerHeight - el.offsetHeight) / 2);

var ticking = false;
var transform; //图像效果
var timer;
var initAngle = 0; //旋转角度
var initScale = 1; //放大倍数

var mc = new Hammer.Manager(el); //用管理器 可以同时触发旋转 拖拽 移动
//var mc = new Hammer(el); //旋转和移动互斥
/**
ev.srcEvent.type touchstart touchend touchmove
ev.deltaX 手势移动位移变量
*/
mc.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
mc.add(new Hammer.Rotate({ threshold: 0 })).recognizeWith(mc.get('pan'));
mc.add(new Hammer.Pinch({ threshold: 0 })).recognizeWith([mc.get('pan'), mc.get('rotate')]);
//结束时做一些处理
mc.on("hammer.input", function(ev) {
  if(ev.isFinal) {
    console.log(START_X+" "+transform.translate.x +" "+ev.deltaX);
    START_X = transform.translate.x ;
    START_Y = transform.translate.y ;
}

});
mc.on("panstart panmove", onPan);
mc.on("rotatestart rotatemove rotateend", onRotate);
mc.on("pinchstart pinchmove", onPinch);
/**
第二次进入拖拽时 delta位移重置
移动时 初始位置startxy不动。delta增加
*/
function onPan(ev){
  if(!ev.isFinal) {
    el.className = '';
    console.log(START_X +" "+ START_Y +" | "+ev.deltaX +" "+ ev.deltaY);
   transform.translate = {
     x: START_X + ev.deltaX,
     y: START_Y + ev.deltaY
   };
   requestElementUpdate();
  }
}

function onPinch(ev){
  if(ev.type == 'pinchstart') {
    initScale = transform.scale || 1;
  }
  el.className = '';
  transform.scale = initScale * ev.scale;
  requestElementUpdate();
}

//旋转相关
var preAngle =0 ;
var tempAngleFlag=0;
var deltaAngle = 0;
var startRotateAngle = 0;

function onRotate(ev) {
  //点下第二个触控点时触发
  if(ev.type == 'rotatestart') {
    startRotateAngle = ev.rotation ;
    tempAngleFlag = 0 ;
  }
  if(ev.type == 'rotatemove'){
    if(tempAngleFlag == 0){
      preAngle = startRotateAngle;
      tempAngleFlag ++;
    }else{
      deltaAngle = ev.rotation - preAngle;
      el.className = '';
      transform.rz = 1; //非0 垂直xy轴
      transform.angle =initAngle + deltaAngle;
      requestElementUpdate();
    }
  }

  //旋转结束 记录当前图片角度
  if(ev.type =='rotateend'){
  initAngle = transform.angle;
  }
}

function updateElementTransform() {
  var value = [
'translate3d(' + transform.translate.x + 'px, ' + transform.translate.y + 'px, 0)',
'scale(' + transform.scale + ', ' + transform.scale + ')',
'rotate3d('+ transform.rx +','+ transform.ry +','+ transform.rz +','+ transform.angle + 'deg)'
];

value = value.join(" ");
  el.style.webkitTransform = value; /*为Chrome/Safari*/
  el.style.mozTransform = value; /*为Firefox*/
  el.style.transform = value; /*IE Opera?*/
  ticking = false;
}

function requestElementUpdate() {
  if(!ticking) {
    reqAnimationFrame(updateElementTransform);
    ticking = true;
  }
}

/**
初始化设置
*/
function resetElement() {

el.className = 'animate';
  transform = {
    translate: { x: START_X, y: START_Y },
    scale: 1,
    angle: 0,
    rx: 0,
    ry: 0,
    rz: 0
  };
  requestElementUpdate();
}

resetElement();

DEMO演示

H5案例分享:JS手势框架 —— Hammer.js

五、注意事项

  1. 试着避免垂直方向上的 pan/swipe

垂直方向上的平移操作一般是用来滚动页面的,而且有些(过时的)浏览器不会传递事件,导致Hammer无法识别这些手势。

  1. 在设备上做测试

有时候Hammer需要做一些调整,像swipe的速率或其它阈值,如果你在一台反应较慢的设备上做测试,那么你要保证你的回调越简单越好。

  1. 去掉Windows Phone点击时的高亮效果

在Windows Phone上的IE10和IE11里tap某元素时,会有一个小小的tap高亮效果,加上这个meta标签可以取消这种效果:

  1. 选择不了文本

Hammer设置了一个属性用来提升桌面平移操作的用户体验(UX)。常规来说,当你在桌面级浏览器上拖动页面时,你应该是可以正常选中文本的,但user-select这个CSS属性禁用了这功能。如果你在意文本选择功能,同时觉得桌面级的体验没必要太尽善尽美,你可以很轻松地取消这个默认选项——确保在创建实例之前执行:

delete Hammer.defaults.cssProps.userSelect;

5.在tap之后,导致触发了一个click事件

这种click事件我们称之为一个“幽灵点击”事件,可创建了一个小函数来避免触摸后导致click。

六、浏览器/终端的支持

Harmmer可以运行在除了IE8及其以下的任何浏览器。浏览器若对触摸行为(touch-action)提供原生支持,那么对比那些不支持HarmmerJS某些事件的浏览器,将会有更好的体验。

H5案例分享:JS手势框架 —— Hammer.js