前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >移动端滚动研究

移动端滚动研究

作者头像
张炳
发布于 2019-08-02 08:18:31
发布于 2019-08-02 08:18:31
3.3K00
代码可运行
举报
运行总次数:0
代码可运行

移动web滚动问题

在移动端如果使用局部滚动,意思就是我们的滚动在一个固定宽高的div内触发,将该div设置成overflow:scroll/auto;来形成div内部的滚动,这时我们监听div的onscroll发现触发的时机区分android和ios两种情况,具体可以看下面表格:

| 机型(内核) | body滚动 | 局部滚动 | | :-: | :-: | :-: | | ios | 不能实时触发 | 不能实时触发 | | android | 实时触发| 实时触发 | | ios wkwebview内核 | 实时触发| 实时触发 |

不能实时触发表现:只在手指触摸的屏幕上一直滑动时和滚动停止的那一刻才触发。

关于模拟滚动

概念

正常的滚动:我们平时使用的scroll,包括上面讲的滚动都属于正常滚动,利用浏览器自身提供的滚动条来实现滚动,底层是由浏览器内核控制。

模拟滚动:最典型的例子就是iscroll了,原理一般有两种:

  • 监听滚动元素的touchmove事件,当事件触发时修改元素的transform属性来实现元素的位移,让手指离开时触发touchend事件,然后采用requestanimationframe来在一个线型函数下不断的修改元素的transform来实现手指离开时的一段惯性滚动距离。
  • 监听滚动元素的touchmove事件,当事件触发时修改元素的transform属性来实现元素的位移,让手指离开时触发touchend事件,然后给元素一个css的animation,并设置好duration和function来实现手指离开时的一段惯性距离。

方案比较

第一种方案由于惯性滚动的时机时由js自己控制所以可以拿到滚动触发阶段的scrolltop值,并且滚动的回调函数onscroll在滚动的阶段都会触发。第二种方案相比第一种要劣势一些,区别在于手指离开时,采用的时css的animation来实现惯性滚动,所以无法直接触发惯性滚动过程中的onscroll事件,只有在animation结束时才可以借助animationend来获取到事件,当然也有一种方法可以实时获取滚动事件,也是借助于requestanimationframe来不断的去读取滚动元素的transform来拿到scrolltop同时触发onscroll回调。

正常滚动和模拟滚动的性能比较

模拟滚动的fps值波动较大,这样滚动起来会有明显的卡顿感觉,各位体验的时候如果滚动超过10屏之后就可以明显感觉到两着的区别。

在使用模拟滚动时,浏览器在js层面会消耗更多的性能去改变dom元素的位置,在dom复杂层级深的页面更为高,所以在长列表滚动时还要使用正常滚动更好。

滚动和下拉刷新

方案1:借助iscroll的原理,整个页面使用模拟滚动,将下拉刷新元素放在顶部,当页面滚动到顶部下拉时,下拉刷新元素随着页面的滚动出现,当手指离开时收回,此方案实现起来较为简单直接借助iscoll即可,但是使用了模拟滚动之后在正常的列表滚动时性能上不如正常滚动。

方案2:页面使用正常滚动,将下拉刷新元素放置在顶部top值为负值(正常情况下不可见),当页面处于顶部时下拉,这时监听touchmove事件,修改scrollcontent的tranlateY值,同时修改下拉刷新元素的tranlateY值,将两者同时位移来将下拉刷新元素显示出来,手指离开时(touchend)收回,这种方案满足了在正常列表滚动时使用原生的滚动节省性能,只在下拉刷新时使用模拟滚动来实现效果。

方案3:方案2的改良版,唯一不同是将下拉刷新元素和scrollcontent放在一个div里,将下拉刷新元素的margintop设为负值,在下拉刷新时,只需要修改scrollcontent一个元素的tranlateY值即可实现下拉,在性能上要比方案2好。

还会有一个性能上的问题就是:当页面的列表过长,dom元素过多时,在模拟滚动,下拉刷新这段时间内,页面也会有卡顿现象,这里采取了一个优化策略即:

  • 列表较长时dom数量较多时,在触发下拉刷新的时机时将页面视窗之外的dom元素隐藏或者存放在fragment里面。
  • 在刷新完成之后手指离开(touchend)时将隐藏的元素显示出来。
  • 需要注意的是,隐藏和显示视窗外的元素这个操作在下拉刷新时只会执行一次,并且只有在下拉刷新时才会执行。

下面介绍如何去优化scroll事件的触发,避免scroll事件过度消耗资源:

