开源库学习之Trigger JS
·2 分钟
Table of Contents
简介 #
一直很好奇像苹果官网中,通过页面滚动实现的产品介绍效果是怎么做出来。
之前有简单了解过视觉差,主要是通过设置背景图来实现的。
今天了解到一个叫Trigger JS
的库,在页面滚动时可以通过CSS变量取得动画所需要的值,而不需要额外写JS代码。
使用方法 #
Tigger JS中文文档 官方给的范例代码:
<div
class="container"
tg-name="size"
tg-edge="inset"
tg-from="14"
tg-to="20"
tg-bezier="0.23, 1.5, 0.32, 2"
>
<div class="container" id="content" tg-name="opacity" tg-from="0" tg-to="1">
<div class="sticky">
<span>Hello.</span>
</div>
</div>
</div>
:root {
font-family: Helvetica, sans-serif;
font-size: 20px;
--blur: 100;
--opacity: 0;
}
body {
padding: 0;
margin: 0;
}
.container {
height: 300vh;
}
.sticky {
font-size: 10rem;
font-weight: bold;
letter-spacing: -0.03em;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
position: sticky;
top: 0;
font-size: calc(var(--size) * 1em);
}
span {
opacity: var(--opacity);
}
DOM元素上添加的tg-name
属性的值就是页面滚动时需要改变的数值的CSS变量名。
父div
的tag-name
属性值设为size
,css代码中就可以使用--size
变量,上面的例子中,在设置font-size
字体大小时读取了这个css变量,calc(var(--size) * 1em)
相当于给变量设置了em
作为单位。
div
上还有tg-from
和tg-to
属性,代表的是size
的值随着页面滚动由14
变化到20
,tg-bezier
是用来设置动画的贝塞尔曲线的。
而子div
上的tag-name
值为opacity
,它的值变化是从0
到1
。
我们当然也能通过监听元素的tag
事件来获取到当前的值是多少:
document.querySelector('#content').addEventListener('tg', (e) => {
console.log(e.detail); // {value: '1'}
});
以上就是这个库的大致用法。
源码解析 #
由上面的源码结构图可以看出我们需要着重解读的是trigger.ts
中的代码。
在index
文件中调用了Trigger.start()
方法,先来看看这个方法是什么:
const Trigger: TriggerType = {
start() {
if (!document.body) {
console.warn(`Unable to initialise, document.body does not exist.`);
return;
}
observeElements();
eventListeners();
},
};
方法里调用了两个私有函数:
// 初始化元素监听
function observeElements() {
// observer是对IntersectionObserver API的封装,传入一个回调函数
ob = observer((entries) => {
entries.forEach((entry) => {
// target:目标元素
let { target } = entry;
// 判断元素是否在当前视窗中
if (entry.isIntersecting) {
/*
通过调用parseAttributes方法获取HTML上所有自定义属性的值,
并返回一个带有原始el对象和所有属性值的对象
{
el: element,
top,
height,
name,
from,
to,
steps,
step,
mapping,
filter,
edge,
range,
increment,
segments,
decimals,
multiplier,
lastValue: null,
bezier,
}
方便后续监听scroll事件时获取;
把对象推入到一个监听数组中存起来
*/
activeElements.push(parseAttributes(target as HTMLElement));
} else {
// 如果元素不在视窗中就从监听数组中移除
activeElements = activeElements.filter(function (obj) {
return obj.el !== target;
});
}
});
});
}
function eventListeners() {
window.addEventListener('DOMContentLoaded', () => {
/*
bind()是对IntersectionObserver.observe()方法的封装,监听所有在视窗中的元素
并添加两个钩子函数,before()和after(),在监听窗口resize事件时会使用到
*/
bind(ob);
setTimeout(() => {
// 一开始就获取所有带有自定义属性的元素,并解析元素的属性值,保证页面滚动前可以获取到正确的样式
let allElements = [
...document.querySelectorAll(`[${getPrefix()}name]`),
].map((element) => {
return parseAttributes(element as HTMLElement);
});
/*
parseValues是这个库最核心的计算方法,当监听到滚动事件时就会调用这个方法
在这个方法中不仅会设置相应的style属性值
并且当属性值发生变化的时候会将自定义tg事件通过el.dispatchEvent方法派发到监听的元素上
*/
parseValues(allElements);
});
});
// 当窗口大小发生变化时重新监听元素
window.addEventListener('resize', () => {
bind(ob, {
// 调用 IntersectionObserver.observe()方法之前的钩子函数
before: () => {
// 解除所有监听数组中元素的监听
activeElements.forEach((element) => {
ob?.unobserve(element.el);
});
// 清除监听数组
activeElements = [];
},
});
});
// 监听滚动事件,改变当前视窗内元素的属性值
window.addEventListener('scroll', (e) => {
parseValues(activeElements);
});
}
实现逻辑 #
通过对trigger.ts
代码的阅读,可以看出整个库最主要的实现原理就是通过IntersectionObserver
对元素进行监听,然后使用parseValues()
方法计算并设置元素的属性值。