《WebKit 技术内幕》学习之十三(1):移动WebKit

1 触控和手势事件

1.1 HTML5规范

        随着电容屏幕的流行,触控操作变得前所未有的流行起来。时至今日,带有多点触控功能已经成为了移动设备的标准配置,基于触控的手势识别技术也获得巨大的发展,如使用两个手指来缩放应用的大小等。所以,在移动系统中,编程需要考虑的不是鼠标事件,而是触控和手势事件,这些事件对于改善用户体验起了非常大的作用。最早将触控和手势事件引入Web领域的是苹果公司,它在iOS2.0中加入了这种支持,随后Android系统也加入了这一阵营。

        在介绍规范之前,有必要先理解一下触控、手势事件与浏览器默认行为的关系。图13-1描述了处理触控事件的可能情况,图中灰色圆圈表示的是一个触控点,当它向上移动的时候,浏览器已面临艰难选择,对于用户触发的触控事件,可能有两个地方需要使用到触控事件:第一是浏览器本身,浏览器可能希望利用这个事件完成翻页动作;另外一方面,该灰色圆圈的部分所对应的元素可能需要由自己来处理这些触控事件,而不是浏览器来处理。浏览器或者WebKit的具体处理逻辑我们在稍后会介绍到。

                                    图13-1 浏览器处理的触控事件

        目前,Web领域引入两种与触控相关的技术,其一是HTML5 Touch Events,它基本上已经成为了规范,得到了众多渲染引擎和浏览器的支持和认可。其二是Gesture Events,它是苹果公司设计并在Safari浏览器中实现的,但是没有得到其他更多浏览器的支持。下面分别来分析这两者。

        首先是HTML5 Touch Events,它已经成为推荐的规范,而且事实上也得到了两家主流移动操作系统中浏览器的支持,可以说发展得非常好,该标准主要是定义如何将原始的触控事件以特定的方式传递给JavaScript引擎,然后再传递给注册的事件响应函数。这一规范在HTML5网页应用中已经比较成熟,网页开发者可以根据规范进行定义,其中最主要的接口是TouchEvent,定义在图13-2中上半部分,表示一次传递给JavaScript注册函数的事件。

                                图13-2 HTML5 Touch Events定义的TouchEvent接口

         根据标准中的定义,TouchEvent分成4种类型:touchstart、touchmove、touchend和touchcancel。熟悉触控事件的读者可能很容易理解,它们分别表示触控点开始接触屏幕、触控点移动、触控点离开屏幕和触控点取消。最后一个类型理解起来比较困难,有时浏览器取消该触控点,可能因为其他一些原因,如它可能进入了其他的窗口等。TouchEvent当然还是继承自DOM的UIEvent,这表明它有同其他事件类似的处理方式,不同点在于这个事件有一些不同的属性。下面逐一来分析它们。

  • “touches” :表示当前屏幕中包括的所有触控点,“touches”是一个列表,如果触控点大于1,表示这是一个支持多点触控的设备。
  • “targetTouches” :表示的是当前所有起始于当前DOM元素的触控点,也就是如果一个触控点的“touchstart”事件发生的位置在该元素的区域内,那就会被包含在该列表中。
  • “changedTouches” :表示发生变化的触控点。如果类型是“touchstart”,那就包含新的触控点。如果是“touchmove”,那就包含发生移动的触控点。而“touchend”就是指触控点移出了屏幕。

每个触控点都需要包含很多信息,也就是图13-2中的众多属性,主要是标记属性的唯一ID、触控的目标(也就是对于的DOM元素)、屏幕位置、视图中的位置等,看起来还是比较直观的。有了这些接口,JavaScript代码能够非常清楚地知道每个触控点的信息,就能够像本地代码一样使用它们来满足各种应用的需求。

使用的方法并不复杂,示例代码13-1展示了如何注册监听事件的处理函数,这同其他的DOM事件区别并不是特别大,而且也只能注册在特定的元素(称为Clickable Element)上,如“div”等。因为TouchEvent有四种类型,示例代码定义了其中三种类型触控事件的处理函数。以“touchstart”为例,它会接受一个事件,就是之前定义的TouchEvent接口,为了避免同浏览器行为的冲突,可以在最开始调用“preventDefault”,这在第5章也做过介绍。后面可以根据事件来做出相应的动作。

