sal
是以效能為中心,輕量級的滾動動畫庫
1.前言
sal
(滾動擴充套件庫)為滾動動畫提供高效能和輕量級的解決方案。sal
採用Intersection Observer,在視口中,它在檢查元素方面提供了很好的效能。強烈建議優先閱讀阮大神的IntersectionObserver API 使用教程文章,瞭解基本IntersectionObserver
的使用
本篇讀後感分為五部分,分別為前言、使用、解析、demo、總結,五部分互不相連可根據需要分開看。
1前言為介紹、2使用為庫的使用、3解析為原始碼的解析、4demo是抽取原始碼的核心實現的小demo,5總結為吹水,學以致用。
建議跟著原始碼結合本文閱讀,這樣更加容易理解!
2.使用
<
!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
屬性的標籤就會隨著滾動展示動畫效果。
data-sal
有三種選項:
data-sal-duration
– 動畫時長;data-sal-delay
– 動畫延遲時間;data-sal-easing
– 動畫速度曲線。
sal
函式接收三個引數:
threshold
– 目標元素的可見比例once
– 只執行一次動畫disable
– 禁用動畫
3.解析
庫的原理是通過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;
複製程式碼
4.demo
通過實現阮大神的兩個例子來上手IntersectionObserver
,也是sal
的原理
4.1 惰性載入(lazy load)
當滾動到一定位置的時候,再載入對應的圖片
<
!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.3var 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>
複製程式碼
4.2 無限滾動(infinite scroll)
觀察列表底部元素載入更多,每當達到設定的可見比例時,就載入資料到列表中
<
!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 = 0var skip = 10var threshold = 0.9function 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>
複製程式碼
5.總結
sal
這個庫其實主要是對IntersectionObserver
的應用,程式碼簡單僅僅只有一百多行,但由於IntersectionObserver
還只是個實驗階段的api(雖然chrome
支援了),在實際專案中運用的機會不是太大,但是對它抱有期待。就如無限滾動的例子,如果不使用IntersectionObserver
的話,就得監聽瀏覽器滾動事件,獲取列表高度、視窗高度和滾動高度來計算是否滾動到底部,必要情況下還需要加上防抖動來優化使用者體驗,所以IntersectionObserver
還是省去很多步驟的,看好!
轉眼就到了2019年了,要堅持分享輸出!