显示标签为“safari”的博文。显示所有博文
显示标签为“safari”的博文。显示所有博文

2010年1月29日星期五

能承载移动 Web 应用的唯一浏览器: Mobile Safari

最近拿 iPhone 、 Android 、 Windows Mobile 这三个平台上的内置浏览器来做了一番对比,结果是只有 iPhone 的 Mobile Safari 能够承载现代化的移动 Web 应用,其他移动浏览器的设计思路还停留在上个世纪──能看网页就行,不存在移动应用一说。

我用来做对比的平台是 iPhone 2.0 、 Android 2.0 、 Windows Mobile 6.5 。 iPhone 之所以选择 2.0 ,是因为 3.0 的浏览器跟 2.0 是一样的,尽管我的测试是在 3.0 上做的,但对 2.0 来说也是适用的。 Windows Mobile 6.5 则是在 Windows Mobile 系列中的唯一选择,因为只有它的浏览器是 IE6 内核的,再往前的 Windows Mobile 6.1 都是 IE4 内核的。

拖放

iPhone 对拖放的支持是完美的,使用 touch 系列事件监控触击及拖动,然后使用 preventDefault 禁用浏览器的默认行为(也就是拖动页面显示区域),这就搞掂了。

Android 也支持拖放,具体方案我没有研究过,估计是改用 mouse 系列事件监控触击及拖动,然后使用 preventDefault 禁用浏览器的默认行为。

Windows Mobile 让人很绝望,连拖放也不支持。估计因为这是一个对 IE6 的封装,封装的时候就把浏览器默认行为写死了,所以你不能够通过 mouse 事件监控到任何东西,也无法禁用浏览器默认行为。

手势

最简单的手势,就是通过两个手指靠进和远离来实现放大和缩小,如果这个手势都实现不了,别的手势也就不用测试了。

iPhone 对手势的支持也是完美的,你可以直接使用 gesture 系列事件监控手势操作,也可以用 touch 系列事件监控手势中每一个触点的活动。 preventDefault 同样可以禁用浏览器默认行为(也就是缩放页面显示区域)。

Android 的浏览器不支持多点触击,尽管 Android 的另一些内置应用是支持的,这估计是因为 Google 和 Apple 纠缠不清的多点触击专利官司。因此,如果你要在 Web 应用上支持缩放,就只能在界面上放两个按钮来实现了。

Window Mobile 就不用说了,你不能期望一个缩小版的 IE6 能做什么针对移动 Web 应用的优化。

定位

iPhone 可以使用 W3C 的 Geolocation API 进行定位,而且可以跟踪定位(也就是定位变化时通过事件调用你的函数)。

Android 内置的地图可以定位,但是浏览器就是不提供这个接口,这只能说 Android 就设计为不考虑移动 Web 应用。

Windows Mobile 在安装 Google Gears 后,理论上可以支持定位。这个 ActiveX 嘛,能让你扩展浏览器,实现任何其他浏览器上难以实现的东西。唯一的问题是,这会让人产生疑问──我到底是在 Web 应用开发还是在做客户端应用开发? Microsoft 背负了太多的历史包袱,使得开发者要从客户端应用开发转向 Web 应用开发也不容易。

绘图

iPhone 同时支持 SVG 和 Canvas 。如果你需要面向对象的图形组合方式,你可以选择 SVG 。如果你需要命令式(类似 GDI )的绘图接口,你可以选择 Canvas 。

Android 只支持 Canvas ,这能够满足大多数绘图需求,唯一的问题是你不能把已经绘制的形状如同对象般删除,需要的话只能全部重绘,这就是 Canvas 跟 SVG 在使用上的主要区别。

至于 Windows Mobile ,不仅仅 SVG 和 Canvas 这样的公开标准不支持,连 VML 这样的私有标准也不支持,基本上也就无法绘图了。

样式

iPhone 支持大部分的 CSS3 特性。这是因为 iPhone 1.0 并不支持客户端应用, Apple 为了让开发者能在 Web 上实现跟客户端应用一样的视觉效果, Mobile Safari 必须做到跟 Safari 一样,支持大多数的 CSS3 特性。

Android 支持的 CSS3 特性要少一些。尽管它的浏览器用的也是 Webkit 内核,但 Webkit 管的是布局而不是渲染,在样式渲染方面 Google 的实现总是比 Apple 要慢一步。

Windows Mobile 的话……跟一个 IE6 核心的浏览器讨论 CSS3 ,这不是纯粹浪费口水吗?

体验

上面列举了一大堆的功能,但这一切只是为了实现特定的体验,所以我们真正应该关注的是一款移动浏览器设计为满足怎样的体验目标。

iPhone 上的 Mobile Safari 拥有最高的体验目标设定。在 iPhone 1.0 的时候, Apple 禁止开发人员开发本地应用,只允许开发 Web 应用,因此 Apple 必须为开发人员提供最好的 Web 应用开发平台。

此外, Apple 拥有业界最好的 HIG (人机交互指引),对比之下 Microsoft 的 UX Guide 连 Windows Mobile 都覆盖不到,而 Android 的 UI Guildlines 则显得十分山寨。由于 Apple 鼓励开发者按照 HIG 进行 iPhone 应用程序的交互设计,所以 Mobile Safari 当初就设计为帮助开发者实践 HIG 。至于其他移动浏览器,它们的设计目标则简单得多──只要用户能够在上面流畅使用面向桌面设计的网站就可以了。

正是由于设计目标的差异,使得最终产出的产品存在着巨大的差异。只有 Mobile Safari 能够承载移动 Web 应用,其他移动浏览器只是桌面浏览器在移动设备上的延伸。

2009年11月24日星期二

编写 iPhone Friendly 的 Web 应用程序 (Part 7 - 多点触击)

这个系列的上一篇文章差不多是两年之前的事情了,在这两年里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元素)增加拖动行为。在这里,我们需要用到touchstarttouchmovetouchend事件。在这三个事件里,我们可以通过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里面的信息。

CSS3变换

接下来我们看看如何将transform里面的信息作用到界面上。在没有CSS3的时代,这是极之痛苦的事情,我们需要修改元素的多个样式属性才能实现这部分的功能,并且还没办法实现旋转。现在有了CSS3,只需要修改一下transform属性就可以了:

var updateTransform = function(){
  element.style.webkitTransform
    = "translate(" + transform.x + "px, "
    + transform.y + "px) "
    + "rotate(" + transform.rotation + "deg) "
    + "scale(" + transform.scale + ")";
}


一句代码就把位置、旋转、缩放都设置好了!尽管我们现在还没用到旋转和缩放属性,那就让它们保持默认值吧,我们在多点触击的事件里面会设置它们的。

多点触击

多点触击涉及到三个事件:gesturestartgesturechangegestureend。这三个事件跟单点触击的三个事件非常类似,使用起来甚至可以说是更简单一些:

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开发,欢迎订阅我的博客: