pointer-events:none提高页面滚动时候的绘制性能?

这篇文章发布于 2014年01月4日,星期六,02:23,归类于 css相关。 阅读 97766 次, 今日 46 次

一、故事是这么发生的

很久很久以前……还没有pointer-events, 因此故事不是发生在很久很久以前,而是就发生前前个月。那是个懒散的下午,温暖的阳光透过落地窗悠然地洒在办公桌上,就在这个惬意的时候,我瞅见了这么一条微博:
微博截图

哟,转发蹭蹭蹭上了3位数,不错哦!此tip源自thecssninja.com上60fps scrolling using pointer-events: none一文。

这种结论直接,通俗好记的奇淫技巧似乎颇受大家喜欢。知道即GET! 无需动脑,无需深入思考,自然大家都喜欢。还有国人翻译了这篇文章:[译]使用pointer-events:none实现60fps滚动

故事似乎挺美好,白雪公主和王子相遇,从此幸福地在一起!然后,众粉纷纷效仿采用,也希望得到幸福……

如果故事就此happy ending结束,似乎皆大欢喜。但是,抱歉,只有童话故事才会这样。代码若江湖,血雨与腥风,活脱脱现实世界。因此,白雪公主和王子相遇之后的故事不能不提,比方说王子一直奇怪“为何自己子女都很矮”这样的场景。

我希望那百来个转发的童鞋能看到本文,否则,就不会兴致勃勃以为新技能get,幸福生活coming呢!

二、怀疑与真理的探究

哈佛的校训简单而不简单:真理

说简单是因为就一个单词,两个汉字。比背鲁迅的文章简单value = PM2.5倍。说不简单,是因为要做到很难。

真理这东西是需要不断探究的,是要不断质疑与论证的。

小学应该学过“两个铁球”的故事:超著名哲学家亚里士多德认为,10斤的铁球和1斤的铁球同时往下落,10斤的那个球速度要快10倍。结果伽利略在著名的比萨斜塔一实验,证实两个铁球同时落地,这就是真理的探究。

首先,要敢于怀疑权威;其次要自己大胆试验认证;最后要勇于提出自己的观点。

这有点类似于我们常说的“科研精神”!

但是,人各有志。很多同学就想混口饭吃,很多同学希望的是来钱多,来钱快。“探求真理”这种看不到短期利益的事情着实不是他们喜欢的。

哈佛校训20条——get it!
反复煮沸的水不能喝——get it!
维C不能和虾一起吃,会中毒——get it!
pointer-events:none提高滚动绘制性能——get it!

类似上面这些快餐式的论点,往往这些同学乐于接受,并甚至作为真理。当然,不影响正常的生存。不过似乎就是几十亿人口中那芸芸众生中的一个。在上帝看来,似乎没有什么特别的区分度,可有可无。不过,这些同学本身就是希望平平凡凡、默默无闻过一生,因此,我觉得挺好的。虽然,上面4条基本上都是假的!

thecssninja那篇文章的视频真真切切显示了绘制性能提高了哈,貌似有同行亲自测试,那条微博Gif截图就能说明问题啊!这些都是活脱脱的证据啊,怎么可能“pointer-events:none提高滚动绘制性能”是假的呢!

那我要反问下诸位了:
1. 所有的测试似乎都是Chrome浏览器,FireFox浏览器测试了没,IE呢?移动设备呢?
2. 所有测试都是较新版本的Chrome浏览器,旧版本的也是这样吗?
3. 测试页面box-shadow盒阴影效果为主,如果是其他UI效果,也会是这个结果吗?
4. pointer-events:none样式应用本身就涉及重绘,这个性能影响有没有考虑进去?
5. 该方法经过实际场景的验证了吗,能否应对各种应用场景?
6. 有没有相关其他方法增强性能,例如覆盖一个div层?

如果最后的结论是,只有最新PC版本的Chrome浏览器才有此性能提高,您还会如此兴奋,以为轻轻松松捡了个宝吗?

三、pointer-events:none知识快补

早在2011年的时候,我就着重介绍了pointer-events:none这个声明,可参考之前的文章“CSS3 pointer-events:none应用举例及扩展”。其中不仅有实例应用,以及其他不支持浏览器的兼容方案。推荐看下。

我个人将pointer-events:none理解为“幻影特性”。原话如下:

pointer-events:none的作用是让元素实体“虚化”。例如一个应用pointer-events:none的按钮元素,则我们在页面上看到的这个按钮,只是一个虚幻的影子而已,您可以理解为海市蜃楼,幽灵的躯体。当我们用手触碰它的时候可以轻易地没有任何感觉地从中穿过去。
一切都是幻影!

我从维基上找到的解释是:用户发起的光标移动或对象选择等

