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

css如何让滚轮滚动时不让页面滚动_[翻译]瞧一瞧现代游览器如何工作?Part4

Insidelookatmodernwebbrowser(part4)原文地址:https:developers.google.comwebupdates201809

Inside look at modern web browser (part 4)

原文地址:https://developers.google.com/web/updates/2018/09/inside-browser-part4

用户输入进入合成器

这是了解 Chrome 如何处理您的代码以显示网站内部系列博客的最后一篇。在上一篇文章中我们了解了 Chrome 的渲染过程和合成器。本文我们将了解合成器如何实现流畅的用户交互。

从浏览器的角度看用户输入

当您听到“输入事件”时,您可能只会想到键入文本框或单击鼠标,但是从浏览器的角度来看,输入包含用户的所有手势。鼠标滚轮滚动是输入事件,触摸或鼠标悬停也是输入事件。

当用户触发手势(如屏幕上的触摸)时,浏览进程将首先接收手势。但是,浏览进程仅知道该手势发生在哪里,因为 Tab 页的内容由渲染进程处理。因此,浏览进程将事件类型(如 touchstart)及其坐标发送给渲染进程。渲染进程通过找到事件目标(EventTarget)并运行附加的事件监听器来响应输入。

b1557803f2de8fa8beaaf3f54303e024.png
图 1:输入事件从浏览器进程传递到渲染进程

合成器接收输入事件

在上一篇文章中,我们研究了合成器如何通过合并栅格化图层来平滑处理滚动。如果该页面上没有任何输入事件监听器,则合成线程可以创建一个完全独立于主线程的新合成帧。但是,如果将某些事件监听器附加到页面上怎么办?合成线程如何判断是否需要处理事件?

理解非快速滚动区域

由于 Javascript 是在主线程上运行,因此在合成页面时,合成线程会将页面上具有事件处理程序的区域标记为“非快速滚动区域”。有了这些信息,如果事件发生在该区域中,则合成线程可以确保将输入事件发送到主线程。如果输入事件来自该区域之外,则合成线程将在不等待主线程的情况下进行新帧的合成。

1b9db7116cf9c10769ed2e2a576444f8.png
图 2:非快速滚动区域发生输入事件的示意图

编写事件处理程序时的注意点

事件委托(事件代理)是一种 Web 开发中常见的事件处理模式。由于事件冒泡,您可以在最顶层的元素上附加一个事件处理程序,并根据 EventTarget 委派任务。您可能看到过或编写过如下代码:

document.body.addEventListener('touchstart', event => {if (event.target === area) {event.preventDefault();}
});

由于您只需要为所有元素编写一个事件处理程序,因此事件委托模式的功效是很吸引人的。但是,如果从浏览器的角度查看此代码,现在整个页面都被标记为不可快速滚动的区域了。这意味着,即使您的页面的某些部分不关心输入事件,合成线程也必须与主线程通信,并在每次输入事件发生时等待主线程。此时,合成线程就丧失了使页面平滑滚动的能力。

78633becb17634a2381226d9e5a2eedc.png
图 3:整个页面都是非快速滚动区时,输入事件的处理示意图

为了避免这种情况的发生,您可以在事件监听时设置 passive: true 。这会告知浏览器您仍要在主线程中监听事件,但是合成器也可以并行继续合成新的帧。

document.body.addEventListener('touchstart', event => {if (event.target === area) {event.preventDefault()}}, { passive: true });

检查事件是否可以取消

假设您在页面中有一个框,想将滚动方向限制为水平滚动。

在手势事件中使用 passive: true 可以让滚动流畅,但垂直滚动可能在你调用 preventDefault() 来阻止垂直滚动之前就开始了。您可以使用 event.cancelable 进行检查。

ec9da207073f842ce5282c68fd2c8dd1.png
图 4:页面中的部分区域固定为水平滚动

document.body.addEventListener('pointermove', event => {if (event.cancelable) {event.preventDefault(); // block the native scroll/** do what you want the application to do here*/}
}, {passive: true});

另外,您可以使用CSS规则 ​touch-action​ 来完全避免使用事件处理程序。

#area {touch-action: pan-x;
}

