忍法,scroll 翻滚之术!

前言

在日常的开发中,我们对 scroll 这个单词肯定不陌生。

例如因为看不惯浏览器默认样式而用 JS 一顿猛如虎操作的 自定义滚动条

或者是嗖~一下就到顶的 回到顶部

又或者是想去哪点哪的 标题导航

image

但是在过去的开发中,要实现这些功能并不是那么轻松的一件事情。

例如我们要实现一个有滚动效果的 回到顶部 功能,我们可能需要写下这些代码。

let timer = null;
let backTop = document.querySelector("#backTop");
backTop.addEventListener("click", () => {
  cancelAnimationFrame(timer);
  let startTime = +new Date();
  let scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
  let totalTime = 300;
  timer = requestAnimationFrame(() => {
    let lastTime = totalTime - Math.max(0, startTime - +new Date() + totalTime);
    document.documentElement.scrollTop = document.body.scrollTop =
      (lastTime * -scrollTop) / totalTime + scrollTop;
    timer = requestAnimationFrame(func);
    if (lastTime === totalTime) {
      cancelAnimationFrame(timer);
    }
  });
});

(免责声明:伪代码未经测试,如有 BUG,跪求原谅。)

嘤,意思就是要写个动画,不断修改当前页面的垂直滚动距离,直到为 0。

(吃瓜群众:“很难嘛?是你太菜了吧,大叔!”)

image

但其实随着时间的推移, web api 以及 css 规范的不断改进,那些我们曾经认为实现起来很麻烦的功能也变得简单了起来。下面我们可以一起来探讨一下这些改进的内容。

Web API 中的 scroll 家族

我们来康康 scroll 家族 里有趣的 API。

scroll 与 scrollTo

scroll()scrollTo 方法是用于在给定的元素中滚动到某个特定坐标的 Element 接口。

语法如下:

  1. scroll/scrollTo(x, y)

    • x:元素要移动的位置横坐标。
    • y:元素要移动的位置纵坐标。
  2. scroll/scrollTo(options)

    options支持属性有lefttop以及behavior

    • top:元素要移动的位置横坐标。
    • left:元素要移动的位置纵坐标。
    • behavior:元素的运动模式,如果是auto,则没有动画效果,如果是smooth,则是平滑滚动。

我们来康康栗子:

image

上面例子来自 MDN 的 GitHub 仓库dom-examples

核心 JS 代码如下:

let scrollOptions;

const form = document.querySelector("form");
const leftInput = document.getElementById("left");
const topInput = document.getElementById("top");
const scrollInput = document.getElementById("scroll");

form.addEventListener("submit", e => {
  e.preventDefault();
  scrollOptions = {
    left: leftInput.value,
    top: topInput.value,
    behavior: scrollInput.checked ? "smooth" : "auto"
  };

  window.scrollTo(scrollOptions);
});

scrollBy

scrollBy() 方法是使得元素滚动一段特定距离的 Element 接口。

语法如下:

  1. scrollBy(x, y)

    • x:元素要移动的位置横坐标。
    • y:元素要移动的位置纵坐标。
  2. scrollBy(options)

    options支持属性有lefttop以及behavior

    • top:元素要移动的位置横坐标。
    • lef::元素要移动的位置纵坐标。
    • behavior:元素的运动模式,如果是auto,则没有动画效果,如果是smooth,则是平滑滚动。

再举个栗子:

image

核心代码如下:

let scrollOptions;

const form = document.querySelector("form");
const leftInput = document.getElementById("left");
const topInput = document.getElementById("top");
const scrollInput = document.getElementById("scroll");

form.addEventListener("submit", e => {
  e.preventDefault();
  scrollOptions = {
    left: leftInput.value,
    top: topInput.value,
    behavior: scrollInput.checked ? "smooth" : "auto"
  };

  window.scrollBy(scrollOptions);
});

Mmmmm,没错,就是只是把上面的 DEMO 中的scrollTo改为scrollBy而已。非常的机智~

image

我们再来看看上述三个API的兼容性:

image

Mmmm,兼容性挺好的。

(吃瓜群众:IE没有人权?????????)

Element.scrollIntoView

Element.scrollIntoView() 方法可以让当前的元素滚动到浏览器窗口的可视区域内。

