这个系列的上一篇文章差不多是两年之前的事情了,在这两年里Mobile Safari并非停滞不前,从iPhone 2.0开始Mobile Safari就加入了对多点触击的支持,现在我们就来看一下我们可以利用它来干什么。
相信很多人都看过WPF为Surface设备做的一个简单demo,也就是在桌面上显示若干张照片,你可以通过单点触击拖放,也可以通过多点触击缩放和旋转。这在iPhone上能够做到,甚至在Mobile Safari里面也能做到,因为Mobile Safari提供了一套专门用于多点触击的JavaScript接口。现在我们就来看看如何利用这套接口吧。
我们都知道,Mobile Safari自身会处理多点触击,默认行为包括滚动和缩放。我们可以接管相应的事件,同时使用e.preventDefault()
禁用浏览器默认行为,使得我们的Web应用程序能够如同WPF桌面应用一样处理多点触击。下面我们来深入看看Mobile Safari提供的多点触击事件。
首先我们要处理的是单点触击事件,禁用浏览器的滚动行为,同时为我们的照片(一个img
元素)增加拖动行为。在这里,我们需要用到touchstart
、touchmove
、touchend
事件。在这三个事件里,我们可以通过e.targetTouches
获取到用户点击的坐标,从而计算相对的位置变化。
首先,我们要在touchstart
事件里面记录下初始坐标:
var transform = {
x: 0,
y: 0,
rotation: 0,
scale: 1
};
var startX;
var startY;
var touching = false;
element.addEventListener("touchstart", function(e){
e.preventDefault();
startX = e.targetTouches[0].clientX;
startY = e.targetTouches[0].clientY;
touching = true;
});
接着,我们要在touchmove
事件里面计算相对位置变化,并且更新element
坐标:
element.addEventListener("touchmove", function(e){
e.preventDefault();
if (!touching) return;
transform.x += e.targetTouches[0].clientX - startX;
transform.y += e.targetTouches[0].clientY - startY;
updateTransform();
startX = e.targetTouches[0].clientX;
startY = e.targetTouches[0].clientY;
});
updateTransform
做了什么?现在先不讨论,我们只要把事件相关数据正确地更新到transform
的四个属性即可,如何把这些属性反映到界面上稍后再说。
最后,我们还要在touchend
事件里面处理一下标志位:
element.addEventListener("touchend", function(e){
e.preventDefault();
touching = false;
)};
就这么简单?是的。关键点也就在于touchmove
时跟踪e.targetTouches的变化,并更新transform
里面的信息。
接下来我们看看如何将transform
里面的信息作用到界面上。在没有CSS3的时代,这是极之痛苦的事情,我们需要修改元素的多个样式属性才能实现这部分的功能,并且还没办法实现旋转。现在有了CSS3,只需要修改一下transform属性就可以了:
var updateTransform = function(){
element.style.webkitTransform =
"translate(" + transform.x + "px, " + transform.y + "px) " +
"rotate(" + transform.rotation + "deg) " +
"scale(" + transform.scale + ")";
}
一句代码就把位置、旋转、缩放都设置好了!尽管我们现在还没用到旋转和缩放属性,那就让它们保持默认值吧,我们在多点触击的事件里面会设置它们的。
多点触击涉及到三个事件:gesturestart
、gesturechange
、gestureend
。这三个事件跟单点触击的三个事件非常类似,使用起来甚至可以说是更简单一些:
var startRotation;
var startScale
var gesturing = false;
element.addEventListener("gesturestart", function(e){
e.preventDefault();
startRotation = transform.rotation;
startScale = transform.scale;
gesturing = true;
});
element.addEventListener("gesturechange", function(e){
e.preventDefault();
if (!gesturing) return;
transform.rotation = startRotation + e.rotation;
transform.scale = startScale * e.scale;
updateTransform();
});
element.addEventListener("gestureend", function(e){
e.preventDefault();
gesturing = false;
});
代码确实比之前的还要少一些,重点就是正确设置transform
的两个属性,随后调用一下updateTransform
就能把最近的状态更新的界面上。
在这篇文章里,我们了解到了Mobile Safari的6个特有事件,以及如何利用这6个特有事件处理多点触击。
如果你直接使用我的代码去实现开头所说的照片拖放应用,你会发现一个小问题——在进行多点触击操作时,旋转与缩放都是很自然的,就是拖动不自然,好像拖动只跟随第一个触点似的。原因很简单,在多点触击时,管理触点移动的还是touchmove
事件,但上述代码只处理e.targetTouches[0]
,所以拖动只跟随第一个触点。
如果需要同时跟随两个触点,你需要对代码稍作改动,使得移动距离为e.targetTouches[0]
和e.targetTouches[1]
的平均值。为什么呢?如果一个触点往上移动30px,另一个触点往下移动10px,除去旋转与缩放效果外,照片的中点应该是往上移动10px的,也就是两个移动的平均值。那么我如何知道当前有多少个触点呢?看看e.targetTouches.length
就知道了。
最后,如果你关注移动设备上的Web开发,欢迎订阅我的博客: