Sal is a performance-centric, lightweight scrolling animation library
1. Introduction
Sal (Scroll extension Library) provides a high performance and lightweight solution for scroll animation. Sal uses the Intersection Observer, which provides good performance in examining elements in the viewport. It is strongly recommended to read the article on the use of IntersectionObserver API by Ruan Da-shen to understand the use of basic IntersectionObserver
This reading is divided into five parts, respectively for the introduction, use, analysis, demo, summary, five parts are not connected to each other can be separated according to the need to see.
1 introduction for the introduction, 2 use for the use of the library, 3 analysis for the source code of the analysis, 4Demo is the core of the source code of the small demo, 5 summary blowing water, learn to apply.
It is recommended to read with the source code combined with this article, so that it is easier to understand!
- IntersectionObserver API usage tutorial
- sal
- Sal resolves the Github address
2. Use
<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>
Copy the code
When the page starts scrolling, the tag with the data-Sal attribute added to it will be animated as it scrolls.
Data-sal has three options:
data-sal-duration
– Animation duration;data-sal-delay
– Animation delay time;data-sal-easing
– Animation speed curve.
The sal function takes three arguments:
threshold
– Visible scale of the target elementonce
– Perform only one animationdisable
– Disable animation
3. The parsing
The principle of the library is to observe the visibility ratio of target elements through the API of IntersectionObserver and start animation by adding or removing classes
import './sal.scss';
/** * Default option */
let options = {
rootMargin: '0% 50%'.threshold: 0.5.animateClassName: 'sal-animate'.disabledClassName: 'sal-disabled'.selector: '[data-sal]'.once: true.disabled: false};/** * private */
let elements = [];
let intersectionObserver = null;
/** * Add class startup animation to element * @param {Node} element */
const animate = element= > (
element.classList.add(options.animateClassName)
);
/** * Reverse the startup animation by removing the class * @param {Node} element */
const reverse = element= > (
element.classList.remove(options.animateClassName)
);
/** * Whether the element has been animated * @param {Node} element */
const isAnimated = element= > (
element.classList.contains(options.animateClassName)
);
/** * Remove disabledClassName for the element to enable animation */
const enableAnimations = (a)= > {
document.body.classList.remove(options.disabledClassName);
};
/** * Disable animation by adding class */
const disableAnimations = (a)= > {
document.body.classList.add(options.disabledClassName);
};
/** * Whether to disable animation * @return {Boolean} */
const isDisabled = (a)= > (
options.disabled ||
(
(typeof options.disabled === 'function') &&
options.disabled()
)
);
/ IntersectionObserver callback function * * * * @ param {Array < IntersectionObserverEntry >} entries * @ param {IntersectionObserver} observer */
const onIntersection = (entries, observer) = > {
entries.forEach((entry) = > {
if (entry.intersectionRatio >= options.threshold) {
// The visible ratio of the element is greater than the configured visible ratio
animate(entry.target);
if(options.once) { observer.unobserve(entry.target); }}else if(! options.once) {// Otherwise, start the reverse animationreverse(entry.target); }}); };/** * disable sal */
const disable = (a)= > {
disableAnimations();
intersectionObserver.disconnect();
intersectionObserver = null;
};
/** * start */
const enable = (a)= > {
enableAnimations();
/** * Sets the behavior function * intersectionObserver: observer * onIntersection: the behavior function to observe the change */
intersectionObserver = new IntersectionObserver(onIntersection, {
rootMargin: options.rootMargin,
threshold: options.threshold,
});
// Get the observation element
elements = [].filter.call(
document.querySelectorAll(options.selector), element => ! isAnimated(element, options.animateClassName), );// Sets the observer for the observed element, firing the behavior function when the change occurs
elements.forEach(element= > intersectionObserver.observe(element));
};
/** * Init * @param {Object} settings * @return {Object} public API */
const init = (settings = options) = > {
// Initialize the configuration
if(settings ! == options) { options = { ... options, ... settings, }; }// Check whether the browser has 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 `);
}
// Start and end animations
if(! isDisabled()) { enable(); }else {
disableAnimations();
}
return {
elements,
disable,
enable,
};
};
export default init;
Copy the code
4.demo
By implementing Ruan Da-shen’s two examples, we can start IntersectionObserver, which is also the principle of SAL
4.1 Lazy Load
When scrolling to a certain position, then load the corresponding image
<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>
Copy the code
4.2 Infinite Scroll
Watch for more elements to load at the bottom of the list, and load data into the list every time it reaches a set visibility ratio
<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">To load more</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>
Copy the code
5. To summarize
Sal is mainly applied to IntersectionObserver with a simple code of only over 100 lines. However, as IntersectionObserver is only an EXPERIMENTAL API (although Chrome supports it), it is not likely to be used in practical projects. But look forward to it. For example, if unlimited scrolling is not used, IntersectionObserver must monitor browser scrolling events, obtain list height, window height and scroll height to calculate whether scrolling to the bottom, and anti-jitter should be added if necessary to optimize user experience. So IntersectionObserver still saves a lot of steps, so watch it!
Fast forward to 2019, keep sharing your output!