语法如下:

  1. scrollIntoView(alignToTop)

    alignToTop是一个布尔值,如果不填则默认为true。相当于{block: 'start', inline: ‘nearest‘}

    如果值为true,则元素的顶端将和其所在滚动区的可视区域的顶端对齐。如果为false,则是底端对齐。相当于{block: 'end', inline: 'nearest'}

  2. scrollIntoView(scrollIntoViewOptions)

    scrollIntoViewOptions包含下列属性的对象:

    • behavior:元素的运动模式,如果是auto,则没有动画效果,如果是smooth,则是平滑滚动。
    • block:定义垂直方向的对齐方式,值可以是 startcenterendnearest之一。默认为 nearest
    • inline:定义水平方向的对齐方式,值可以是 startcenterendnearest之一。默认为 nearest

来来来,我给大家解释一下blockinline的可选值到底是怎么回事:

  • start:跟当前元素它爹的头发(顶部)对齐。
  • center:跟当前元素它爹的肚子(中间)对齐。
  • end:跟当前元素它爹的 jio(底部)对齐。
  • nearest:就近原则,挨哪里近去哪,如果在中间就不动。

如果是block,就当元素是站着的(从上往下),如果是inline,就当元素是躺着的(从左到右)。当然,前提是默认的writing-mode: horizontal-tb

有点绕?快来康康栗子呀:https://codepen.io/krischan77/pen/mdJMQpz

image

好了,上述就是杀马特,哦不是,scroll家族的特性了。(废弃或准备废弃的就没往上写了。)

最后我们同样来看看兼容性:

image

嗯,还行,IE都能用了。。。

至此,我们Web AP里的杀马特家族,哦不是,scroll家族已介绍完毕,像Element.scrollIntoViewIfNeed这种不染发(不是标准)的API,就没有介绍了,有兴趣的可以自己去看看它到底能干啥。

image

CSS scroll

分享完 JS 中的 scroll ,我们再来了解下 CSS 中的 scroll

scroll-behavior

我们上面在讲这个 JS 中的 scroll 时,多次提到一个单词叫“behavior”。

Mmmm,所以你们猜猜这个scroll-behavior跟 JS 里的behavior有木有关系?

嗯,没错,你们猜对啦,是有关系的。

(吃瓜群众:“都没人理你~”)

scroll-behavior跟上述各个scrollAPI 里的behavior一样,是用来定义页面进行滚动操作时的动画效果。

如果定义为smooth,则页面触发滚动操作时,就会有滚动的效果,如果为auto,则跟原来一样,是瞬间移动到指定位置。这指的是类似于点击#hash跳转一样的触发,而不是滑动滚动条。

其效果可以参照本文第一小节的 DEMO。

兼容性就如图:

image

Scroll Snap

CSS Scroll Snap 是 CSS 中一个比较新的独立模块,它的第一个正式版本CSS Scroll Snap 模块 Level 1也是在 2019 年 3 月 19 日才发布。

CSS Scroll Snap 模块 可以让页面容器停止滚动时,捕捉并让其自动滑动到指定元素的指定位置。

一给我哩 giaogiao!这可是非常了不起的特性啊~

image

它分了两部分,一部分作用于滚动容器上,一部分作用于相对的滚动子元素上,具体关系如下表:

作用于滚动容器 作用于滚动子元素
scroll-snap-type scroll-snap-align
scroll-padding scroll-snap-stop
scroll-margin

scroll-snap-type

scroll-snap-type属性指定能不能去捕捉当前滚动的容器并让它对齐,以及所执行的方向跟严格程度。

它可选的方向值有:

  • x :捕捉 X 轴上的位置
  • y :捕捉 Y 轴上的位置
  • block :捕捉块轴上的位置(逻辑意义上与 y 一样)
  • inline :捕捉内联轴上的位置(逻辑意义上与 x 一样)
  • both :捕捉两个方向上的位置

它可选的严格值有:

  • none :默认值,Mmmm,啥也不干
  • proximity :一个感性的值,如果元素进入到了容器的捕捉位置范围内,则进行捕捉并滚动,否则就不管,至于这个范围是多少,约莫着 45%的位置吧(手动测的,W3C 没给出具体算法,瞎猜吧,哈哈哈)。
  • mandatory :一个靠谱点的值,只要有参数,停止滚动时就肯定能对齐。