防抖(Debouncing)和节流(Throttling)

scroll 事件本身会触发页面的重新渲染,同时 scroll 事件的 handler 又会被高频度的触发, 因此事件的 handler 内部不应该有复杂操作,例如 DOM 操作就不应该放在事件处理中。 特别是针对此类高频度触发事件问题(例如页面 scroll ,屏幕 resize,监听用户输入等)。

防抖(Debouncing)

防抖技术即是可以把多个顺序地调用合并成一次,也就是在一定时间内,规定事件被触发的次数。

节流(Throttling)

防抖函数确实不错,但是也存在问题,譬如图片的懒加载,我希望在下滑过程中图片不断的被加载出来,而不是只有当我停止下滑时候,图片才被加载出来。又或者下滑时候的数据的 ajax 请求加载也是同理。这个时候,我们希望即使页面在不断被滚动,但是滚动 handler 也可以以一定的频率被触发(譬如 250ms 触发一次),这类场景,就要用到另一种技巧,称为节流函数(throttling)。

节流函数,只允许一个函数在 X 毫秒内执行一次。

与防抖相比,节流函数最主要的不同在于它保证在 X 毫秒内至少执行一次我们希望触发的事件 handler。

关于防抖动与节流,我的博客文章也有提及。

使用rAF(requestAnimationFrame)触发滚动事件

如果页面只需要兼容高版本浏览器或应用在移动端,又或者页面需要追求高精度的效果,那么可以使用浏览器的原生方法 rAF(requestAnimationFrame)。

window.requestAnimationFrame() 这个方法是用来在页面重绘之前,通知浏览器调用一个指定的函数。这个方法接受一个函数为参,该函数会在重绘前调用。

rAF 常用于 web 动画的制作,用于准确控制页面的帧刷新渲染,让动画效果更加流畅,当然它的作用不仅仅局限于动画制作,我们可以利用它的特性将它视为一个定时器。(当然它不是定时器)

通常来说,rAF 被调用的频率是每秒 60 次,也就是 1000/60 ,触发频率大概是 16.7ms 。(当执行复杂操作时,当它发现无法维持 60fps 的频率时,它会把频率降低到 30fps 来保持帧数的稳定。)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var ticking = false; // rAF 触发锁
 
function onScroll(){
  if(!ticking) {
    requestAnimationFrame(realFunc);
    ticking = true;
  }
}
 
function realFunc(){
    // do something...
    console.log("Success");
    ticking = false;
}
// 滚动事件监听
window.addEventListener('scroll', onScroll, false);

实现以16.7ms 触发一次 handler,降低了可控性,但是提升了性能和精确度。

从本质上而言,我们应该尽量去精简 scroll 事件的 handler ,将一些变量的初始化、不依赖于滚动位置变化的计算等都应当在 scroll 事件外提前就绪。

避免在scroll 事件中修改样式属性 / 将样式操作从 scroll 事件中剥离

输入事件处理函数,比如 scroll / touch 事件的处理,都会在 requestAnimationFrame 之前被调用执行。

因此,如果你在 scroll 事件的处理函数中做了修改样式属性的操作,那么这些操作会被浏览器暂存起来。然后在调用 requestAnimationFrame 的时候,如果你在一开始做了读取样式属性的操作,那么这将会导致触发浏览器的强制同步布局。

滑动过程中尝试使用 pointer-events: none 禁止鼠标事件

pointer-events 是一个 CSS 属性,可以有多个不同的值,大概的意思就是禁止鼠标行为,应用了该属性后,譬如鼠标点击,hover 等功能都将失效,即是元素不会成为鼠标事件的 target。

pointer-events: none 可用来提高滚动时的帧频。的确,当滚动时,鼠标悬停在某些元素上,则触发其上的 hover 效果,然而这些影响通常不被用户注意,并多半导致滚动出现问题。对 body 元素应用 pointer-events: none ,禁用了包括 hover 在内的鼠标事件,从而提高滚动性能。

大概的做法就是在页面滚动的时候, 给 添加上 .disable-hover 样式,那么在滚动停止之前, 所有鼠标事件都将被禁止。当滚动结束之后,再移除该属性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// css 代码
.disable-hover,
.disable-hover * {
  pointer-events: none !important;
}
// js 代码
var body = document.body,
    timer;
window.addEventListener('scroll', function() {
  clearTimeout(timer);
  if(!body.classList.contains('disable-hover')) {
    body.classList.add('disable-hover')
  }
  timer = setTimeout(function(){
    body.classList.remove('disable-hover')
  },500);
}, false);

