AOS 源码解读
项目中有个需求是官网浏览时,滚动鼠标,相对应的元素会以动画的形式加载到对应的位置。看到这个需求我第一印象就是window下有个MutationObserver这个api,可以监听对应的dom元素变化,应该可以使用。本着不重复造轮子,找了npm上有这个包,可以实现。
AOS - Animate on scroll library (michalsnik.github.io)
安装
npm install aos --save
使用
在对应dom元素上增加相关data-aos相关的属性
<div data-aos="flip-left" data-aos-delay="100" data-aos-anchor=".example-selector">
使用也是非常简单,直接init就完事了。
AOS.init(options?)
全局配置
offset 出发的偏移量
duration 动画持续时间
delay 延迟触发时间
easing 动画形式
API
method
-
init 初始化
-
refresh 刷新
-
refreshHard 强制刷新
// 可以使用的动画效果
* Fade animations:
* fade
* fade-up
* fade-down
* fade-left
* fade-right
* fade-up-right
* fade-up-left
* fade-down-right
* fade-down-left
* Flip animations:
* flip-up
* flip-down
* flip-left
* flip-right
* Slide animations:
* slide-up
* slide-down
* slide-left
* slide-right
* Zoom animations:
* zoom-in
* zoom-in-up
* zoom-in-down
* zoom-in-left
* zoom-in-right
* zoom-out
* zoom-out-up
* zoom-out-down
* zoom-out-left
* zoom-out-right
// data-aos-anchor-placement
// 锚点可选触发值
* top-bottom
* top-center
* top-top
* center-bottom
* center-center
* center-top
* bottom-bottom
* bottom-center
* bottom-top
// easing 可选的值
* linear
* ease
* ease-in
* ease-out
* ease-in-out
* ease-in-back
* ease-out-back
* ease-in-out-back
* ease-in-sine
* ease-out-sine
* ease-in-out-sine
* ease-in-quad
* ease-out-quad
* ease-in-out-quad
* ease-in-cubic
* ease-out-cubic
* ease-in-out-cubic
* ease-in-quart
* ease-out-quart
* ease-in-out-quart
具体的使用可以用demo自己跑一跑
源码解读
// aos.js
const init = function init(settings) {
// 复制全局配置
options = Object.assign(options, settings);
// 内部实现即querySelectorAll 查处所有的带data-aos的元素
$aosElements = elements();
//....
//disabled 方法 即删除所有带aos的元素属性
if (isDisabled(options.disable) || browserNotSupported) {
return disable();
}
/**
* Disable mutation observing if not supported
*/
if (!options.disableMutationObserver && !observer.isSupported()) {
console.info(`
aos: MutationObserver is not supported on this browser,
code mutations observing has been disabled.
You may have to call "refreshHard()" by yourself.
`);
options.disableMutationObserver = true;
}
/**
* Set global settings on body, based on options
* so CSS can use it
*/
document.querySelector('body').setAttribute('data-aos-easing', options.easing);
document.querySelector('body').setAttribute('data-aos-duration', options.duration);
document.querySelector('body').setAttribute('data-aos-delay', options.delay);
/**
* Handle initializing
*/
if (options.startEvent === 'DOMContentLoaded' &&
['complete', 'interactive'].indexOf(document.readyState) > -1) {
// Initialize AOS if default startEvent was already fired
refresh(true);
} else if (options.startEvent === 'load') {
// If start event is 'Load' - attach listener to window
window.addEventListener(options.startEvent, function() {
refresh(true);
});
} else {
// Listen to options.startEvent and initialize AOS
document.addEventListener(options.startEvent, function() {
refresh(true);
});
}
/**
* Refresh plugin on window resize or orientation change
*/
window.addEventListener('resize', debounce(refresh, options.debounceDelay, true));
window.addEventListener('orientationchange', debounce(refresh, options.debounceDelay, true));
/**
* Handle scroll event to animate elements on scroll
*/
window.addEventListener('scroll', throttle(() => {
handleScroll($aosElements, options.once);
}, options.throttleDelay));
/**
* Observe [aos] elements
* If something is loaded by AJAX
* it'll refresh plugin automatically
*/
if (!options.disableMutationObserver) {
observer.ready('[data-aos]', refreshHard);
}
return $aosElements;
};
以上最重要的就是handleScroll这个方法了, 通过throttle函数控制出发频率
const setState = function (el, top, once) {
const attrOnce = el.node.getAttribute('data-aos-once');
// 通过计算滚动条的滚动距离 和 元素位置做对比
if (top > el.position) {
el.node.classList.add('aos-animate');
} else if (typeof attrOnce !== 'undefined') {
if (attrOnce === 'false' || (!once && attrOnce !== 'true')) {
el.node.classList.remove('aos-animate');
}
}
};
const handleScroll = function ($elements, once) {
const scrollTop = window.pageYOffset;
const windowHeight = window.innerHeight;
// 遍历, 通过控制class样式来控制动画
$elements.forEach((el, i) => {
setState(el, windowHeight + scrollTop, once);
});
};
这里有一段
if (!options.disableMutationObserver) {
observer.ready('[data-aos]', refreshHard);
}
看看这个observer.ready具体做了些什么
function ready(selector, fn) {
const doc = window.document;
const MutationObserver = getMutationObserver();
const observer = new MutationObserver(check);
callback = fn;
observer.observe(doc.documentElement, {
childList: true,
subtree: true,
removedNodes: true
});
}
可以看到内部的实现就是用new MutationObsever来监听dom元素的变化,这里是兼容如果dom元素是异步生成的,就会强制刷新dom元素上的动画属性