如果我们在body标签上应用该CSS声明,则整个页面(如果内部没有其他pointer-events设置)就像是一个背景图片的存在。不能选不能点不能相应hover,幻影而已。

四、评论中的集体智慧

一个微博要想成为经典,必须要有神最右的支持。一个知识体系需要完善,也需要百家之言。

新技能tip的出现,如果只看文章主体介绍,似乎又雾里看花水中望月之感。点评网为什么很多人去看,是因为商家的介绍吗?显然是因为出彩有价值的点评。

thecssninja这篇文章显然也是如此。

如果您单纯看文章的结论,那你可就错过了很多精彩的内容。

我们从上往下(时间顺序)一条一条过一下(版式很考究,如果您发现布局shit一般,请访问原出处):

Ekrem Buyukkaya says:

Brilliant.
Ekrem Buyukkaya 说:

聪明。

//zxx: 酱油评论!

Chris says:

Wonder if there are any side effects…
BTW, the demo works nicely in IE11 too, Shows the same difference in the Performance tab in the new F12 Tools.
Chris 说:

不知道有没有其他副作用…
顺便说一句,在IE11浏览器下,通过F12打开性能测试选项卡进行性能测试,出现了同样的差异。
//zxx: 很赞的同行。第一句提出疑问,怀疑其副作用;第二句自己实践,在IE11浏览器下测试。亲自验证方法的可行性。完全符合我上面提到的对“真理的探究”。对比下自己,如果你遇到这样的状况,会怀疑吗?会自己测试吗?如果没有,可能需要反思下了。

这里评论说IE11下测试结果一致,你是相信呢还是怀疑呢?你是直接接受了这个观点呢?还是会自己在IE11下测试一遍?是真是假,是对是错,咱接着往下看!

Benz says:

Jesuz so simple and so powerful – thx!
Benz 说:

好简单好强大 – 多谢!

//zxx: 典型的拿来主义者。快餐速食者。但至少人家还会评论以表示感谢。

Tyler says:

Just a note, this won’t work with fullscreen applications that rely on content panels using `overflow: auto`, as `pointer-events: none` on the body element won’t register the scroll events. It appears the pointer-events property needs to be applied to a container within the scrollable area.
Tyler 说:

这里要注意下,这个对于内容面板需要使用overflow: auto全屏应用不适用,因为pointer-events: none应用在body元素上的时候不会注册滚动事件。似乎pointer-events属性需要应用在有滚动区域的容器上。

//zxx: 这个算是一个补充tip. 哪个滚动哪个应用pointer-events: none. 是不是应该自己测试下?没错,后面会展示我的测试结果。

Malte Ubl says:

Simple, but doesn’t work as well as you think because this approach makes the document unclickable for up to 500ms after scrolling end. Here is an explanation how to fix ithttps://plus.google.com/+MalteUbl/posts/NsyYKenqYNP
Malte Ubl 说:

很明显,这厮不会如你所想那般工作。因此,它会让页面在滚动结束后500毫秒内没法点击。这显然会有问题,下面这个可以修复这个问题
https://plus.google.com/+MalteUbl/posts/NsyYKenqYNP.

//zxx: 该评论给出的链接似乎要爬梯子的干活。我司天然翻墙,于是截个图给大家看看:

其解决问题的关键技术点与我11年关于pointer-events那篇文章给出的兼容解决方案的技术点一致。

Chip says:

Really nice – solves a specific issue we we’re having with hover events firing off while scrolling! The only issue I’m still seeing is the recalcute style (purple) event jumping to 30 fps each time the class is added to the body. I didn’t see the same thing in your video so I’m wondering if I’m doing something wrong.

https://www.dropbox.com/s/jant444jwgo2jep/recalculate%20jump.png

Chip 说:

真的很赞!解决了我们滚动时候触发hover事件这个问题。现在我仍不解的唯一问题就是,每次往body添加类名的时候,计算样式(粉色那个)事件立马跳到30帧频。但是,我在你的视频上没有看到同样的现象。我在想是不是我哪里做错了。一张需要爬墙同时可能被删除的图片地址。

//zxx: 哈,这一部分对话很精彩的,注意下面作者的回复~

Ryan Seddon says:

@Chip – Yep @derSchepp has said to set document.body.style directly rather than adding/removing a class to avoid a reflow. I’ll update the article real soon.Also I’d say it’s way less noticable in my video as my DOM structure is a lot simpler than your app you’re trying this in.
Ryan Seddon 说:

@Chip – 嗯哪,根据@derSchepp的说法,直接在document.body.style上设置相比添加/删除类名可以避免回流。我待会就更新下文章。
另外,您可能没注意到,我视频中DOM结构要比你尝试的app要简单多。

//zxx: 继续精彩的对话~

Chip says:

