Obeta

你所不知道的滚动锚定

滚动锚定(Scroll Anchoring)是去年2018年11月份的一个CSS草案,目的是为了解决一个困扰用户在阅读屏幕内容时发生的一些定位上的问题.

这个新的 CSS 提案css-scroll-anchoring首先是被 Chrome 首先支持(最近 Chrome 的更新速度越来越快了,增加了很多开发调试上的功能,赞 👍),而后 Firefox 也在最近的 66 版本中也支持了.

不得不说浏览器产商之间的竞争越激烈,对于用户(开发也是用户,也是人)来说这就越好,说明越来越多的新特性被支持,开发和使用上会更舒适,bug 会被 fix 的更快.

因此建议各位为了防止一家独大,最好同时使用多种浏览器,都下载体验一下尝尝鲜,不同的浏览器提供的开发体验是并不一样的,同时也会提升自己对多种浏览器的认识.大多数时候 Chrome 的开发体验会更好,但是有时候 Firefox 开发会更舒服,比如我上一篇文章一些不常用的 css3 与 css4 属性总结中提到的clip-path,如果你使用此属性,那么明显提供了实时 clip-path 绘制工具的 Firefox 会让你更惊喜.

废话就不多说了,来看看这个滚动锚定是个什么神奇的东西吧.

为什么

首先做一个实现,假设你现在手上有一台手机,里面装有微信,那么你可以来做个小实验:

  1. 打开手机的自动旋转功能.
  2. 打开微信的横屏模式设置->通用->开启横屏模式
  3. 随便找一篇公众号文章滚动到一个位置(记得这个位置的文字),然后旋转手机至横屏

这时候你会发现你刚刚滚动到的位置不知道哪里去了,如果是你正在阅读并不小心旋转了手机或者当前也么内容正在加载,那么你可能会一下找不到自己的阅读位置了,这是非常糟心的体验.

这是由于之前的浏览器记录的位置是视窗顶部相对于页面顶部的距离来设置位置,因此一般情况下你打开上次阅读的公众号你是可以回到原来的位置,但是如果你旋转了屏幕或者页面内容有更改,那么这个高度是错误的.

相对应的你可以打开一个长一点的网站测试,比如就这个页面吧,打开你的控制台改变视窗的高度.是不是发现如何更改你都是在之前显示的 dom 位置.

原理

Scroll anchoring works by selecting a DOM node (the anchor node) whose movement is used to determine adjustments to the scroll position.

上面截取的 w3c 的描述,大概的意思是滚动锚定的工作原理是首先选择 DOM 元素作为锚节点,然后尝试将该节点保持在屏幕上相同的相对位置.

相对应的节点选择是根据锚节点算法决定(目前可能各个浏览器的算法实现不一致).一般情况下都是在当前滚动区域最顶部进行选择锚节点,对锚节点的选择要求是:

  • 锚节点不能有display: none;属性值.
  • 锚节点不能有display: fixed;属性值.
  • 锚节点不能同时有display: absolute;属性值且与当前滚动框同级.
  • 锚节点不能有overflow-anchor: none;的属性.

通俗的来说就是选择当前滚动元素里可见的尽量小的靠近顶部的子元素为锚点.

同时此算法会优先选择深层次的节点作为锚节点,因为只有这样才能让视窗的改变对浏览器调整的幅度降低到非常小甚至不需要调整.

概念上来讲每次滚动都是需要计算当前的锚节点,但是这对于性能来说是很难忍受的,因此目前采用的优化是当视差改变的时候再去计算(还好来得及).视窗改变前的位置设y0,改变后设y1,那么偏移量为y1 - y0.浏览器就可以根据此偏移量来进行调整.

兼容性

可以查看caniuse.可以看出也就 Chrome,Firefox 目前支持,默认是开启的.

新特性可以理解,毕竟没法那么快用上,需要漫长的审核测试走流程,这个没法控制,其实我们更应该担心的是这个新特性是否会破坏目前网上用户对于网站的一些预期行为(用户习惯了 bug,没错就是有这种操作),又或者网站开发者自己通过 js 的方式 fix 了这个行为,然后部分更新浏览器的用户发现网站出现了异常的行为,那么怎么办呢?

因此,一个新的 css 属性被增加了进来,它就是overflow-ancher

overflow-anchor: auto; // default

overflow-anchor: none;

一旦我们对某个元素设置了none,由上面的锚点选择算法可知那么这个元素以及其子元素都无权参加锚点竞选,而对于子元素也是滚动容器的来说会自动重新设置为auto.需要注意的是父级开始了none,那么此父级的非滚动容器子级都无法再次开启锚点竞选,毕竟你爸不同意你参加补习班,你还能说啥,但是你可以让你自己儿子去上补习班...

hack

关于overflow-ancher可以实现一个比较有意思的东西,就是在滚动容器中增加内容但是能保持视窗一直在最底部.

just show the code:

<div id="scroller">
	<!-- 内容新增点 -->
	<div id="anchor"></div>
</div>

然后样式:

#scroller * {
	/* 不允许子元素被选为锚节点 */
	overflow-anchor: none;
}

#anchor {
	/* 把最后一个元素设置为锚节点 */
	overflow-anchor: auto;

	/* 锚节点需要一点高度(不显示的无法作为锚节点) */
	height: 1px;
}

上面 CSS 强制#anchor一直是锚节点.

实现这个功能并不需要任何 JavaScript,说不定以后会非常有用.

个人随笔记录,内容不保证完全正确,若需要转载,请注明作者和出处.