skrollr 官方示例全解析:从经典视差到锚点、缓动与 SVG 路径绘制
skrollr 官方示例全解析:从经典视差到锚点、缓动与 SVG 路径绘制
本篇指南以 skrollr 仓库 examples/README.md 中收录的官方示例清单为主线,逐个拆解其背后的 data-* 属性语法、锚点机制、自定义缓动函数与渲染回调等核心技术点,并结合 src/skrollr.js 源码说明底层实现。读完本文,你将掌握如何把 skrollr 的示例移植到自己的页面中,以及每个示例究竟演示了什么能力、改哪些参数可以控制效果。
示例目录与运行方式
examples/ 目录是 skrollr 官方效果的集中展示区,每个 HTML 文件都对应一个独立、可复制的技术场景。它们全部使用浏览器原生 JavaScript,不依赖 jQuery,仅引入构建产物 dist/skrollr.min.js(仓库内对应源码为 src/skrollr.js)。
本地查看这些示例时,需要在一个 HTTP 服务下运行(直接双击打开 file:// 协议在某些浏览器中可能受限)。最简单的方式是在仓库根目录启动静态服务器,例如:
python -m http.server 8000
然后访问 http://localhost:8000/examples/classic.html 即可。注意示例脚本路径为 ../dist/skrollr.min.js,必须保持 examples/ 与 dist/ 的相对目录结构才能正常加载。
examples/README.md 一共收录了 8 个示例,分别聚焦不同的能力,对应关系如下:
| 示例文件(仓库内路径) | 演示主题 |
|---|---|
| index.html | 主示例:综合展示 skrollr 的绝大多数能力 |
| examples/classic.html | 经典视差:多个分区 + 分区之间穿插视差图片 |
| examples/anchors.html | 锚点:data-start/data-end 等不同锚点类型 |
| examples/anchor_target.html | 锚点目标:data-anchor-target 绑定其他元素 |
| examples/pausing.html | 暂停:滚动到某处暂停,穿插其他动画 |
| examples/path.html | 绘制 SVG 路径:stroke-dashoffset 动画 |
| examples/circular_motion.html | 圆周运动:两个自定义缓动函数驱动 |
| examples/bg_constant_speed_less.html | 恒定速度视差背景(内容较少版) |
| examples/gradientsmotherfucker.html | 渐变:旋转的 CSS 线性渐变背景 |
其中 examples/bg_constant_speed_more.html 是恒定速度背景示例的"更多内容"对照版,与 bg_constant_speed_less.html 成对出现,用于演示相同动画效果与内容高度无关。
核心语法基础:一切从 data-* 属性开始
在深入每个示例前,先建立 skrollr 的语法心智模型。skrollr 不要求你写任何 JavaScript 动画代码,它扫描 DOM 中带 data-[数值] 或 data-[锚点] 形式的属性,把属性值当作关键帧,根据滚动位置在关键帧之间线性插值。属性值格式为 CSS 声明列表:
<div data-0="opacity:1;" data-500="opacity:0;"></div>
表示:滚动位置为 0 时透明度为 1,滚动到 500px 时透明度为 0,中间过程由 skrollr 自动插值。
这种设计在主示例 index.html 中体现得淋漓尽致。它把 background-position、width、top、transform、opacity、display 等大量属性串成一条滚动时间线,例如:
<div id="bg1" data-0="background-position:0px 0px;" data-end="background-position:-500px -10000px;"></div>
<div id="progress" data-0="width:0%;background:hsl(200, 100%, 50%);" data-end="width:100%;background:hsl(920, 100%, 50%);"></div>
data-end 是特殊锚点,代表"文档滚动到底部";data-0 则代表"文档顶部"(配合 forceHeight 默认开启时,页面高度会被撑高以容纳动画空间,见下文)。主示例还展示了动画的不仅仅是 transform 和 opacity,还包括 hsl() 颜色、width、border-radius、font-size、z-index 等数值属性,甚至是 display:none 这样的非数值切换。对应地,src/skrollr.js 在解析属性值时会区分可插值的数值/颜色与只能跳变的离散值,这正是 index.html 里 data-3000="display:block;" data-3700="top:-100%;display:none;" 能工作的原因。
示例一:经典分区视差(classic.html)
examples/classic.html 演示了最常见的商业站点视差布局:多个内容分区(header/content),分区之间夹着全屏图片"gap",图片以 position:fixed 固定在视口内,随滚动以不同速度移动。
其关键 HTML 结构(节选)为:
<div class="parallax-image-wrapper parallax-image-wrapper-100"
data-anchor-target="#dragons + .gap"
data-bottom-top="transform:translate3d(0px, 200%, 0px)"
data-top-bottom="transform:translate3d(0px, 0%, 0px)">
<div class="parallax-image parallax-image-100"
style="background-image:url(images/kitteh1.jpg)"
data-anchor-target="#dragons + .gap"
data-bottom-top="transform: translate3d(0px, -80%, 0px);"
data-top-bottom="transform: translate3d(0px, 80%, 0px);"></div>
</div>
这里有两个重要机制:
data-anchor-target重定向锚点:外层 wrapper 和内层图片的动画进度都不以自身位置为基准,而是以#dragons + .gap这个兄弟选择器匹配到的元素(即标题分区#dragons后面紧跟的.gap占位块)为基准。这是 skrollr 允许把动画绑定到页面任意元素的关键特性,源码解析见 src/skrollr.js:运行时读取元素的data-anchor-target属性,把该属性值作为 CSS 选择器去querySelector,找不到时回退到元素自身。- 双层反向位移制造视差深度:wrapper 从
200%移到0%,内层图片从-80%移到+80%,两者速度差让图片看起来"穿行"在内容之间。注释也直接指出:"the +/-80% translation can be adjusted to control the speed difference of the image"——调大这组百分比就能增强视差速度差。
配套 CSS 中 .skrollr-desktop body { height:100% !important; }、.skrollr .gap { background:transparent !important; } 与 .parallax-image.skrollable-between { display:block; } 一起构成了"渐进增强"策略:无 JavaScript 时显示静态背景图,启用 skrollr 后隐藏 .gap 的真实背景、只显示固定定位的动画图片层。同时页面把固定定位的图片层放在 #skrollr-body 之外,这是 skrollr 使用 position:fixed 元素的硬性要求——#skrollr-body 内部的元素会被视为普通文档流,固定定位才不会被滚动容器干扰。
该示例的初始化参数也值得注意:
skrollr.init({
smoothScrolling: false,
mobileDeceleration: 0.004
});
smoothScrolling: false 关闭桌面端平滑滚动(直接跟随原生滚动),mobileDeceleration: 0.004 设置移动端惯性滚动减速系数,数值越小减速越慢、滑得越远。对应源码默认值见 src/skrollr.js:mobileDeceleration 默认 0.004,smoothScrolling 默认开启。
示例二:锚点类型大全(anchors.html)
examples/anchors.html 是一个纯粹的锚点语法教学页,页面中 7 个彩色方块分别演示 7 种锚点写法(节选):
<div class="box" data-start="background:hsl(0,50%,75%);" data-end="background:hsl(180,50%,75%);">start / end</div>
<div class="box" data-bottom-top="background:hsl(0,50%,75%);" data-top-bottom="background:hsl(180,50%,75%);">bottom-top / top-bottom</div>
<div class="box" data-top-top="background:hsl(0,50%,75%);" data-bottom-bottom="background:hsl(180,50%,75%);">top-top / bottom-bottom</div>
<div class="box" data-center-top="background:hsl(0,50%,75%);" data-center-bottom="background:hsl(180,50%,75%);">center-top / center-bottom</div>
<div id="bacon" class="box" data-center-center="background:hsl(0,50%,75%);" data-1-center-center="background:hsl(180,50%,75%);">center-center / 1-center-center</div>
<div class="box" data-50-bottom-bottom="background:hsl(0,50%,75%);" data--50-top-top="background:hsl(180,50%,75%);">50-bottom-bottom / -50-top-top</div>
<div class="box" data-300-end="background:hsl(0,50%,75%);" data-end="background:hsl(180,50%,75%);">300-end / end</div>
锚点名的通用格式为 [数字][视图位置]-[元素位置],语义是"元素的哪个边缘,与视口的哪个位置对齐时触发":
top/bottom/center指元素自身的边缘或中心;- 前缀数字(如
1、50、300、-50)表示元素边缘越过视口对应位置n个自身高度(正数向下越、负数向上越),单位是元素高度倍数; start/end则分别对应文档顶部/底部,等价于全局滚动位置 0 与最大滚动位置,可省略数字前缀直接使用。
例如 data-50-bottom-bottom 表示"元素底部越过视口底部 50 个元素高度"时到达关键帧;data--50-top-top 表示"元素顶部在视口顶部上方 50 个元素高度"处。data-300-end 则把关键帧定在距文档底部 300px 处。
该页初始化时设置了 skrollr.init({ forceHeight: false }),因为示例需要大量真实文档流内容来产生滚动距离,不再需要 skrollr 自动撑高页面。forceHeight 默认为 true(见 src/skrollr.js),开启时 skrollr 会在文档末尾插入一个高度等于滚动最大位置的元素,确保 data-end 等锚点有足够的滚动空间;内容本身足够高时设为 false 可避免多余的空白。
示例三:把动画绑到别的元素上(anchor_target.html)
examples/anchor_target.html 是 data-anchor-target 的专项演示:页面右侧有一个 position:fixed 的蓝色方块,它的动画进度完全跟随左侧文档流中的红色方块 #red——红方块滚出视野时,蓝方块同步旋转并"振动"。
关键代码(节选):
<div id="red" data-bottom="transform:rotate(0deg);transform-origin:center;" data-top="transform:rotate(360deg);"></div>
<div id="blue_wrapper" data-smooth-scrolling="off">
<div id="blue"
data-anchor-target="#red"
data-0="background-position:!0 bottom;"
data-bottom-top="margin-right[vibrate]:0px;background-position:!0 0;"
data-top-bottom="margin-right[vibrate]:20px;background-position:!0 bottom;"></div>
</div>
本示例带出三个进阶知识点:
data-anchor-target的值是选择器:#blue的动画以#red的位置为基准。结合示例一可以总结出规则——想让某个元素(尤其是position:fixed元素)跟随其他元素的位置变化,就在它上面写data-anchor-target="<选择器>"。data-smooth-scrolling="off"元素级开关:在#blue_wrapper上关闭平滑滚动,让振动效果不被打散。源码在 src/skrollr.js 读取该属性,若为"off"则该 skrollable 不参与平滑滚动插值。[easing]属性级缓动与!强制值前缀:margin-right[vibrate]表示该属性使用名为vibrate的自定义缓动;background-position:!0 0中的!前缀表示该属性的值不做插值、直接使用关键帧原值(用于避免 background-position 插值导致的非预期中间帧)。自定义缓动在初始化时注册:
skrollr.init({
forceHeight: false,
easing: {
vibrate: function(p) {
return Math.sin(p * 10 * Math.PI);
}
}
});
缓动函数接收归一化进度 p(0 到 1),返回变换后的进度值,vibrate 用 sin 在 0~1 区间内来回振荡 5 个周期,于是蓝方块在动画区间内快速抖动。主示例 index.html 中还演示了 linear、quadratic、cubic、swing、WTF(即 Math.random)、inverted(1-p)、bounce 等多种缓动,以及 color[swing] 这种"只给颜色属性套缓动"的写法。
示例四:用 constants 常量实现滚动暂停(pausing.html)
examples/pausing.html 演示如何在滚动途中"暂停":三张全屏图层依次切换,第二层滚动到一半时内部的蓝色进度条用 150% 视口高度的滚动距离慢慢填满,形成视觉上的停留。
HTML 关键结构(节选):
<div data-0="transform:translate(0,0%);" data-100p="transform:translate(0,-100%);"></div>
<div data-0="transform:translate(0,100%);" data-100p="transform:translate(0,0%)"
data-_box-100p="" data-_box-200p="transform:translate(0,-100%)">
<div class="box" data-100p="width:0%;" data-_box-100p="width:100%"></div>
</div>
<div data-_box-100p="transform:translate(0,100%);" data-_box-200p="transform:translate(0,0%);"></div>
初始化代码定义了常量:
skrollr.init({
constants: {
//fill the box for a "duration" of 150% of the viewport (pause for 150%)
//adjust for shorter/longer pause
box: '150p'
}
});
这里的核心是 constants(常量)机制:
data-100p中的100p是内建常量语法,p后缀表示"视口高度的百分比",即滚动到 1 个视口高度处。- 自定义常量
box: '150p'定义后,data-_box-100p中的_box会被替换为150p,于是该锚点实际等于data-150p-100p——比data-100p再远 150% 视口高度。蓝色进度条就在这段"额外 150% 视口高度"的滚动中从width:0%填到width:100%,第三张图也在这之后才开始入场,从而产生"暂停"错觉。
改注释中 box: '150p' 的数值即可加长/缩短暂停距离。源码层面,constants 在 src/skrollr.js 从 options.constants 读取,并在 src/skrollr.js 的求值函数中对所有 _xxx 形式的常量进行字符串替换(支持函数与字符串值),窗口 resize 时还会重新求值——因此常量可以安全地依赖视口尺寸(如 p 单位)。
示例五:滚动驱动 SVG 路径描边(path.html)
examples/path.html 展示了一个精巧的组合用法:不画任何 canvas,仅靠 skrollr 驱动 SVG 的 stroke-dashoffset 属性,让一条复杂贝塞尔路径在滚动时"被画出来"。
关键代码(节选):
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="900px" height="1200px">
<path
style="fill:none;stroke:#333333;stroke-width:7;...;stroke-dasharray:6000;stroke-dashoffset:0"
data-0="stroke-dashoffset:6000;" data-end="stroke-dashoffset:0;"
d="m 199.16266,227.29566 c ..." />
</svg>
原理是:stroke-dasharray 设为路径总长(此处约 6000px),起始时 stroke-dashoffset: 6000 让整条虚线全部"偏移出"可视区域(看起来为空),滚动到文档末尾时 stroke-dashoffset: 0 让虚线完整呈现。由于 stroke-dashoffset 是数值属性,skrollr 可以逐像素插值,滚动过程就等价于路径描边动画。这个示例再次印证了 skrollr 的通用性:任何 CSS 数值属性都可以被滚动驱动,SVG 呈现属性也不例外。初始化同样使用 skrollr.init({ forceHeight: false }),因为 1200px 高的 SVG 加正文已经提供了足够的滚动距离。
示例六:两个缓动函数合成圆周运动(circular_motion.html)
examples/circular_motion.html 演示如何用缓动函数把一条直线滚动"翻译"成二维圆周运动:一个黑色圆点沿圆形轨迹绕圈,滚到底部后自动回到顶部继续循环。
HTML 与初始化代码:
<div id="thing" data-0="left[cos]:10%;top[sin]:10%;" data-10000="left:90%;top:90%;"></div>
<script>
skrollr.init({
easing: {
sin: function(p) {
return (Math.sin(p * Math.PI * 2 - Math.PI/2) + 1) / 2;
},
cos: function(p) {
return (Math.cos(p * Math.PI * 2 - Math.PI/2) + 1) / 2;
},
},
render: function(data) {
//Loop
if(data.curTop === data.maxTop) {
this.setScrollTop(0, true);
}
}
});
</script>
两个关键点:
- 属性级缓动
[sin]/[cos]:left[cos]让水平位置按余弦曲线变化,top[sin]让垂直位置按正弦曲线变化,两者相位相差 90°,叠加后元素轨迹即圆周。自定义函数把三角函数输出归一化到 0~1 区间,保证最终值落在两个关键帧(10% → 90%)的范围内。 render回调 +setScrollTop:render在每次渲染时被调用,参数data携带curTop/maxTop等当前滚动信息;当滚动到达最大值时调用this.setScrollTop(0, true)强制跳回顶部(第二个参数true表示即时跳转、不做平滑动画),从而形成无限循环。这是 skrollr 官方示例中唯一使用 JavaScript 回调解耦动画循环的案例,也展示了 src/skrollr.js 对外暴露的setScrollTopAPI 的典型用法。
示例七:与内容高度无关的恒定速度背景(bg_constant_speed_less / more)
examples/bg_constant_speed_less.html 与 examples/bg_constant_speed_more.html 是一对对照实验:页面标题明确写道 "Demo of background scrolling at constant speed independent of content height"——背景以恒定速度滚动,且不受内容高度影响。
两个文件的实现完全一致(节选):
<body data-0="background-position:0px 0px;" data-100000="background-position:0px -50000px;">
要点在于:
- 关键帧用绝对像素
data-100000(滚动 100000px 处)而不是百分比锚点,因此背景位移-50000px与视口、内容高度都无关,只取决于全局滚动量——这就是"恒定速度"的来源。 - 两个页面一个内容少、一个内容多,但同一个
body关键帧让背景移动速度完全相同,直观证明速度与内容高度解耦。 - 配合 CSS
background:url(images/bubbles.png) repeat fixed;使用position:fixed背景图,避免背景跟随文档滚动。 - 两页初始化均为
skrollr.init({ forceHeight: false }),依赖真实内容高度产生滚动距离。
值得注意的是,这种写法会让 data-end 之外的剩余滚动继续无动画(滚动超过 100000px 后背景停在 -50000px),实际项目中应根据页面最大滚动高度估算合适的终值,或改用 data-end 配合百分比换算。
示例八:旋转渐变背景(gradientsmotherfucker.html)
examples/gradientsmotherfucker.html 展示了对 CSS 渐变的滚动驱动:页面背景从红/橙渐变旋转到 10 圈之后的多色渐变,底部还有一张图片随接近页尾而淡入。
关键代码(节选):
<body data-0="background-image:linear-gradient(0deg, hsl(0, 100%, 50%), hsl(40, 50%, 50%));"
data-5000="background-image:linear-gradient(3600deg, hsl(360, 100%, 50%), hsl(400, 100%, 50%));">
<div id="bottom" data-500-end="opacity:0;" data-end="opacity:1;">
<img src="images/slj.png" />
</div>
这里展示了 skrollr 对**非数值属性(background-image)**的处理:linear-gradient 本身无法插值,skrollr 会在关键帧之间直接切换(跳变),因此示例通过把角度参数从 0deg 一步改到 3600deg、配合渐变色相从 0 跳到 360,在到达 data-5000 的瞬间完成"旋转"——这是属性级离散值的标准用法。而 #bottom 的 opacity 则走正常的数值插值,在最后 500px 滚动中淡入。初始化直接 skrollr.init(),使用全部默认参数(forceHeight: true 会撑高页面以容纳 5000px 的动画空间)。
从示例反推:核心初始化参数速查
综合 8 个示例的初始化配置,可以整理出最常用的 skrollr.init(options) 参数及它们在 src/skrollr.js 中的默认值:
| 参数 | 默认值 | 作用 | 出现示例 |
|---|---|---|---|
forceHeight |
true |
是否在文档末尾插入元素撑高页面,为 data-end 等锚点预留滚动空间 |
anchors.html、anchor_target.html、path.html 等设为 false |
smoothScrolling |
true |
桌面端是否用平滑滚动插值替代原生滚动 | classic.html 设为 false |
mobileDeceleration |
0.004 |
移动端触摸惯性滚动的减速系数,越小滑行越远 | classic.html 设为 0.004 |
easing |
内置 linear 等 |
注册自定义缓动函数,可在 属性名[缓动名] 中按属性使用 |
anchor_target.html 的 vibrate、circular_motion.html 的 sin/cos、index.html 的多种内置缓动 |
constants |
空对象 | 定义 _name 形式的常量,在锚点中替换,支持依赖视口尺寸的 p 单位 |
pausing.html 的 box: '150p' |
render |
无 | 每次渲染回调,接收含 curTop/maxTop 的数据对象,可用于循环、统计等 |
circular_motion.html 的滚动循环 |
对应的选项解析逻辑集中在 src/skrollr.js,forceHeight 的行为(是否在页面末尾追加占位元素)在 src/skrollr.js,移动端减速距离的计算在 src/skrollr.js。
小结:把官方示例变成你的工具箱
examples/ 目录的 8 个示例覆盖了 skrollr 的全部核心能力,可以当作一份"能力地图"来检索:
- 布局与视差:照抄 classic.html 的双层
data-anchor-target+ 固定定位结构,即可复刻主流视差站点; - 锚点语义:锚点语法以 anchors.html 为准,先想清楚"元素的边 vs 视口的边"再写属性名;
- 元素解耦动画:凡是"元素不动、动画跟随别处",一律用
data-anchor-target(anchor_target.html); - 节奏控制:需要停留/节奏感时用
constants加长关键帧间距(pausing.html); - 非常规目标:SVG 属性、
background-image跳变、背景恒定速度、双缓动合成圆周运动,分别对应 path.html、gradientsmotherfucker.html、bg_constant_speed_less.html 与 circular_motion.html。
每个示例都只有不到 100 行、无任何构建依赖,直接复制 HTML 片段、替换选择器与关键帧数值即可快速落地。想进一步验证行为细节,可阅读测试目录 test/tests.js 中针对锚点解析、缓动与属性插值的断言,以及 README.md 顶部的快速上手说明。