I tested Malte’s idea of adding / removing a cover div and that is working the best for me. Changing the document.body.style still causing a 30fps recalculate, but simply adding / removing a cover div with the following css worked perfect and keeps us at 60fps:

.scroll-cover {
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  position: fixed;
  pointer-events: auto !important;
  z-index: 10000;
}
var body = document.body,
    cover = document.createElement('div');
    cover.setAttribute('class','scroll-cover');

window.addEventListener('scroll', function() {
  clearTimeout(timer);
  body.appendChild(cover);
  
  timer = setTimeout(function(){
    body.removeChild(cover);
  },500);
}, false);

I was able to change the setTimeout to 100 to make clicks more responsive and still keep perf

Chip 说:

我试验了Malte关于“添加/移除一个覆盖div”的想法,貌似效果最好。改变document.body.style依旧会导致30fps的重计算,但是很单纯地“添加/移除一个覆盖div”同时使用下面的CSS效果完美,依然保持60fps.

代码略…

我把setTimeout时间改成100毫秒依然效果很赞哦。

//zxx: 是不是又新技能get啦!到底是新技能还是新鸡肋?面对这个疑问我们该怎么办?很简单,按照上面的真理步骤,1. 怀疑; 2.自己试验。这个后面我们一起实践一番。

Matt Stuehler says:

How does this apply to mobile devices (iOS in particular)? Is it at all relevant?

And if it is… what are the implications of using -webkit-overflow-scrolling: touch? That seems to complicate the issue of knowing when the scroll event begins and ends…

Matt Stuehler 说:

这个在移动设备上咋整啊(尤其iOS设备)?这个是一方通行的吗?

如果……使用了-webkit-overflow-scrolling: touch又该如何实现?似乎当滚动事件开始于结束的时候,问题变得更复杂了……

//zxx: iOS等移动设备上副作用开始有人提出……

Peter says:

This looks great! However there’s one thing I don’t understand – you disable pointer events and immediately after show that the hover states still work, before scrolling down the page at 60fps. How did the hover states still work? Were you enabling the JS version not just applying the CSS class? Thanks!
Peter 说:

看上去很不错!但我有一事不明——你禁用了pointer eventshover状态在滚动结束后又立即显示出来了。怎么办到的?你是不是JS里面捣腾了葫芦药而不仅仅是应用CSS类名啊?多谢!

//zxx: 没好好看文章的小白来问问题了。

sam says:

Blown away. This is simply brilliant.
sam 说:

走起。简直超神了。

//zxx: 马屁党。

Aurelien Foutoyet says:

Really clever sir.
Thanks for sharing !
Aurelien Foutoyet 说:

太聪明了。
多谢分享!

//zxx: 马屁党, again!

Ryan Seddon says:

@Peter –

The hover effects are only disabled once you start to scroll so it won’t interfere with hover until then, so that’s why hover still works in the video after I click the disable button.

Ryan Seddon 说:

@Peter –

hover效果只在开始滚动的时候禁用,所以此时没有hover相关交互,直到滚动停止。这就是为什么视频中我点击了禁用按钮,hover效果仍然有效的原因。

//zxx: 小白辅导中……

Ryan Seddon says:

@Matt – Hover on scroll isn’t really relevant to non mouse based devices as you can’t really trigger a hover. You should really avoid the use of :hover on touch based devices all together as it causes issues.
Ryan Seddon 说:

@Matt – 对于无鼠标设备而言,scroll上面的hover实际没有关联的。因为你没法真正触发hover. 如果这导致了某些问题,你应该试着避免在触摸式设备上使用:hover.

//zxx: 有推脱之嫌……

Sam Jarvis says:

Amazing.
Sam Jarvis 说:

震精。

//zxx: 马屁党,again’s again.

Manumanu says:

Great idea. Just wanted to say : IE11 naturally disable pointer events when scrolling. Dunno since which version, ’cause i wasn’t aware of this issue.
Manumanu 说:

想法不错。我只想说:IE11在滚动的时候天然禁用pointer events. 但不知道从哪个版本开始,因此这里的探讨的问题我一点也不在意。

//zxx: 哟,注意啦!问题来了!前面有测试说IE11下结果跟Chrome是一样的,这位兄弟说IE11天然滚动禁用鼠标事件。孰对孰错?还得自己测试~~

Ben Knight says:

Ben Knight 说:

//zxx: 有想法要通过更好的渠道呈现告知。否则,你就不是首创者。这位兄弟twitter发布于17号,见下图:

Mathias Bynens says:

Instead of document.body, consider applying this to the root element (document.documentElement). That way, it will still work if you use <html> and <body> as a free <div> — e.g. if you apply margin: 0 auto; max-width: 30em; box-shadow: …; to <body>.
Mathias Bynens 说:

