sal
是以性能为中心,轻量级的滚动动画库javascript
sal
(滚动扩展库)为滚动动画提供高性能和轻量级的解决方案。sal
采用Intersection Observer,在视口中,它在检查元素方面提供了很好的性能。强烈建议优先阅读阮大神的IntersectionObserver API 使用教程文章,了解基本IntersectionObserver
的使用css
本篇读后感分为五部分,分别为前言、使用、解析、demo、总结,五部分互不相连可根据须要分开看。html
1前言为介绍、2使用为库的使用、3解析为源码的解析、4demo是抽取源码的核心实现的小demo,5总结为吹水,学以至用。java
建议跟着源码结合本文阅读,这样更加容易理解!git
<!DOCTYPE html>
<html lang="en">
<body>
<div data-sal="slide-up" data-sal-delay="300" data-sal-easing="ease-out-bounce" ></div>
</body>
<script> sal({ once: false }); </script>
</html>
复制代码
当页面开始滚动时,为标签添加了data-sal
属性的标签就会随着滚动展现动画效果。github
data-sal
有三种选项:chrome
data-sal-duration
- 动画时长;data-sal-delay
- 动画延迟时间;data-sal-easing
- 动画速度曲线。sal
函数接收三个参数:api
threshold
- 目标元素的可见比例once
- 只执行一次动画disable
- 禁用动画库的原理是经过IntersectionObserver
的api
,观察目标元素的可见比例,经过添加或者移除class
来启动动画浏览器
import './sal.scss';
/** * 默认选项 */
let options = {
rootMargin: '0% 50%',
threshold: 0.5,
animateClassName: 'sal-animate',
disabledClassName: 'sal-disabled',
selector: '[data-sal]',
once: true,
disabled: false,
};
/** * 私有 */
let elements = [];
let intersectionObserver = null;
/** * 为元素添加class启动动画 * @param {Node} element */
const animate = element => (
element.classList.add(options.animateClassName)
);
/** * 经过移除class来反转启动动画 * @param {Node} element */
const reverse = element => (
element.classList.remove(options.animateClassName)
);
/** * 元素是否已经启动过动画 * @param {Node} element */
const isAnimated = element => (
element.classList.contains(options.animateClassName)
);
/** * 为元素移除disabledClassName来启用动画 */
const enableAnimations = () => {
document.body.classList.remove(options.disabledClassName);
};
/** * 经过添加class来禁用动画 */
const disableAnimations = () => {
document.body.classList.add(options.disabledClassName);
};
/** * 是否禁用动画 * @return {Boolean} */
const isDisabled = () => (
options.disabled ||
(
(typeof options.disabled === 'function') &&
options.disabled()
)
);
/** * IntersectionObserver的回调函数 * @param {Array<IntersectionObserverEntry>} entries * @param {IntersectionObserver} observer */
const onIntersection = (entries, observer) => {
entries.forEach((entry) => {
if (entry.intersectionRatio >= options.threshold) {
// 元素的可见比例大于配置的可见比例,启动动画
animate(entry.target);
if (options.once) {
observer.unobserve(entry.target);
}
} else if (!options.once) {
// 不然,启动反转动画
reverse(entry.target);
}
});
};
/** * 禁用sal */
const disable = () => {
disableAnimations();
intersectionObserver.disconnect();
intersectionObserver = null;
};
/** * 启动 */
const enable = () => {
enableAnimations();
/** * 设置对观察元素变化后的行为函数 * intersectionObserver:观察者 * onIntersection:观察到变化的行为函数 */
intersectionObserver = new IntersectionObserver(onIntersection, {
rootMargin: options.rootMargin,
threshold: options.threshold,
});
// 获取观察元素
elements = [].filter.call(
document.querySelectorAll(options.selector),
element => !isAnimated(element, options.animateClassName),
);
// 为观察元素设置观察者,当变化后触发行为函数
elements.forEach(element => intersectionObserver.observe(element));
};
/** * Init * @param {Object} settings * @return {Object} public API */
const init = (settings = options) => {
// 初始化配置
if (settings !== options) {
options = {
...options,
...settings,
};
}
// 判断浏览器是否存在IntersectionObserver
if (!window.IntersectionObserver) {
disableAnimations();
throw Error(` Your browser does not support IntersectionObserver! Get a polyfill from here: https://github.com/w3c/IntersectionObserver/tree/master/polyfill `);
}
// 开始和结束动画
if (!isDisabled()) {
enable();
} else {
disableAnimations();
}
return {
elements,
disable,
enable,
};
};
export default init;
复制代码
经过实现阮大神的两个例子来上手IntersectionObserver
,也是sal
的原理app
当滚动到必定位置的时候,再加载对应的图片
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>lazyLoad</title>
<style> html, body{ height: 100%; padding: 0; margin: 0; } .block{ width: 100%; height: 700px; } .red{ background-color: red; } .green{ background-color: green; } .yellow{ background-color: yellow; } img{ width: 100%; } </style>
</head>
<body>
<div class="block red"></div>
<div class="block green"></div>
<div class="block yellow"></div>
</body>
<script> var threshold = 0.3 var onIntersection = (changes, observer) => { changes.forEach(function(change) { var container = change.target if (change.intersectionRatio > threshold) { var img = new Image() img.src = './fafa.jpeg' container.append(img) observer.unobserve(container) } }) } var observer = new IntersectionObserver(onIntersection, {threshold}) document.querySelectorAll('.block').forEach(element => observer.observe(element)) </script>
</html>
复制代码
观察列表底部元素加载更多,每当达到设定的可见比例时,就加载数据到列表中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>lazyLoad</title>
<style> html, body{ height: 100%; padding: 0; margin: 0; } h1{ border-bottom: 1px solid #000; } </style>
</head>
<body>
<div class="wrap">
<div class="list"></div>
<div class="bottom">加载更多</div>
</div>
</body>
<script> var num = 0 var skip = 10 var threshold = 0.9 function load(){ var list = document.querySelector('.list') var fragment = document.createDocumentFragment(); Array(skip).fill().forEach((v, i) => { var dom = document.createElement('h1') num += 1 dom.innerText = num fragment.append(dom) }) list.append(fragment) } var onIntersection = (changes, observer) => { changes.forEach(function(change) { if (change.intersectionRatio > threshold) load() }) } var observer = new IntersectionObserver(onIntersection, {threshold}) observer.observe(document.querySelector('.bottom')) </script>
</html>
复制代码
sal
这个库其实主要是对IntersectionObserver
的应用,代码简单仅仅只有一百多行,但因为IntersectionObserver
还只是个实验阶段的api(虽然chrome
支持了),在实际项目中运用的机会不是太大,可是对它抱有期待。就如无限滚动的例子,若是不使用IntersectionObserver
的话,就得监听浏览器滚动事件,获取列表高度、窗口高度和滚动高度来计算是否滚动到底部,必要状况下还须要加上防抖动来优化用户体验,因此IntersectionObserver
仍是省去不少步骤的,看好!
转眼就到了2019年了,要坚持分享输出!