视差滚动(Parallax Scrolling)已经是老生常谈了,自最早在Nike Better World中出现后,就风靡一时。老调重弹旨在自我巩固,总结归纳。

What is Parallax Scrolling

先来看看百科词条解释:

视差滚动(Parallax Scrolling)是指让通过鼠标实现网页多层背景以不同的速度移动,从而形成立体的运动效果,带来非常出色的视觉和交互体验。这种网站通常通过鼠标滚轮滚动來实现,用不同于传统的方式來营造不一样的使用者感受。

这段文字非常拗口,我个人理解就是通过鼠标滚轮滚动控制视图的不同水平层以不同速度滾动,实现立体视觉效果。

原諒我從 ISUX 盜圖一張OvO

如图,我们可以明显地区分出这张gif图具有3个水平层,它们沿x轴负方向的移动速度不相同,从而給人一种立体的情境化的直观感受,这就是视差滚动需要达到的效果。

再来看一个非常棒的视差滚动网页示例:

parallax-scrolling-analysis-1.png

Three Steps of Parallax Scrolling

Pure CSS

Pure CSS方法实现视差滚动的核心來自于:background-attachment: fixed || scroll || local

默认情况下取值为scroll,即内容跟着背景走,而视差滚动页面取值fixed,则背景相对页面固定而不跟内容滚动。

同时,我们还可以给页面添加一些动效。

下面来看一个例子:
 Create a Masked Background Effect With CSS — — by Kezz Bracey

Scroll Event

在原理展示图中,我们曾讨论过整张图片的3个水平层,而实现它的方法就是将同一水平层的DOM元素装载在同一个Div元素中。在页面滚动过程中,根据获取的页面的ScrollTop值,以不同的参数值设置各层所在Div的top值,达到滚动速度不同的效果。

Debouncing Scroll Event

Defects of Scroll Event

如果你使用的是Chrome或者IE10+的现代浏览器,你会发现Scroll Event方法的一个致命缺陷:

使用鼠标滚轮滚动页面触发Scroll事件时,视差滚动元素会随着滚轮滚动而抖动。

你可以脑补一下這鬼畜的画面,你就知道问题多严重了。

Reflows and Repaints

要深入理解Scroll Event的跳动bug,就必須提到 重排重绘 两个概念。

重排:当你在JavaScript中使用ScrollTop获取元素属性时,即为浏览器布置了大量的任务,浏览器必须立即对页面进行布局來完成任务的过程
 重绘:当你基于ScrollTop值改变元素其他屬性,则需要对元素进行的动作;同时,重绘还会导致Scorll值暂时失效,因为此时页面发生了变化。

那么,我们思考一下同时对多个元素进行上述操作时的情境,如果我们对每个元素进行位置计算并重绘,则会使浏览器进入一种Reflow-repaint Cycle中。而具有大量破坏页面区域、重排和重绘操作的视差滚动就是这一种典型情況。由重排和重绘导致的滚动事件和浏览器视觉更新的不合拍,毀掉了整個视差滚动的体验。

引起重绘和重排的其他操作:

Element
 clientHeight, clientLeft, clientTop, clientWidth, focus(), getBoundingClientRect(), getClientRects(), innerText, offsetHeight, offsetLeft, offsetParent, offsetTop, offsetWidth, outerText, scrollByLines(), scrollByPages(), scrollHeight, scrollIntoView(), scrollIntoViewIfNeeded(), scrollLeft, scrollTop, scrollWidth
 Frame, Image
 height, width
 Range
 getBoundingClientRect(), getClientRects()
 SVGLocatable
 computeCTM(), getBBox()
 SVGTextContent
 getCharNumAtPosition(), getComputedTextLength(), getEndPositionOfChar(), getExtentOfChar(), getNumberOfChars(), getRotationOfChar(), getStartPositionOfChar(), getSubStringLength(), selectSubString()
 SVGUse
 instanceRoot
 window
 getComputedStyle(), scrollBy(), scrollTo(), scrollX, scrollY, webkitConvertPointFromNodeToPage(), webkitConvertPointFromPageToNode()

Mousewheel or sth.

了解了抖动bug的原因,我们需要对其进行针对性优化,消除元素的抖动。

为此,我想了几种方案:

  1. 使用Mousewheel事件來替代Scroll事件
  2. 当滚动事件触发时就把上一次滚动的值保存在变量中,然后使用其值执行视觉更新

Some Reflection

视差滚动如今已经不再炙手可热,崛起与没落只在一夜之间,是什么造成了她的昙花一现?

对搜索引擎的不友好(SEO)
 1.URL中没有关键词
 2.缺少內部链接
 3.页面加载速度慢

难以跟踪访问用户的行为
 对于一个单页应用,你很难对访问用户的行为进行跟踪,使得网站维护者很难通过改善页面或內容來提升网站流量和转化率

读取时间的延长
 在前面給出的网页示例中,网络环境不佳的用户可能在页面加载的进度上耗费了更多的时间,即使网页的效果让人眼前一亮,也无法弥补用户糟糕的体验

缺乏良好的导航设计
 对于部分有明确目的性的用户来说,冗长的视差滚动效果是一种噩梦,这意味着他们无法直截了当地到达他们想要到达的位置

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.