参考 移动 Web 的滚动,高性能滚动及页面渲染优化

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【前端性能】高性能滚动 scroll 及页面渲染优化
最近在研究页面渲染及web动画的性能问题,以及拜读《CSS SECRET》(CSS揭秘)这本大作。 本文主要想谈谈页面优化之滚动优化。 主要内容包括了为何需要优化滚动事件,滚动与页面渲染的关系,节流与防抖,pointer-events:none 优化滚动。因为本文涉及了很多很多基础,可以对照上面的知识点,选择性跳到相应地方阅读。  滚动优化的由来 滚动优化其实也不仅仅指滚动(scroll 事件),还包括了例如 resize 这类会频繁触发的事件。简单的看看: var i = 0; window.addEv
Sb_Coco
2018/05/28
2.1K0
前端高性能滚动 scroll 及页面渲染优化
最近在研究页面渲染及web动画的性能问题,以及拜读《CSS SECRET》(CSS揭秘)这本大作。
哲洛不闹
2018/09/18
2.7K0
前端高性能滚动 scroll 及页面渲染优化
原生 JS 实现惯性滚动,给鼠标滚轮增加阻尼感,纵享丝滑
当我们在移动终端上滑动页面,手指离开屏幕后,页面的滚动并不会马上停止,而是在一段时间内继续保持惯性滚动,并且滑动阻尼感和持续时间与滑动手势的幅度成正比。
茶无味的一天
2023/09/06
2.1K0
原生 JS 实现惯性滚动,给鼠标滚轮增加阻尼感,纵享丝滑
移动端click事件300ms延迟
一般情况下,如果没有经过特殊处理,移动端浏览器在派发点击事件的时候,通常会出现300ms左右的延迟。也就是说,当我们点击页面的时候移动端浏览器并不是立即作出反应,而是会等上一小会儿才会出现点击的效果。在移动WEB兴起的初期,用户对300ms的延迟感觉不明显。但是,随着用户对交互体验的要求越来越高,现今,移动端300ms的点击延迟逐渐变得明显而无法忍受。
Dawnzhang
2018/12/28
2.9K0
使用 requestAnimationFrame 解决滚动点停误触和 scroll 事件延迟
在手机端网页开发过程中,我们经常会遇到滚动点停误触的问题,最开始想到的解决办法就是判断当前页面(DOM)是否在滚动,如果在滚动,就取消点击或者其他事件。但是在判断页面是否在滚动的时候出现了一些问题,最常见的就 uiwebview scroll 事件延迟,导致我们无法准确判断当前页面(DOM)是否还在滚动。于是想到了使用 requestAnimationFrame 判断某个元素的位置是否发生变化来标识当前页面(DOM)是否在滚动。
李振
2021/11/26
1.1K0
throttle与debounce的区别
注:文章中有例子无法在微信里面展示,最好看原文。 以前写过一篇文章《“节流函数”提高性能》,里面讲到用函数“节流”来减少执行次数(不影响体验的情况下),其实实现的代码并没有问题,但是第二个方法的函数名有问题。前几天看到一篇文章,我的公众号里也分享了《一次发现underscore源码bug的经历以及对学术界拿来主义的思考》具体文章详见。 文中讲了大家对throttle和debounce存在误解,同时提到了《高程3》中实现节流方法存在一些问题,为了更好的理解这两个概念,搜了很多相关文章,详见文章底部。 thro
前端黑板报
2018/01/29
2.1K0
throttle与debounce的区别
移动端吸顶fixbar解决方案
在IOS端,使用 position: sticky 这个属性,使用类似于 position: relative 和 position: absolute 的结合体。在目标区域在屏幕中可见时,它的行为就像position:relative; 而当页面滚动超出目标区域时,它的表现就像position:fixed,它会固定在目标位置。
Tiffany_c4df
2019/09/04
3.1K0
第134天:移动web开发的一些总结(二)
width —— 视口宽高 height —— 视口宽高 device-width —— 设备的宽高 device- height —— 设备的宽高 orientation:检查设备处于横向(landscape)还是竖屏(portrait)
半指温柔乐
2018/09/11
1.9K0
移动端效果之ScrollList
写在前面 列表一直是展示数据的一个重要方式,在手机端的列表展示又和PC端展示不同,毕竟手机端主要靠滑。之前手机端之前一直使用的IScroll,但是IScroll本身其实有很多兼容性BUG,想改动一下需求也很不容易,可以看我之前写的这一文章IScroll那些事——内容不足时下拉刷新(这里并不是说IScroll不好,里面对手机、浏览器兼容性都做了大量的处理,只是当遇到bug时或者想改一下需求时不时特别方便,毕竟是一个这么大的库)。因此也一直想了解一下这类列表的实现原理,万一真到时候可以自己写一个,这样自己维护自
糊糊糊糊糊了
2018/05/09
1.2K0
移动端效果之ScrollList
对用户输入事件的处理去抖动
对输入事件处理函数去抖动,存储事件对象的值,然后在requestAnimationFrame 回调函数中修改样式属性
吴裕超
2018/12/12
1K0
控制页面的滚动:自定义下拉到刷新和溢出效果
通过阅读本文,你可以通过css 中overcroll-behavior属性值的设置,处理浏览器溢出滚动,以及禁用移动设备上刷新,下拉滚动时的发光和橡皮圈回弹效果,当然也可以看到css中 Houndini(胡迪宁),也就是css中也可以写变量等知识,如果文有误导的地方,欢迎路过的老师拍砖指正
itclanCoder
2020/10/28
3.7K0
控制页面的滚动:自定义下拉到刷新和溢出效果
学会一行CSS即可提升页面滚动性能
一般情况下 CSS 并不会影响 JS 事件,但有一个 CSS 属性则比较特殊,那就是 pointer-events,它可以用来规避鼠标事件,其中大部分属性值只适用于 SVG(可缩放矢量图形)操作,而我们只需要关注其 none 属性值,该值表示鼠标事件“穿透”该元素并且指定该元素“下面”的任何东西。
茶无味的一天
2022/10/08
3.3K1
学会一行CSS即可提升页面滚动性能
移动端的touch事件处理
在iPhone 3Gs发布的时候,其自带的移动Safari浏览器就提供了一些与触摸(touch)操作相关的新事件。随后,Android上的浏览器也实现了相同的事件。触摸事件(touch)会在用户手指放在屏幕上面的时候、在屏幕上滑动的时候或者是从屏幕上移开的时候出发。下面具体说明:
周陆军博客
2023/05/07
1.8K0
HTML5移动端开发的常用触摸事件
HTML5中新添加了很多事件,但是由于他们的兼容问题不是很理想,应用实战性不是太强,所以在这里基本省略,咱们只分享应用广泛兼容不错的事件,日后随着兼容情况提升以后再陆续添加分享。今天为大家介绍的事件主要是触摸事件:touchstart、touchmove和touchend。
全栈程序员站长
2022/08/31
1.8K0
网页|利用touch实现下拉刷新
下拉刷新在常见的手机app上大多都有运用。下拉刷新即向下拉重新加载、刷新。下拉刷新在下拉到松手的过程中,经历了三个状态分别是:当前手势滑动位置与初始位置差值大于零时,提示正在进行下拉刷新操作。之后当下拉到一定值时,显示松手释放后的操作提示。最后当下拉到达设定最大值松手时,执行回调,提示正在进行更新操作。如下图则为下拉刷新的实现效果:
算法与编程之美
2020/07/16
1.8K0
网页|利用touch实现下拉刷新
移动端app开发问题及理解
vant官网地址 https://vant-contrib.gitee.io/vant/#/zh-CN/
全栈程序员站长
2022/07/02
3.9K0
移动端app开发问题及理解
移动端下拉刷新和上拉加载实现
最近在做移动端开发,移动端的性能不如 PC 端,屏幕页没有 PC 大,需要我们优化的东西很多;在工作中我所做的移动端小页面,无一例外的都是将网页嵌入到安卓或者 IOS 里面去。
sunseekers
2019/08/09
1.7K0
[OHIF-Viewers]医疗数字阅片-医学影像-Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。_.throttle(func, [wait=0], [option
https://www.lodashjs.com/docs/lodash.throttle
landv
2020/07/14
2.6K0
[OHIF-Viewers]医疗数字阅片-医学影像-Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。_.throttle(func, [wait=0], [option
吸顶效果解决方案
吸顶元素的初始位置一般靠近页面顶部,但与顶部有一定距离,这块区域放的是最醒目的元素,比如Banner图。页面向下滚动超过吸顶元素初始位置时,把吸顶元素固定在顶部
ayqy贾杰
2023/03/15
3.8K0
吸顶效果解决方案
JS中的touch事件与canvas绘图
其中有三个相似的属性touches、targetTouches 及changedTouches,它们有什么不同呢?
码客说
2020/05/09
7.9K0
相关推荐
【前端性能】高性能滚动 scroll 及页面渲染优化
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验