查找 EventTarget

当合成线程将输入事件发送到主线程时,要做的第一件事是运行命中检测以找到 EventTarget。命中检测使用在渲染阶段中生成的绘制记录来找出事件发生的点坐标对应的元素。

ad18392a76451789e9c5c49dc06709b9.png
图 5:主线程使用绘制记录查找在 x, y 的位置是什么元素

压缩分配到主线程的事件

在上一篇文章中,我们讨论了如何每秒刷新屏幕 60次,以及如何保持刷新率以实现流畅的动画。对于用户输入,常用的触摸屏设备每秒发送 60-120次触摸事件,而常用的鼠标则每秒发送 100次事件。输入事件的频率高于我们的屏幕刷新能力。

如果诸如 mousemove 这样的连续事件被以每秒钟 120次的频率传递给渲染进程的主线程,与屏幕低刷新率相比,会触发过量的命中测试和 Javascript 的回调执行。

87df995bbf8cdfaa3c189248751ee160.png
图 6:事件泛滥到帧时间轴上,导致页面卡顿

为了尽量减少对主线程过度调用,Chrome 的聚合了连续事件(如 wheel,mousewheel,mousemove,pointermove, touchmove)并延迟调度直到下一次 requestAnimationFrame 执行前。

3daf27f9a6b58098b49cb39954455132.png
图 7:与之前相同的帧时间轴,但事件被合并并延迟执行

非连续事件,如 keydown,keyup,mouseup,mousedown,touchstart,和 touchend 会被立即派发。不会进行延迟和合并。

(译者注:这也是为什么当使用扫码枪一类的输入设备时,某些键盘上正常使用的输入框,可能不再可用,特别是在使用受控方式编写 format 逻辑的情况下。)

使用 getCoalescedEvents 得到帧内事件

对于大多数 Web 应用程序,合并事件足以提供良好的用户体验。但是,如果要构建诸如画图应用程序之类的基于 touchmove 坐标路径的东西,则可能会丢失中间的坐标,而使绘制不平滑。

54566a1ad6d9150a236fd19070feebed.png
图 8:左侧是手势路径,右侧是合并结果路径,变得不平滑

window.addEventListener('pointermove', event => {const events = event.getCoalescedEvents();for (let event of events) {const x = event.pageX;const y = event.pageY;// 使用 x、y 坐标绘制路径}
});

结语

在本系列中,我们介绍了 Web 浏览器的内部工作原理。如果您从未想过为什么 DevTools 建议添加 { passive: true } 给事件处理程序,或者为什么要添加 async 标签中,那么我希望本系列文章能阐明为什么浏览器需要这些信息来提供更快,更流畅的 Web 体验。

总结

当我开始建立网站时,我几乎只关心如何编写代码以及什么可以帮助我提高效率。这些事情很重要,但是我们还应该考虑浏览器如何使用我们编写的代码。现代浏览器正持续为用户提供更好的 Web 体验而投入资源。通过组织对浏览器友好的代码,从而改善了您的用户体验。希望您加入我们的行列,以求对浏览器友好!

译者额外补充

Chrome v51 开始引入 { passive }。Chrome v55 开始 touchstart and touchmove 默认 passivetrue。Chrome v73 增加了 wheelmousewheel 事件默认 passivetrue

Pegasus:[翻译] 瞧一瞧现代游览器如何工作?Part 1​zhuanlan.zhihu.com
056e58f9a81dee5168df144946d163f0.png
Pegasus:[翻译] 瞧一瞧现代游览器如何工作?Part 2​zhuanlan.zhihu.com
a04500df813fa4794d99b59c064fb2db.png
Pegasus:[翻译] 瞧一瞧现代游览器如何工作?Part 3​zhuanlan.zhihu.com
8d058700cbf3b46f8d381520bcf4a212.png



推荐阅读
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
author-avatar
三十二号t娶我吧
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有