可不可以应用在根元素上(document.documentElement),而不是document.body. 这样,即使你把<html><body>作为普通的<div>使唤也是可以的。——例如,应用margin: 0 auto; max-width: 30em; box-shadow: …;<body>上。

//zxx: 看上去又是一个不错的建议。是否OK,还需要我们的亲自测试,而不是别人说什么就信什么。

newable says:

Great!
newable 说:

赞!

//zxx: 又见马屁党!

Richard Ayotte says:

It would be nice if this behaviour was the default in browsers.
Richard Ayotte 说:

如果浏览器的默认行为就是这样那就帅气了。

//zxx: 我说兄弟,你何来结论认为浏览器的默认行为不是如此?

Thierry Koblentz says:

Hey Ryan,
In my opinion, this is something browsers should be smart enough to do themselves. So it would make sense to me if one already did; IE11 may be? As @Manumanu suggests.
Thierry Koblentz 说:

那个,Ryan,
在我看来,这种事情应该让浏览器自己解决。如果有那个浏览器已经做了这件事,对我而言是极好的;可能IE11? 正如@Manumanu提到的。

//zxx: 好吧,其实我也是这个观点。后面的一些实验数据和现象会说明,浏览器不是傻子。

JosiahS says:

Interesting idea, but it seems to have the side effect of disabling multi-touch page scrolling on Chrome in Windows 8. (I haven’t tested it anywhere else.)
JosiahS 说:

很有意思的想法,但是貌似在Windows 8的Chrome浏览器中有副作用,页面的触屏滚动被禁用掉了(我还没有测试其他任何地方)。

//zxx: 哦哦哦,出大篓子了,如果这个属实,此方法又中了一刀致命伤。

Ron says:

Any reason why this shouldn’t just be built into Chrome?
Ron 说:

有什么理由这不应该被内置到Chrome浏览器?

//zxx: 我只想说,我们深受喜爱的Chrome浏览器不是傻子。

RS says:

Echoing poster above, yes brilliant indeed. I’ve got to say, one thing I personally find just annoying is hover effects on images–you know like those with an overlay and some icon–something, anything–showing that it’s a video post, or image post or whatever. But alas, every client wants this garbg! And of course when you’re scrolling down the page, and mouse pointer hits the images/elements then it can be a very unsatisfactory experience.

Thanks for the post!

RS 说:

我很赞同上面的文艺,确实很赞。我不得不说,关于hover效果,我一直有件事情耿耿于怀,那就是图片的hover效果,你懂的,经常会出现一个覆盖层,上面有图标以及其他什么东西,告诉你这是个含视频的文章,或者含图片的文章等。但是不幸的是每个客户端都会触发之,试想下,当你向下滚动页面的时候,鼠标触碰了这些图片或元素,会是一个不好的体验。

感谢这篇文章!

//zxx: 怎么感觉是个脑残粉,或者专门调停的老好人。兄弟,你说的那出戏不是我们这场,好吧。你嫌弃鼠标hover晃瞎自己的研究,使用鼠标hover延迟技术啊,与这里提高绘制呈现性能不搭噶的,好吧。哪边凉快哪边待去~求你了,你那凌乱不通的语句让我很辛苦的~

Jake says:

I tried this a while ago on a project and it does trigger a layout when pointer-events: none; is applied to the body (It does in your demo as well). I came up with a different solution but it was more complicated. It basically involves moving a 100x100px fixed position div under the mouse using translate3d() while scrolling, then moving it back out of the window when scrolling is done. That stops the hover effects and doesn’t trigger a layout. This was on a really complicated page, so that forced layout was more of an issue.
Jake 说:

不久前在一个项目中我尝试了这个,当pointer-events: none;应用在body上的时候(正如demo做的那样)触发了布局。我就想了一个不同的解决方法,但是要更复杂些。我创建了一个100x100px fixed定位的元素,通过translate3d()让其在滚动的时候移动在鼠标的小面,当滚动停止的时候再移动到窗体外面。这下子阻止了hover效果同时不触发布局。因为这是一个非常复杂的页面,因此触发布局(layout)是个比较严重的问题。

//zxx: 复杂页面的重布局确实可能会有明显的性能损耗。但是,这位同学的做法,我个人觉得是不是应该治本而不是治标。

Greg says:

Thank you!!!

I used this technique to solve a different problem which should also be worth mentioning.

On my site I am using the Google Maps API and when I scroll down the page (using the scroll-wheel) the page suddenly stops once my mouse hovers over the map. The scroll-wheel would then start to re-scale the map which was annoying, I could disable the scroll wheel in the API but that would disable it permanently.

Using this method the user can now scroll past the map using the scroll wheel AND re-scale the map if they so desire using the scroll wheel. Two worlds in harmony 🙂

Greg 说:

多谢!!!