示例代码13-1 使用HTML5 Touch Events的JavaScript代码

    var targetElement = document.getElementById("aTouchableElement");
    targetElement.addEventListener("touchstart", onTouchStartEvent, false);
    targetElement.addEventListener("touchmove", onTouchMoveEvent, false);
    targetElement.addEventListener("touchend", onTouchEndEvent,false);
    
    function onTouchStartEvent(event) {
      // 处理事件
      event.preventDefault();
      event.touches;
      event.targetTouches;
      event.changedTouches;
    }
    …

        有了这些原始的触控事件,Web开发者可以在网页中使用JavaScript代码来识别这些原始触控事件并生成手势事件,如Long Press、Pinch、Swipe、Fling等手势事件。目前有很多库提供这样的实现,如jQuery Mobile、Sencha Touch等,这极大地方便了Web开发者。

        除了原始的触控事件,苹果公司开发的Safari浏览器还支持向JavaScript代码提供Gesture Events,其含义是由浏览器来识别原始事件并将手势事件传递给JavaScript代码,当然它定义了一个新的GestureEvent接口,事件类型也分为gesturestart、gesturechange和gestureend。这里的手势事件并没有与上面定义的Pinch等采用同样的方式,而是将旋转角度和缩放大小数据传递给JavaScript,这更像是支持两个手指的触控事件。由于它的局限性和不够通用,所以并没有得到像原始触控事件一样比较广泛的支持,这里也不做过多的介绍。

1.2 工作原理

        WebKit和Chromium是如何支持触控事件的呢?其实这是比较复杂的过程,特别是某些处理方式跟鼠标事件其实还是有不一样的地方。首先事件的派送机制依然是使用第5章介绍的捕获和冒泡机制,具体参看图5-18的过程。

图13-3描述WebKit处理触控事件所使用到的一些主要类和它们之间的关系。最下层的WebWidget和WebView是WebKit的Chromium移植提供的接口,同之前介绍的一样,它们也是被Chromium项目的代码所调用,当Chromium接收到事件之后会将其传给WebViewImpl这个非常重要的类来处理。这个类大家应该很熟悉了,因为已经见过很多次面了。因为事件有多种类型,WebViewImpl类借助于PageWidgetDelegate类来处理和区分这些输入事件。经过PageWidgetDelegate类处理后的事件会调用WebViewImpl类各个事件处理接口,而WebViewImpl类的这些接口基本上使用主框(Frame)的事件处理句柄EventHandler对象来处理事件。细心的读者可以发现,图中的EventHandler包含两个函数,第一个是处理原始触控事件的函数,第二则是处理手势事件的函数。为什么会这样呢?

                                图13-3 WebKit处理触控事件的基础设施

        WebKit除了接收原始的触控事件之外,还需要它的移植或者说是浏览器提供手势事件,这些事件会触发WebKit的默认动作。例如“LongPress”事件,它表示手指在屏幕上长按一段时间,这需要浏览器将其识别成手势事件然后传递给WebKit,当WebKit接收到这个事件之后,触发自己的默认动作。这个事件同前面介绍的Safari提供的Gesture Events不是一回事,因为Safari只是提供了旋转和缩放的值给JavaScript,而这里的手势事件包括一个或者多个手指触发的动作,WebKit并不会将这里的手势事件传递给JavaScript代码,如“longPress”事件会触发浏览器弹出右键菜单的动作。

        对于多框结构的网页,事件首先由WebKit交给主框处理,WebKit会检查该事件是否需要由子框处理,如果是的话,WebKit会将该事件派发给子Frame,依此类推。这是一个递归过程,请读者结合第三章介绍的框结构来理解该过程。

        下面来分析一下在Chromium中浏览器是如何处理从系统传递过来的触控事件,并将它们转换成之后的手势事件的。这一过程稍显复杂,让我们来解释一下原因。当触控事件发生后,Chromium首先需要将触控事件保存,然后使用众多的手势识别器(Gesture Recognizers)来将其识别成手势事件。此时,如下面所描述的问题来了。

  • Chromium是否需要将所有的原始触控事件传递给网页呢?答案是否定的。如一些网页并没有注册监听函数来处理它们,那么就会造成极大的浪费,因为这些事件的传递和处理是个稍长的过程。更为致命的是,一个简单的用户操作通常有非常多的事件,这会极大地浪费CPU等资源。
  • 为什么需要Gesture Event传递给WebKit呢?因为是由浏览器识别并将识别出结果的事件传递给WebKit,这客观上能够有效减少很多事件的传递。
  • 除了发送TouchEvent和GestureEvent之外,也可能会发送MouseEvent,这是为什么?原因很简单,因为目前还存在一些网页,它们需要监听鼠标相关的事件以完成特定的动作,如果Chromium不模拟这些事件,那么网页显然不能正常的工作。但是某些鼠标事件可以模拟,如MouseDown其实对应于TouchStart,MouseUp对应用TouchEnd等。但是MouseOver就比较麻烦,比如一些网页需要根据当前鼠标悬浮事件来显示一个菜单,这对于触控设备来说,的确是一个问题。

        对于Chromium来说,事件的处理还是相当复杂的,因为需要三种类型的事件并将其传递给WebKit。由于触控事件最初是应用在移动设备上的,所以这里也主要以Chromium的Android版为例来介绍,而Chromium的桌面版对触控事件的支持目前还不是特别完善。

        在Chromium的Android版中,所有的事件都是由Android系统传送过来的,这也意味着事件的处理首先是在Java层,当然是在Browser进程的主线程中,如图13-4所示为层次结构图和相关层次中的基础设施。Java层主要包含两个类。

图13-4 Chromium处理触控事件的基础设施

  • ContentViewGestureHandler :它主要有几个任务。首先,它需要通过相应的设施来决定是否需要原始的触控事件,这其实依赖于WebKit,在每个“touchstart”事件开始的时候,需要进行HitTest检查,该动作检查当前触控点所对应的元素,然后检查该元素是否注册了监听事件的函数,如果是,需要将原始事件传送给WebKit。其次是各种手势事件的识别器,它们能够对WebKit所需要的各种手势进行识别并传递给WebKit,最后根据需要(如果有鼠标事件的监听函数)模拟鼠标事件。
  • ContentViewCore类 :主要负责将C++中的功能桥接到Java层中,并将Java中处理好的事件等信息桥接到C++代码中,它对应C++中的类是ContentViewCoreImpl。

        两个类主要负责Java层的事件处理和传递。

        在Java层之下是著名的RenderWidgetHostView类,它表示一个网页的视图。虽然这是Browser进程中的代理类,表示的是Renderer进程中相应的网页视图,它被ContentViewCoreImpl用来将事件传递给Renderer进程。后面大家应该比较清楚了,Chromium通过IPC机制来完成传递,Browser进程中的基础类是ImmediateInputRouter,而Renderer进程中的基础类是RenderWidget类。

        在Renderer进程中,RenderWidget在Chromium中表示网页的结构,它拥有前面WebKit定义的接口类WebWidget,这样,完整的过程就被这些类串联起来了,如图13-4所示。

