移动端利用hammer制作移动缩放旋转功能

  目录

移动端利用hammer制作移动缩放旋转功能

移动端利用hammer制作移动缩放旋转功能

最近在项目中,有个需求是页面放大,我二话不说,直接把viewport的user-scalable属性设为yes,这倒是勉强的完成了需求的要求,但是这只是整个页面的缩放,而且初始上来的页面只能放大,并不能缩小,如果再加点需求,局部放大,这就挂了。。。。。。
于是乎,这几天有空闲的时候补了补关于移动端手势事件的一些知识,为了完成这几项功能,最终选了hammer这个插件,其实这类插件很多,大同小异,选一个稳定点,普及广的,还在维护的就行。当然了,不用插件,自己用原生的touchstart,touchmove,touchend事件也可以完成,但是各种算法还是很复杂的。
下面是几个例子:

元素的缩放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./hammer.js"></script>
<!-- <script src="./vconsole.min.js"></script> -->
<style>
#myElement {
background: silver;
height: 300px;
text-align: center;
font: 30px/300px Helvetica, Arial, sans-serif;
}
</style>
</head>
<body>
<div id="myElement"></div>
</body>
<script>
// var vConsole = new VConsole();
var scale = 1, old_scale;
var myElement = document.getElementById('myElement');
var mc = new Hammer.Manager(myElement);
// pinch
var pinch = new Hammer.Pinch();
// add to the Manager
mc.add([pinch]);
// pinch事件
mc.on("pinchstart", function(ev) {
// 注意这里1
old_scale = scale;
});
mc.on("pinchmove", function(ev) {
// 注意这里2
scale =ev.scale + old_scale-1;
// myElement.style.transform = `scale(${scale})`;
updateTransform();
});
// 更新样式函数
function updateTransform() {
var val = [
`scale(${scale})`
// `rotate(`+deg+`deg)`
];
myElement.style.transform = val.join(' ');
}
</script>
</html>

元素的旋转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./hammer.js"></script>
<!-- <script src="./vconsole.min.js"></script> -->
<style>
#myElement {
background: silver;
height: 300px;
text-align: center;
font: 30px/300px Helvetica, Arial, sans-serif;
}
</style>
</head>
<body>
<div id="myElement"></div>
</body>
<script>
// var vConsole = new VConsole();
var deg = 0, init_deg=0, rotate_deg, start_deg, pre_deg, flag_deg;
var myElement = document.getElementById('myElement');
var mc = new Hammer.Manager(myElement);
// rotate
var rotate = new Hammer.Rotate();
// add to the Manager
mc.add([rotate]);
// rotate事件
mc.on("rotatestart", function(ev) {
// 注意这里1
start_deg = ev.rotation; // 开始旋转时记录下当前rotation的角度
flag_deg = 0;
});
mc.on("rotatemove", function(ev) {
// 注意这里2
rotate_deg = ev.rotation - start_deg; // rotation的角度减去开始旋转的角度就是转过了多少角度
deg = init_deg + rotate_deg; // 元素旋转之前的角度 + 旋转了多少角度是当前元素的状态
// myElement.style.transform = `rotate(`+deg+`deg)`;
updateTransform();
});
mc.on("rotateend", function(ev) {
// 注意这里3
init_deg = deg; // 旋转结束时别忘了把当前角度状态赋值给初始化角度,以便下次旋转时接着上次的角度转
});
// 更新样式函数
function updateTransform() {
var val = [
// `scale(${scale})`
`rotate(`+deg+`deg)`
];
myElement.style.transform = val.join(' ');
}
</script>
</html>

最后,附上一个拖拽,缩放,旋转的

拖拽,缩放,旋转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="./hammer.js"></script>
<!-- <script src="./vconsole.min.js"></script> -->
<style>
#myElement {
background: silver;
width: 250px;
height: 250px;
text-align: center;
font: 30px/300px Helvetica, Arial, sans-serif;
}
</style>
</head>
<body>
<div id="myElement"></div>
</body>
<script>
var reqAnimationFrame = (function () {
return window[Hammer.prefixed(window, 'requestAnimationFrame')] || function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();

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

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();

</script>
</html>

最后,附上github源码地址,点这里