我使用这个技术解决了另外一个问题,我觉得值得一提。

我的站点上使用了Google地图API当我使用滚轮滚动页面的时候,当我鼠标经过这个地图的时候,滚动就会停止,变成地图大小的缩放了。我可以利用API禁止地图的滚轮缩放,但是这种禁用是永久的。

使用这个方法用户就能够自如地从地图上面滚过去,同时不影响地图的缩放。两个世界都和谐了 🙂

//zxx: 这倒是一个不错的应用场景。只是我疑惑的是,难道这位兄弟不要关心IE9, IE10浏览器吗?实际上,你使用一个小小div覆盖在地图上,也可以到达一样的效果。且兼容性更好。

d43m says:

Looks like it doesn’t change anything with firefox, at least on the demo site, the hover effect doesn’t trigger either way while scrolling.

However, good tip for scrolling on the map Greg !

d43m 说:

似乎firefox下没有任何改变,至少本文的demo页面如此,滚动时候不会触发任何hover效果。

不过,Greg的那个地图处理技巧很赞!

//zxx: 大家可以自己在FireFox浏览器下测试下,滚动的时候是不会有hover效果触发的,停下来才会有,也就是从肉眼所见来看,似乎FireFox天然对滚动与hover呈现做了优化处理。

Schalk Neethling says:

Thanks for this awesome article! Here is how I ended up implementing it and it works great ~ https://gist.github.com/ossreleasefeed/7768761
Schalk Neethling 说:

多谢分享如此精彩的文章。这里是我的实现,效果很赞~ https://gist.github.com/ossreleasefeed/7768761
//zxx: 这就是上面一位兄弟提的pointer-events:none应用在document.documentElement上。
/**
 * Disable and enable event on scroll begin and scroll end.
 * @see http://www.thecssninja.com/javascript/pointer-events-60fps
 */
var root = document.documentElement;
var timer;

window.addEventListener('scroll', function() {
    // 用户滚动停止timeout
    clearTimeout(timer);
    // Pointer events 已被禁用
    if (!root.style.pointerEvents) {
        root.style.pointerEvents = 'none';
    }

    timer = setTimeout(function() {
        root.style.pointerEvents = '';
    }, 500);
}, false);
egiova says:

Excellent little trick to avoid “light pollution”. What about side effects on touch screens?However, it remains to be tested at large scale. But good clue to follow…
egiova 说:

避免“光污染”不错的小伎俩。触摸屏幕的副作用怎么办呢?显然,这还需要大规模的测试。但是值得跟进……

//zxx: 有疑问,没有实践~

Greg Whitworth says:

@Manumanu @RyanSeddon He is somewhat correct in regards to how IE11 handles :hover on scroll. To keep the explanation simple (we may post a blog post for more detail on the matter), we turn off hit testing if the mouse isn’t moving so you won’t get any un-necessary paints, however, there are some caveats to this for compatibility reasons. Even when using your suggested CSS+JS trick you won’t get too much perf increase in IE11 due to the fact that we use independent rendering (we’re rendering on a separate thread from the UI thread) but it would free up some CPU cycles for any script you might be running. Hope that helps!
Greg Whitworth 说:

@Manumanu @RyanSeddon 他对IE11的:hover在滚动上的认识或多或少是正确的。为了解释简单(关于此话题的细节我们可以发表一篇博文),我们可以简单理解为,如果鼠标不移动,我们关闭hit testing, 这样就不会有无所谓的绘制,但考虑到兼容原因,还需要一些注意事项。即使使用你提出的CSS+JS技巧,在IE11中也不会得到很完美的性能提升。因为我们使用了独立渲染(使用分离的UI线程进度渲染),它会为你运行的任何脚本进行周期性的CPU释放。希望这个有所帮助!
//zxx: “hit testing”在维基上的解释是:在计算机图形编程中,hit-testing是一个确定用户的光标(如鼠标光标,接触点或触摸屏界面)是否与屏幕上绘制的给定图形对象(如形状,线和曲线)相交接的过程。Hit-testing可以在鼠标或其他指针设备移动或启动执行。

在Web编程语言(如CSS, HTML, SVG)中,这是与pointer-events(如用户发起的光标移动或对象选择)的概念相关联的。

听口气,这位兄弟是在IE干活的吗?似乎总是把IE11称为我们~~不解~~

Yuanyan says:

If u do it, it will make scrolling not work on mobile device.Test on mobile that “pointer-events: none” will disable the touch events. It’s seems not a good hack for mobile web.
Yuanyan 说:

如果你砍了这一刀,移动设备的滚动直接毙命。经测试,pointer-events: none会干掉touch事件。貌似此技巧对移动web不适用啊!

//zxx: 貌似是国人啊,还貌似是我厂的同行。微博是@元彦,大家加粉的时候记得补上一刀,说是我推荐的,(*^__^*)

