热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

移动设备网页中快速响应单击动作

下文中的内容主要是在iOS平台测试,参考资料也主要是Apple的关于移动平台Web页面开发的书籍。现在主流的移动设备,例如智能手机、iPad、Android平板电脑等,都没有鼠标输入设备,只是通过

下文中的内容主要是在iOS平台测试,参考资料也主要是Apple的关于移动平台Web页面开发的书籍。

现在主流的移动设备,例如智能手机、iPad、Android平板电脑等,都没有鼠标输入设备,只是通过手指触摸屏幕来实现用户交互。而传统的网页大都针对鼠标输入设备进行页面元素的事件绑定,例如:mouseover, mousemove, mousedown, mouseup, click。在移动设备上,要通过单指触摸来模拟鼠标事件。根据Apple的官方文档,单指点击的事件模拟过程如下图:

 

上图中,当单指触摸手指抬起之后,浏览器将判断你按下的元素是“可点击元素”还是“非可点击元素”。对于这两种类型的元素,浏览器将会进行不同的动作。这里出现了一个新的概念“可点击元素”。在移动Safari浏览器中,以下DOM元素会被识别为“可点击元素”:

- 超级链接
- 表单元素
- 带有区域映射的标签
- 绑定了mousemove, mousedown, mouseup, click事件的元素,无论是直接使用Onxxx=的方式绑定还是使用Javascript后期绑定的

所以,通常来说,我们在页面中直接使用DOM元素的click事件就可以让页面在移动设备浏览器中也能够正常工作。但是,如果你仔细观察,你会发现页面中绑定了click事件的DOM元素,在手指点击之后,并不能够快速的响应click事件中的Javascript代码。也就是说,从你的手指点击之后到click事件的代码执行,这之间存在一个大约300毫秒的延迟,这是因为浏览器尝试识别你是不是在进行Double Tap,即单指快速连击的动作。

如果你希望你的网页上的元素,通常是一个按钮,能够快速的响应用户的触摸动作,就像在原生应用中一样,你需要使用touch相关的事件:

- touchstart - 手指按下
- touchmove - 手指按下之后保持按下状态移动手指
- touchend - 手指按下之后抬起来,可能移动过手指,也可能没有移动
- touchcancel- 手指按下之后,抬起来之前,被系统通知打断了,例如来电、短信提醒、电量不足等

一般的页面中可以不关心touchcancel事件。但是如果你做的是针对移动设备的网页游戏,那么可能你需要关注一下touchcancel事件,避免游戏的逻辑和用户的实际操作出现不一致。

如果你为你的DOM元素绑定touchstart, touchmove, touchend事件,你会发现它对用户按下动作的响应就像原生应用一样,非常迅速,毫无延迟。

以一个按钮为例,通常我们会在touchstart事件中,改变按钮的底色,让用户知道自己点中了这个按钮,在touchend事件中,我们会真正执行实际的业务逻辑,例如通过Ajax获取后台数据等。

但是如果用户的实际意图是要滚动这个页面,只是不小心按在了这个按钮上,此时按钮的touchend依然会触发,你的代码逻辑依然会被执行,但是实际上这并不是用户预期的行为。在原生UI组件中,例如UIButton本身就带有TouchUpInside的事件,也就是说,如果你的手指按下和抬起的时候都处于这个元素的范围,才认为你是在点击这个按钮。

遗憾的是,在Javascript并没有提供DOM元素的类似TouchUpInside事件,我们只能通过一点小小的处理来尽量去识别用户是否想点击这个按钮:

1. 在touchstart事件中,记录用户点击的位置,使用clientX和clientY坐标对儿
2. 在touchmove事件中,计算当前手指的位置和开始点击的位置的差值的绝对值是否超过了一个设定的值,如果超过了设定值,则认为用户不想按这个钮了,或者用户并不是真的想按这个钮。给这个DOM元素设置一个标志位data-moved="y"。data-开头的是自定义属性,这个是HTML5规范中建议的
3. 在touchend事件中,检测这个DOM元素的data-moved属性是否为y,如果为y则不去执行既定的业务逻辑,而只是恢复按钮的样式为未点击的状态


问题:为什么不在touchend事件中直接判断手指离开时的位置和手指按下时的位置的差值?因为touchend事件和touchcancel事件中,传入到事件绑定函数的参数touchEvent对象并不包含手指位置的信息。所以只能在touchmove中跟踪手指位置。

运行效果:

 

说明:第一个写有“Click”字样的DIV是为了让你观察click事件的延迟。

完整的代码如下,为了让页面出现滚动效果,特意加入了一些无意义的文字。你可以根据这个代码进行一下封装,以便于在真正的应用中使用它。此代码在iPhone 4S/iOS 6.1.3/Safari 测试通过。

DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="/>
    <title>click vs touchtitle>
    <style type="text/css">
        div {
            font-size: 18px;
            font-family: sans-serif;
            line-height: 150%;
        }
        #c {
            margin-top: 40px;
            height: 60px;
            line-height: 60px;
            vertical-align: middle;
            text-align: center;
            background-color: lightgray;
            -webkit-user-select:none;
        }
        #c.click {
            background-color: yellow;
        }

        #t {
            margin-top: 20px;
            margin-bottom: 40px;
            height: 60px;
            line-height: 60px;
            vertical-align: middle;
            text-align: center;
            background-color: lightgray;
            -webkit-user-select:none;
        }
        #t.start {
            background-color: yellow;
        }
        #t.move {
            background-color: blue;
            color: white;
        }

    style>
    <script type="text/Javascript">
        var MOVE_THRESHOLD = 10;
        function pageOnLoad() {
            var c = document.getElementById("c");
            var t = document.getElementById("t");
            c.addEventListener("click", function(e){
                c.setAttribute("class", "click");
            });

            t.addEventListener("touchstart", function(e) {
                t.setAttribute("class", "start");
                t.textContent = "touchstart";
                t.setAttribute("data-moved", "n");
                t.setAttribute("data-startx", e.touches[0].clientX);
                t.setAttribute("data-starty", e.touches[0].clientY);
            });
            t.addEventListener("touchmove", function(e){
                t.setAttribute("class", "move");
                
                var startx = parseInt(t.getAttribute("data-startx", 10));
                var starty = parseInt(t.getAttribute("data-starty", 10));
                var deltax = e.touches[0].clientX - startx;
                var deltay = e.touches[0].clientY - starty;

                if (Math.abs(deltax) > MOVE_THRESHOLD 
                    || Math.abs(deltay) > MOVE_THRESHOLD) {
                    t.setAttribute("data-moved", "y");
                } 

            });
            t.addEventListener("touchend", function(e){
                t.setAttribute("class", "");
                t.textContent = "touchend with moved = " + t.getAttribute("data-moved");
            });
        }
    script>
head>
<body onload="pageOnLoad();">
    <div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
div>
<div id="c">Clickdiv>
<div id="t">Touchdiv>
<div>
set eiusmod tempor incidunt et labore et dolore magna aliquam. Ut enim ad minim veniam, quis nostrud exerc. Irure dolor in reprehend incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse molestaie cillum. 
Tia non ob ea soluad incom dereud facilis est er expedit distinct. Nam liber te conscient to factor tum poen legum odioque civiuda et tam. Neque pecun modut est neque nonor et imper ned libidig met, consectetur adipiscing elit, sed ut labore et dolore magna aliquam is nostrud exercitation ullam mmodo consequet.
At vver eos et accusam dignissum qui blandit est praesent. Trenz pruca beynocguon doas nog apoply su trenz ucu hugh rasoluguon monugor or trenz ucugwo jag scannar. Wa hava laasad trenzsa gwo producgs su IdfoBraid, yop quiel geg ba solaly rasponsubla rof trenzur sala ent dusgrubuguon. Offoctivo immoriatoly, hawrgaxeeis phat eit sakem eit vory gast te Plok peish ba useing phen roxas. Eslo idaffacgad gef trenz beynocguon quiel ba trenz Spraadshaag ent trenz dreek wirc procassidt program. Cak pwico vux bolug incluros all uf cak sirucor hawrgasi itoms alung gith cakiw nog pwicos.
Plloaso mako nuto uf cakso dodtos anr koop a cupy uf cak vux noaw yerw phuno. Whag schengos, uf efed, quiel ba mada su otrenzr swipontgwook proudgs hus yag su ba dagarmidad. Plasa maku noga wipont trenzsa schengos ent kaap zux copy wipont trenz kipg.
Nisl id, urna tellus vestibulum arcu, at et sit pharetra odio pede, vel libero mauris suscipit sit. Ligula dolor vel ipsum posuere consequat gravida, mauris at, in suscipit magna libero enim mauris a. Sed ut imperdiet ridiculus.
    div>
body>
html>

 


推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • IB 物理真题解析:比潜热、理想气体的应用
    本文是对2017年IB物理试卷paper 2中一道涉及比潜热、理想气体和功率的大题进行解析。题目涉及液氧蒸发成氧气的过程,讲解了液氧和氧气分子的结构以及蒸发后分子之间的作用力变化。同时,文章也给出了解题技巧,建议根据得分点的数量来合理分配答题时间。最后,文章提供了答案解析,标注了每个得分点的位置。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 判断数组是否全为0_连续子数组的最大和的解题思路及代码方法一_动态规划
    本文介绍了判断数组是否全为0以及求解连续子数组的最大和的解题思路及代码方法一,即动态规划。通过动态规划的方法,可以找出连续子数组的最大和,具体思路是尽量选择正数的部分,遇到负数则不选择进去,遇到正数则保留并继续考察。本文给出了状态定义和状态转移方程,并提供了具体的代码实现。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • This article discusses the efficiency of using char str[] and char *str and whether there is any reason to prefer one over the other. It explains the difference between the two and provides an example to illustrate their usage. ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • Server Installation for Jitsi Meet
    2019独角兽企业重金招聘Python工程师标准ServerInstallationforJitsiMeetThisdescribesconfiguringaserverji ... [详细]
  • 单目标应用:最有价值球员算法(Most Valuable Player Algorithm,MVPA)求解旅行商问题TSP
    一、最有价值球员算法最有价值球员算法(MostValuablePlayerAlgorithm,MVPA)由Bouchekara等人于20 ... [详细]
author-avatar
刘胜良昭桂家贤
这个家伙很懒,什么也没留下!
Tags | 热门标签
RankList | 热门文章
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有