sal源碼解析輕量級的滾動動畫庫

NO IMAGE

sal是以性能為中心,輕量級的滾動動畫庫

1.前言

sal(滾動擴展庫)為滾動動畫提供高性能和輕量級的解決方案。sal採用Intersection Observer,在視口中,它在檢查元素方面提供了很好的性能。強烈建議優先閱讀阮大神的IntersectionObserver API 使用教程文章,瞭解基本IntersectionObserver的使用

本篇讀後感分為五部分,分別為前言、使用、解析、demo、總結,五部分互不相連可根據需要分開看。

1前言為介紹、2使用為庫的使用、3解析為源碼的解析、4demo是抽取源碼的核心實現的小demo,5總結為吹水,學以致用。

建議跟著源碼結合本文閱讀,這樣更加容易理解!

  1. IntersectionObserver API 使用教程
  2. sal
  3. sal解析的Github地址

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.解析

庫的原理是通過IntersectionObserverapi,觀察目標元素的可見比例,通過添加或者移除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.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>

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 = 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>

5.總結

sal這個庫其實主要是對IntersectionObserver的應用,代碼簡單僅僅只有一百多行,但由於IntersectionObserver還只是個實驗階段的api(雖然chrome支持了),在實際項目中運用的機會不是太大,但是對它抱有期待。就如無限滾動的例子,如果不使用IntersectionObserver的話,就得監聽瀏覽器滾動事件,獲取列表高度、窗口高度和滾動高度來計算是否滾動到底部,必要情況下還需要加上防抖動來優化用戶體驗,所以IntersectionObserver還是省去很多步驟的,看好!

轉眼就到了2019年了,要堅持分享輸出!

sal源碼解析輕量級的滾動動畫庫

相關文章

Redux梳理分析【一:reducer和dispatch】

Babel插件起手式

Koa2源碼學習(上)

springboot/cloud(二十)相同服務,發佈不同版本,支撐並行的業務需求