我们来康康这玩意到底是啥效果:

image

以上 DEMO 来自于 MDN 的scroll-snap-type

scroll-snap-align

scroll-snap-align属性指定捕捉容器要捕捉的捕捉子元素位置。可选的值如下:

  • none :默认值,啥也不干 0.0。
  • start :跟开始位置对齐。
  • end :跟结束位置对齐。
  • center :居中对齐。

效果如下:

image

以上 DEMO 来自于 Andy Adams 的scroll-snap-align

scroll-snap-stop

因为 Scroll Snap 元素会有几个捕捉的位置,而scroll-snap-stop可以控制到达这些位置之后是否被捕获,还是到了指定的位置才被捕获。可选属性如下:

  • normal :默认值,滚动的时候,可以忽略捕捉位置。
  • always :滚动的时候,不能忽略捕捉位置,还必须定位到第一个捕捉元素的位置。

栗子如下:

image

以上 DEMO 来自于 MDN 的scroll-snap-stop

scroll-margin

scroll-margin是一个简写属性,跟margin一样,有不同的逻辑属性可以选。它可以设置元素跟滚动条之间的外边框大小。我们看两个动图对比下区别。

当我们点击#hash跳转时。

普通操作:

image
h3 {
}

添加了scroll-margin-top

image
h3 {
  scroll-margin-top: 5rem;
}

上面 DEMO 来自于 Chris Coyier 的Fixed Headers and Jump Links? The Solution is scroll-margin-top

从上面的两个 DEMO,我们可以清晰地对比,假设#hash导航的元素有修改scroll-margin,那么最终跳转的位置是会以scroll-margin的值为边界的。

不仅如此,我们再看下面的 DEMO:

image

以上 DEMO 源自于 Andy Adams 的scroll-margin

当我们设置了scroll-margin的元素进入 scroll 的可视区域时,浏览器会根据当前元素就近的scroll-margin值,移动到相应的位置。

scroll-margin的复写属性有以下几个:

  • scroll-margin-top
  • scroll-margin-right
  • scroll-margin-bottom
  • scroll-margin-left
  • scroll-margin-block
  • scroll-margin-inline
  • scroll-margin-block-start
  • scroll-margin-inline-start
  • scroll-margin-block-end
  • scroll-margin-inline-end

scroll-padding

scroll-paddingscroll-margin类型,只不过跟paddingmargin的一样,有内外边距的区别。

来个 DEMO:

image

以上 DEMO 源自于 Andy Adams 的scroll-padding

scroll-padding的复写属性也同样有以下几个:

  • scroll-padding-top
  • scroll-padding-right
  • scroll-padding-bottom
  • scroll-padding-left
  • scroll-padding-block
  • scroll-padding-inline
  • scroll-padding-block-start
  • scroll-padding-inline-start
  • scroll-padding-block-end
  • scroll-padding-inline-end

以上API兼容性如下:

image

CSS overscroll

overscroll-behavior

overscroll-behavior是 2019 年 6 月份 W3C 第一次发布的CSS 过渡滚动行为模块 Level 1里唯一一个属性。

overscroll-behavior让你可以控制浏览器滚动到边界时的表现。

它也是个简写属性,具体的属性有:

  • overscroll-behavior-x :正常情况下,处理横轴滚动条滚动到边界时的表现。
  • overscroll-behavior-y :正常情况下,处理纵轴滚动条滚动到边界时的表现。
  • overscroll-behavior-inline :跟overscroll-behavior-x一样。
  • overscroll-behavior-block :跟overscroll-behavior-y一样

可选的值为:

  • auto :默认值。
  • contain :当一个元素滚动到边界时,不会再影响临近的滚动元素。
  • none :当一个元素滚动到边界时,不仅不会不会再影响临近的滚动元素,连默认滚动到边界的表现都会被阻止。

栗子如下:

使用了overscroll-behavior: contain;

image

默认情况

image

兼容性如下:

image

课外姿势

新旧逻辑属性

不知道各位有没有注意上述各个属性的值,除了有常规的xytoprightbottomleft之外,还有四个比较少见的值blockinlinestartend

所以这到底是什么呢?

其实是因为 W3C 为了照顾到非西文排序国家的书写习惯,特意修改了 CSS 的逻辑属性。