1.3 启示和实践

        示例代码13-1其实是一个非常典型的用法,只是对于鼠标、触控等类型的事件处理过程可能需要复杂一些的步骤。

        网页除了可能需要自身处理触控事件以外,还有一个比较特别的问题,那就是对于一个为移动设备定制的网页,它可能不需要使用缩放网页(使用Pinch手势的浏览器默认行为来放大或者缩放网页)或者不需要翻滚网页(Fling手势的浏览器默认行为是滚动网页),因开发者已经考虑并设计出了适合移动设备网页阅读的网页了。那有没有办法帮助开发者和浏览器合力规避浏览器默认行为呢?

        根据上面的描述,相信读者已经看出一些端倪了,那就是网页开发者可以注册事件的响应函数,并调用“preventDefault”函数来阻碍浏览器执行默认行为。问题是这一方法只是针对某个元素而已,而不是整个网页,只是当手指触控到该元素的时候才禁止默认行为。解决这一问题的方法很简单,那就是可以将函数注册到区域更大的元素,如示例代码13-2所示的使用“body”元素就可以解决这个问题。

        示例代码13-2 使用触控事件的响应函数来禁止网页的方法和滚动的代码

    function handleEvent(event) {
      event.preventDefault();
      …
    }
    document.body.addEventListener('touchstart', handleEvent, false);
    document.body.addEventListener('touchmove', handleEvent, false);
    document.body.addEventListener('touchend', handleEvent, false);

        这个方法看起来还是需要一些代码,虽然只是短短的不到十行代码,但是除此以外还有一个更好更简单的办法,那就是使用“meta”标签。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/348880.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习(6)---Transformer

文章目录 一、介绍二、架构2.1 Multi-head Attention2.2 Encoder(编码器)2.3 Decoder(解码器) 三、Encoder和Decoder之间的传递四、Training五、其他介绍5.1 Copy Mechanism5.2 Beam Search 一、介绍 1. Transformer是一个Seq2Seq(Sequence-to-Sequence)…

Christmas Log Village Pack (Interior / Exterior) - VR/Mobile

这个圣诞主题的包包含了建造一个美丽的雪村所需的一切! 现在已更新为Unity 2019.3(与Unity 4以来的所有Unity版本兼容) 该包针对移动设备进行了优化,每个道具仅有两个纹理图集(外部和内部),2个雪地纹理,2个房屋地面纹理。该包包含大约150个预制件,还包括演示场景。 每…

【计算机网络】协议,电路交换,分组交换

定义了在两个或多个通信实体之间交换的报文格式和次序,以及报文发送和/或接收一个报文或其他事件所采取的动作.网络边缘: 端系统 (因为处在因特网的边缘) 主机 端系统 客户 client服务器 server今天大部分服务器都属于大型数据中心(data center)接入网(access network) 指将端…

项目解决方案:非执法视频监控系统项目设计方案

目 录 一、概述 (一)前言 (二)设计思路 (三)设计原则 1、实用性 2、可靠性 3、安全性 4、先进性 5、开放性 6、易管理、易维护 (四)设计依据 二、方案总…

【QT+QGIS跨平台编译】之十:【libbz2+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、libbz2介绍二、文件下载三、文件分析四、pro文件五、编译实践一、libbz2介绍 bzip2是一个基于Burrows-Wheeler 变换的无损压缩软件,压缩效果比传统的LZ77/LZ78压缩算法来得好。它是一款免费软件。可以自由分发免费使用。 bzip2能够进行高质量的数据压缩。它利用…

Spring Boot如何统计一个Bean中方法的调用次数

目录 实现思路 前置条件 实现步骤 首先我们先自定义一个注解 接下来定义一个切面 需要统计方法上使用该注解 测试 实现思路 通过AOP即可实现,通过AOP对Bean进行代理,在每次执行方法前或者后进行几次计数统计。这个主要就是考虑好如何避免并发情况…

Gradle学习笔记:Gradle的使用方法

文章目录 1.初始化项目2.构建脚本语言选择3.项目命名4.项目构建过程 1.初始化项目 创建一个test空文件夹,在该文件夹下打开终端,并执行命令:gradle init. 会有一个选项让你选择项目的类型。下面是每个选项的含义和用途: basic&am…

腾讯LLaMA Pro大模型:突破大模型微调的知识遗忘难题

引言:大模型微调中的挑战 在人工智能的发展过程中,大型语言模型(LLM)的微调(fine-tuning)始终是提升模型在特定任务上性能的关键。然而,微调过程中常面临一个主要挑战:知识遗忘。这…

【TCP】传输控制协议

前言 TCP(Transmission Control Protocol)即传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。它由IETF的RFC 793定义,为互联网中的数据通信提供了稳定的传输机制。TCP在不可靠的IP层之上实现了数据传输的可…