Alex says:

Firefox also turns off hit testing while scrolling, and from a quick test of the test page this trick would probably slow it down (Changing the pointer-events causes it to repaint whatever the mouse was hovering over when the scroll started, which it doesn’t do otherwise)
Alex 说:

Firefox滚动时候也会关闭hit testing, 面对本文的测试页面的快速测试,这种处理可能会降低页面速度(但是,如果浏览器不这么做,那在滚动开始的时候改变pointer-events会导致无论鼠标移到哪里都会重绘)

//zxx: 尼玛,我尽力了。我实在不知道这位兄弟要讲的是什么意思。不是他说的不懂,而是我被头像吓住了。欢迎大家帮忙纠正!

五、实践出真知

上面的评论为我们打开看很多天窗。

我们看到了很多需要我们自己动手去试验的东西,比如IE11的绘制呈现帧测试,div层覆盖测试,FireFox天然滚动体验,Chrome浏览器下亲自测试等。

为了得到更广泛更具有价值的测试结果,我们使用自己设计的测试页面,含半透明,投影以及纹理背景。

您可以狠狠地点击这里:滚动与hover绘制时间测试demo

进度demo,会看到一排排张含韵的上面有如下图所示的几个按钮:
测试页面的几个按钮

点击第一个按钮会给body添加pointer-events:none;点击第二个按钮会有一个层覆盖在页面上。通过ESC键可以取消之前任意两个禁用hit testing操作。

测试的方法是,鼠标位置不变(避免不必要的重绘),上下快速滚动鼠标。由于鼠标要去点击停止记录按钮,也会有重绘出现。因此,末尾的数据不可信,主要看截图的中间主体部分。关注fps等帧频信息。

分别测试了如下三种状态:1. 默认滚动; 2. pointer-events:none下的滚动; 3. div fixed定位层覆盖后的滚动

以下数个测试截图都是按照这三个状态下拉的。

1. 下面三张图是自己在最新版IE11浏览器下的连续三个测试截图:

默认滚动:

默认态下的滚动呈现

pointer-events:none绘制:

pointer-events:none状态下的滚动

div元素覆盖绘制:

div层覆盖下的滚动呈现

2. 这是我本机Chrome26测试的结果

默认滚动:

默认滚动hover的绘制

pointer-events:none绘制:

pointer-events:none的渲染

div元素覆盖绘制:

创建覆盖div元素的绘制

3. 下面三张图是公司机Chrome31下测试结果
默认滚动:

pointer-events:none态的鼠标hover+滚动效果

pointer-events:none绘制:

默认态的hover+滚动效果

div元素覆盖绘制:

覆盖层下的滚动呈现速率效果