对于像我们国家或者是美国这样,文档排列是从上到下,从左到右的,toprightbottomleft就很好理解。

但是像日本或者阿拉伯等书写排列跟我们不一样的国家,在逻辑上就会有不合理的地方,例如:

  • 在阿拉伯,他们的padding-left实际上方向是我们的padding-right
  • 在日本,他们的padding-left实际上方向是我们的padding-top

按照上面的情况,这就比较诡异。所以 W3C 出来新的逻辑属性,新旧的对比如下:

旧的逻辑属性 新的逻辑属性
top inset-block-start
bottom inset-block-end
left inset-inline-start
right inset-inline-end
margin-top margin-block-start
margin-right margin-inline-end
margin-bottom margin-block-end
margin-left margin-inline-start
border-top border-block-start
border-right border-inline-end
border-bottom border-block-end
border-left border-inline-start
padding-top padding-block-start
padding-right padding-inline-end
padding-bottom padding-block-end
padding-left padding-inline-start
width inline-size
height block-size

总结一下就是横坐标为inline,纵坐标为block,起始位置为start,结束位置为end

image

最后来个特效

这是一个利用scroll-behavior: smooth的特性写出来的效果,之前跟朋友们一起出去玩,我们进行了许多的活动。其中有一项游戏就是“你比划我猜”,作为策划者的鱼头,自然不能放过这次机会,遂用技术小秀了一把。

我们先来看看效果。

image

源码地址在这:

https://codepen.io/krischan77/pen/zYOjyry

由于代码太长,就不完全贴出来了,但是核心逻辑就是利用scroll-behavior: smooth;来控制#hash跳转时的效果,为了不污染 url,同时利用了 history api 来维护正常的 url。大概就是酱紫:

let pageIndex = 1;
const urlChangeHandler = event => {
  const { newURL } = event;
  const current = newURL.replace(/.+\#page\-(\d)/, "$1");
  pageIndex = +current;
  console.log(pageIndex);
  history.pushState(
    {},
    window.location.href,
    window.location.origin + window.location.pathname
  );
};
window.onhashchange = urlChangeHandler;

大家也不妨尝试下用所掌握的姿势增添点生活情趣呀~

image

后记

吃瓜群众:我看完了整篇,没看到哪里有跟忍术相关的内容啊?骗我流量,赔钱。

鱼头:没有又咋啦?说好的宠我,你现在凶我是什么意思?

image

参考资料

  1. scrollIntoView block vs inline
  2. CSSOM View Module
  3. Element.scrollIntoView()
  4. CSS Scroll Snap Module Level 1
  5. CSS TRICK scroll-snap-type
  6. MDN scroll-snap-type
  7. CSS TRICK scroll-snap-align
  8. scroll-snap-stop
  9. scroll-margin
  10. scroll-padding
  11. Fixed Headers and Jump Links? The Solution is scroll-margin-top
  12. caniuse

后记

如果你喜欢探讨技术,或者对本文有任何的意见或建议,非常欢迎加鱼头微信好友一起探讨,当然,鱼头也非常希望能跟你一起聊生活,聊爱好,谈天说地。
鱼头的微信号是:krisChans95
也可以扫码关注公众号,订阅更多精彩内容。

https://user-gold-cdn.xitu.io/2020/3/4/170a55cc795174aa?w=1000&h=480&f=png&s=311000
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 一、CSS Scroll Snap简介 CSS Scroll Snap是CSS中一个独立的模块,可以让网页容器滚动...
    pansly阅读 1,825评论 0 2
  • 1. tab列表折叠效果 html: 能源系统事业部 岗位名称: 工作地点 岗位名...
    lilyping阅读 1,764评论 0 1
  • day01­_起源和结构 结构:Xhtml xml 表现:CSS 行为:DOM ECMAScript 以上都属于W...
    Sakura_明妃阅读 1,094评论 0 1
  • 一、在什么场景下会出现外边距合并?如何合并?如何不让相邻元素外边距合并?给个父子外边距合并的范例 在CSS当中,相...
    dengpan阅读 530评论 0 0
  • 前端开发面试题 面试题目: 根据你的等级和职位的变化,入门级到专家级,广度和深度都会有所增加。 题目类型: 理论知...
    怡宝丶阅读 2,548评论 0 7