HCIE之BGP正则表达式(四)

BGP 一、AS-Path正则表达式数字| 等同于或的关系[]和.$ 一个字符串的结束_代表任意^一个字符串的开始()括号包围的是一个组合\ 转义字符* 零个或多个?零个或一个一个或多个 二、BGP对等体组 一、AS-Path正则表达式 正则表达式是按照一定模版匹配字符串的公式 AR3上…

数字孪生系统的难点

数字孪生系统的开发和实施涉及一些技术难点,这些难点需要综合应用多个领域的知识和技术来克服。以下是一些数字孪生系统开发中的技术难点,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公司,欢迎交流合作。 1…

React进阶 - 14(说一说”虚拟DOM“中的”Diff算法“)

本章内容 目录 一、了解 Diff 算法二、key 值的重要性三、为什么不建议使用 index 做 key 值 上一节我们初步了解了 React中的”虚拟 DOM“ ,本节我们来说一说”虚拟DOM“中的”Diff算法“ 一、了解 Diff 算法 在上一篇中,我们有讲到:当 st…

CentOS 6/7/8系统加固方案

密码失效时间 设置密码失效时间,强制定期修改密码,减少密码被泄漏和猜测风险,若使用非密码登陆方式(如密钥对)请忽略此项。 在 /etc/login.defs 中将 PASS_MAX_DAYS 参数设置为 60-180之间,如: PASS_MAX_DAYS 180 需同时执行命令设置root密码失效时间: chage --maxdays…

编程笔记 html5cssjs 057 CSS导航栏

编程笔记 html5&css&js 057 CSS导航栏 一、导航栏 链接列表二、垂直导航栏三、水平导航栏四、下拉菜单五、实例: 响应式导航栏小结 导航栏。易用的导航对于任何网站都很重要。通过使用 CSS,您可以将无聊的 HTML 菜单转换为美观的导航栏。 一、导航栏 链接…

C语言实现归并排序算法(附带源代码)

归并排序 把数据分为两段,从两段中逐个选最小的元素移入新数据段的末尾。 可从上到下或从下到上进行。 动态效果过程演示: 归并排序(Merge Sort)是一种分治算法,它将一个数组分为两个子数组,分别对这两个…

【linux】Debian防火墙

Debian系统默认没有安装防火墙,但用户可以根据需要自行选择并安装一个防火墙以增强系统安全性。 一、查看Debian 桌面系统的防火墙是否关闭 在Debian及其他基于Linux的桌面系统中,防火墙功能通常是由iptables或nftables规则集控制的,而ufw&…

金蝶云星空 ServiceGateway RCE漏洞复现

0x01 产品简介 金蝶云星空是一款云端企业资源管理(ERP)软件,为企业提供财务管理、供应链管理以及业务流程管理等一体化解决方案。金蝶云星空聚焦多组织,多利润中心的大中型企业,以 “开放、标准、社交”三大特性为数字经济时代的企业提供开放的 ERP 云平台。服务涵盖:财…

burp靶场--CSRF

burp靶场–CSRF https://portswigger.net/web-security/csrf#what-is-csrf ### 什么是 CSRF? 跨站请求伪造(也称为 CSRF)是一种 Web 安全漏洞,允许攻击者诱导用户执行他们不打算执行的操作。它允许攻击者部分规避同源策略&#…

【Python】采用OpenCV和Flask来进行网络图像推流的低延迟高刷FPS方法(项目模板)

【Python】采用OpenCV和Flask来进行网络图像推流的低延迟高刷FPS方法(项目模板) gitee项目模板: 网络图像推流项目模板(采用OpenCV和Flask来进行网络图像推流的低延迟高刷FPS方法) 前文: 【最简改进】基于…

短剧小程序开发:打造全新用户体验

随着移动互联网的普及,小程序作为一种轻量级的应用程序形式,已经成为了现代人生活中不可或缺的一部分。短剧小程序作为其中的一种,更是以其独特的魅力,吸引了大量用户。本文将探讨短剧小程序的发展背景、优势、开发流程和未来趋势…