都是纯手工的测试,因此,测试结果截图长得不是很整理,但是,足够说明一些问题了,我觉得是很有意思的一些问题,尤其结合众老外的一些精彩评论。

  1. 首先,对比IE11, Chrome26以及Chrome31的测试结果,可以发现,div层覆盖的方法绝对是呈现性能最差的,基本上要奔30帧/秒的频率去了。但是,第8条评论中Chip对div覆盖方法赞不绝口,说是最佳实践,60fps.

    怎么回事?细细阅读会发现,其关注的是“重计算”,也就是recalculate. 见下图高亮:
    重计算高亮显示

    而不是我们这里着重探讨的呈现 – paint.

    至于div层覆盖法对fps的影响,根据我在Chrome26下不太专业的测试,似乎是相反的结论。精力原因,我就不深入测试,您有兴趣可以稍稍研究下,发个博文或者微博什么的。

  2. IE11自身纵向对比。肉眼观察来看,pointer-events:none似乎要比默认态滚动的呈现要快那么一点点,但是,真的并不明显。这似乎正如编号为30的评论所说,本文的CSS+JS技巧不会在IE11下得到完美的性能提升。因为IE11本身的渲染以及CPU回收等都有自己的 优化机制。
  3. 再从Chrome26浏览器的纵向对比来看,似乎pointer-events:none有还是没有没什么区别啊。这个是不是出乎意料了!我拿老外的那个测试页面测试,发现,确实没有什么差异!
  4. 我们再拿Chrome26横向对比Chrome31浏览器,会发现Chrome31浏览器下,浏览器的绘制呈现时间基本上就没有了!而div覆盖时间类似。说明差异不是系统造成的,只可能是,Chrome随着版本的升级,在渲染呈现性能上也有了提升。
  5. Chrome31默认状态和pointer-events:none状态对比,您会发现,似乎差别不大,默认态除了几个冒尖的柱状图,其他基本上和pointer-events:none类似。咋回事?

    Chrome31的这三张图实际上是最先测试的,并未注意到鼠标不能移动这一细节。我没估计错的话,冒尖的几个小绿柱子应该是鼠标移动造成的。如果我的这个推论正确的话,那岂不是如果鼠标不移动,单纯滚动的话,pointer-events:none加与不加是没有任何区别的?!

    没错!我就是要告诉你们,pointer-events:none提高页面滚动时候的绘制性能是不准确的

    您可能会疑惑,哎呀,原作者视频都出来了,确实绘制柱状图差异明显啊!我自己去demo页面测试,也是如此啊!兄弟,实际上,原作者变了个魔术而已,一个近景魔术!

    你有没有想过这么一个问题:为何作者的定时器间隔要设置成500毫秒这么久?当滚动结束,我们想点击一个东西,只要速度一快,岂不是根本就没效果!如此大的可用性风险,为何不设置个100毫秒呢,正如评论(编号8)中某人说的一样?

    答案很简单。为了测试结果好看,数据对比明显,干扰条件减少!

    试想下,定时器间隔如果是100毫秒,如果滚动稍微慢一点,岂不是body就会发生状态变化,类名移除,页面重计算,测试数据变成默认态数据,数据污染,柱状图从平原变山峰。如果一不小心又滚快了,body再变化,重计算与重绘就会交替出现。重计算干扰+状态不稳定,必然导致测试数据不好看。因此,小间隔时间定时器不合适。

    但这个原因其实不是主要原因。

    如果间隔时间是100毫秒,我们要想一直保持pointer-events:none状态,势必滚动的时候要速度比较快。问题来了,这滚动速度一块,你就会发现,默认状态的重绘时间也是60fps的,柱状条的高度与pointer-events:none类似。这不傻逼了吗?这魔术不久穿帮了吗?

    因此,保险起见,定时器间隔还是500毫秒的好!

    我在对一干评论的麻辣点评中多次提到:Chrome浏览器不是傻子,不会考虑hover重绘与滚动的性能优化?

    类似的优化在IE11以及FireFox浏览器中均有。其中,FireFox浏览器尤其明显。你现在用FireFox打开demo页面,鼠标位置放在任意图片上,不移动,单纯滚动鼠标轮子。当你鼠标停止,你会明显看到一定延迟时间后,hover效果才呈现。这不就是pointer-events:none要实现的状态吗?

    Chrome浏览器也是有的,但是,Chrome浏览器由于自身渲染性能出众,因此,这种延迟时间比较短。

    因此,如果你的页面滚动比较迅速,你是完全不要担心hover渲染问题的。如果你滚动比较慢,浏览器认为你希望查看hover态,这不挺好的。

    编号24评论吐槽鼠标经过hover效果闪啊闪的,体验不好。诸位,这完全是另外一码事了,就算你应用了这里的pointer-events:none方法,解决了滚动,那你鼠标经过该怎么处理呢,还不是闪啊闪?莫非再给document绑定mousemove事件,要是这样,你让a链接情何以堪。这种状况还是乖乖使用鼠标hover延时执行技术吧,与这里不是一码事。

简单总结下就是:div覆盖法重绘损耗明显,直接枪毙;快速滚动下,pointer-events:none并无或极少绘制性能提升;慢速滚动,提高重绘有没有意义。

六、我的综合结论

众家精彩评论中多次提到pointer-events:none影响触屏设备的滚动,如手机,windows 8等。这个严重的性能缺陷显然制约了此方法的普及。

其次,原文也提到另外一个问题:子元素设置了pointer-eventes: auto会导致滚动的时候页面闪动。需要下面的代码修正:

.disable-hover,
.disable-hover * {
    pointer-events: none !important;
}

好麻烦,低效选择器,这下页面的估计重计算要到30fps了。

最后,也是最最重要的。几乎所有现代浏览器天然具有“快速滚动hover绘制的性能保护”。何必多此一举吃力不讨好使用问题多多的pointer-events:none?

因此,如果您单纯想要页面的绘制速度,没有任何理由使用pointer-events:none技术。如果您是希望禁用某些操作,例如26号评论中地图缩放的处理等,这个倒是作为参考技术。

pointer-events:none技术只有页面在慢滚动的时候,才会有重绘性能提高优势。不过话说话了,你页面如果是慢滚动,还需要碍事的pointer-events:none吗?

再退一万步讲,当下Chrome浏览器SVG动画满天飞也照样跑得屁颠屁颠的,hover所造成的重绘对用户而言根本不足以造成丝毫的影响。

因此,所谓的提高hover绘制时间的技术,你就当路边走过的一个美女吧,看一下,心扑通一下,就让她过去吧,她不适合你的!

七、故事需要一个结尾

我老婆曾多次替我担忧:“你把技术都分享出去,别人都学会了你的技术,那你岂不是更有压力啦?”

我表示我一点都不担心。

之前知乎上看到一个赞非常多的问题:“2013 年高考状元业余爱好广泛也看电视打网游,是我们素质教育开花结果了吗?”

3000多赞的回答

我不担心的原因类似:“专注度差太多了!

中国的环境好啊,大家浮躁啊,喜欢吃现成饭啊。类似文章读完就觉得,哦,灭了个boss,升级了。真的升级了吗?突然想起了句中国古话:是知其然不知其所以然。大家忙着赚钱啊,理财啊,娱乐啊各种事情,能抽时间出来学习已经不错了,你还让我要潜心修学,深入思考。啊,我自己想想也确实不容易的。

如何能够到达超出常人的高度,有个便捷的方法,那就是站在巨人的肩上。但是,如何站在巨人肩上是个头疼的问题,对于我们最技术的人而言,在我看来,基本上就只有一步一步爬上去这条路,扎扎实实走自己的路。

但是,很多人以为跟巨人照个面,打个招呼,握个手就get的巨人的身高。实际上,啥都没有。

面对一个技术点,如果你没有追求“真理”的精神,单纯是欣然接受,没有自己的疑问,没有自己的佐证,没有自己的思考。最多只是捡了漏,技能最多照搬80%过去。因此,完全没有必要担心这类人会超越自己。但是,如果你敢于质疑与思考,有自己的试验、实践、思考与总结,发现未被关注的细节,完成更进一步的创新,恭喜你,你真真切切站在的巨人的肩上。

如果人人都是如此,那我确实要担心不断被别人超越的事情。好在中国大环境普遍浮躁,势利,好大喜功,急功近利。因此,我依然高枕无忧地写着自己的小文章,分享着自己的所思所想。

读到这里的你是希望站在巨人的肩上呢?还是继续捡捡巨人扔下的鸡骨头呢?

如果你要做前者,你应该知道要怎么做的?很简单,“真理”三部曲:1. 质疑2. 试验实践3. 提出自己认为正确的观点

欢迎质疑我在本文提出的技术观点,并提出你认为的真理!

(本篇完)

分享到:

赞助商推荐(我也要赞助)

想学到点真东西? ×
如果你有1~3年前端开发经验,不妨 ×
想高薪入职阿里? ×
想通过真实互联网项目成长自己? ×


发表评论(目前18 条评论)

  1. 记住了作者专门找来的说道:

    博主不写小说真是可惜了O(∩_∩)O精彩!

  2. ayqy说道:

    专注度是个问题,末尾几句话受教了,谢谢

  3. 迷途小前端说道:

    智商差太多了啊

  4. weixiao909说道:

    支持!

  5. Gazzooid说道:

    看到這個說是”幻影”什麼的, 我反而引申了有關 WCAG 的想法….
    我沒有太多時間去做測試~ 不過谷歌了一下卻貌似沒有相關文章~
    您有相關的經驗可以分享嗎~!?

  6. penJunTan说道:

    你在pocket 躺了很久..
    浏览器足够聪明的话 pointer-events:none 也应该做到不影响触屏设备的滚动 呵呵

    非常赞同你的怀疑与学问!! 共勉

  7. 谢亮说道:

    谢谢旭哥,每次读你的文章都醍醐灌顶。

  8. mao说道:

    博主测试的时候,是否有触发hover?
    我在chrome33下,用博主的demo,不专业测试,纯滚动,发现触发hover的时候,
    默认情况下,hover时,是会低于60FPS的,而pointer-events:none则表现很好。
    而没有触发hover的时候,
    测试结果是跟博主一致的。
    而设置这个的关键字,不是避免hover带来的重绘吗,如果不触发hover,测试结果有意义吗?
    PS:当然,我可能姿势不对,没认真看完,眼瞎看错什么地方了……不要诅咒我……

  9. G-lin说道:

    我看了一些你的文章,博主的实践出真知,关注细节是小学老师教的吧,呵呵。本文文章的结尾道出了很多人的心声,受教了,入门级的前端,路还很远,一路你们这些前辈的陪伴,让我们在迷失的时候有了方向感,感谢!!

  10. bingqian说道:

    我不是专注做前端,不过感觉你里面的观点挺新颖的!希望我也继续努力。谢谢您。

  11. paopao说道:

    除了耍嘴皮子,技术一般般啊

  12. ethanchen说道:

    rss 翻译乱了

  13. canon说道:

    谢谢旭哥,每次读你的文章都醍醐灌顶。

  14. muzinian说道:

    “五 实践出真知 ”的demo连接是404

  15. ADD~``说道:

    内个.. 文章我翻的…….
    但是, 受教了… 这种直接技能GET的事儿- -.. 果然不合适…. 特别是对程序猿们…
    以后一定注意….. 多点质疑精神.