Github (SRC /demo/example)
Meaning of image lazy loading:
In the project, if the page is loaded at the beginning, all the real images are also loaded, no matter from the network consumption, or from the page rendering will be very performance consumption, resulting in too slow loading.
In real development, we usually render the first time, don’t render the real image, place the image portion with a default box (or put a default loading background image) and then load the image when the IMG tag is fully in the viewport.
To realize the demo
Method one:getBoundingClientRect
Traditional scheme
Using API getBoundingClientRect, the bottom of the box is less than or equal to the height of the viewport (the height of the screen).
<style>
html.body {
height:300%; }. lazyImageBox {position: absolute;left:50%;top:1500px;transform: translateX (-50%);width:400px;height:300px;background: url (". / images/default. GIF ") no-repeat center center#EEE; }. lazyImageBoximg {
width:100%;height:100%;opacity:0;transition:opacity 。3s; }</style>
<div class="lazyImageBox">
<img src="" alt="" lazy-image="images/12.jpg">
</div>
Copy the code
function throttle(func, wait = 500) {
let timer = null,
previous = 0;
return function anonymous(. params) {
let now = new Date(),
remaining = wait - (now - previous);
if (remaining <= 0) {
clearTimeout(timer);
timer = null;
previous = now;
func.call(this. params); }else if(! timer) { timer =setTimeout(() = > {
clearTimeout(timer);
timer = null;
previous = new Date(a); func.call(this. params); }, remaining); }}; }let lazyImageBox = document.querySelector('.lazyImageBox'),
lazyImage = lazyImageBox.querySelector('img');
const singleLazy = function singleLazy() {
let trueImg = lazyImage.getAttribute('lazy-image');
lazyImage.src = trueImg;
lazyImage.onload = () = > {
// The real image was loaded successfully
lazyImage.style.opacity = 1;
};
lazyImageBox.isLoad = true;
};
const lazyFunc = function lazyFunc() {
console.log('OK');
// Prevent repeated processing
if (lazyImageBox.isLoad) return;
let A = lazyImageBox.getBoundingClientRect().bottom,
B = document.documentElement.clientHeight;
if(A <= B) { singleLazy(); }};setTimeout(lazyFunc, 1000);
// window.onscroll = lazyFunc;
// The default browser will listen for the scroll event to trigger and execute the lazyFunc method in the fastest response time, which causes the trigger frequency too high
// Throttling processing
window.onscroll = throttle(lazyFunc);
Copy the code
Method 2:IntersectionObserver
DOM listener
Principle: A listener IntersectionObserver monitors the intersection information of one or more DOM elements and visual Windows. Pass in a function that, when the information changes, executes the callback function, passing in the parameter CHANGES, changes an array that contains all the listening DOM elements and the viewport intersection information. For example, the first monitored element, changes[0]. IsIntersecting indicates whether it appears in the viewport. By default, it fires once the first load completes, again when it appears in the viewport, and again when it disappears in the viewport.
The second parameter is configuration, which triggers the callback function when the viewport is present. For example, {threshold: [0,0.5]} will be triggered at the beginning of the occurrence, and triggered in the middle of the occurrence.
Without throttling, the listener is automatically optimized internally.
According to this principle, the code is as follows
let lazyImageBox = document.querySelector('.lazyImageBox'),
lazyImage = lazyImageBox.querySelector('img');
const singleLazy = function singleLazy() {
let trueImg = lazyImage.getAttribute('lazy-image');
lazyImage.src = trueImg;
lazyImage.onload = () = > {
lazyImage.style.opacity = 1;
};
};
IntersectionObserver: monitors the intersection information of one or more DOM elements and visual Windows
let ob = new IntersectionObserver(changes= > {
// Changes is an array containing the cross-information of all listening DOM elements and viewports
let item = changes[0], {isIntersecting, target/ / the target DOM
} = item;
if (isIntersecting) {
// Completely in the viewport
singleLazy();
ob.unobserve(lazyImageBox); // After loading the real image, remove the listening on the box}}, {threshold: [1]}); ob.observe(lazyImageBox);// ob.observe(lazyImageBox); // By default, when listening, it is de-heavy, do not need to repeat the firm
Copy the code
This API is not compatible with IE and is the main application mode on mobile. This is how we implement plug-ins when we package them
Package as plug-in
Some principles of encapsulation:
- Ease of use
- Call simple
- Don’t need too many dependencies (preferably zero)
- Various fault tolerant handling and perfect error prompt
- Detailed documentation and reference demos for various cases
- strong
- Powerful function, often present in the project effect, basic can support
- Adapt to more needs
- More user – defined extensions (styles/features)
- Upgrade and backward compatibility (low learning cost)
- High performance (performance optimization, lightweight (less code, small size))
- Maintainability (application of various design patterns)
First, the factory pattern is used so that the exported function can be executed either as a class or as a normal function. When executed as a normal function, it can also create an instance of its own class
const lz = LazyImage()
Copy the code
const lz = new LazyImage()
Copy the code
They work the same way
Complete package code, specific logic is in the comments:
(function () {
function LazyImage(options) {
// You can use the function to generate the instance directly or as a constructor
return new LazyImage.prototype.init(options);
}
LazyImage.prototype = {
constructor: LazyImage,
init: function init(options) {// With jQuery's factory mode, the logic in init is actually the same as the logic in the original constructor, because new init is required at the end
// init params merge config
options = options || {};
let defaults = {
context: document.attr: 'lazy-image'.threshold: 1.speed: 300.callback: Function.prototype
};
let config = Object.assign(defaults, options)
// Hang the information on the instance: in other methods, the information is obtained based on the instance
this.config = config;
this.imageBoxList = [];
// Create a listener
const oboptions = {
threshold: [config.threshold]
};
this.ob = new IntersectionObserver(changes= > {
changes.forEach(item= > {
let {
isIntersecting,
target
} = item;
if (isIntersecting) {
this.singleHandle(target);
this.ob.unobserve(target);// If the listener is already loaded, cancel the listener}}); }, oboptions);this.observeAll();// Listen to all
},
// Lazy loading of single images
singleHandle: function singleHandle(imgBox) {
let config = this.config,
imgObj = imgBox.querySelector('img'),
trueImage = imgObj.getAttribute(config.attr);
imgObj.src = trueImage;
imgObj.removeAttribute(config.attr);
imgObj.onload = () = > {
imgObj.style.transition = ` opacity ${config.speed}ms ` ;
imgObj.style.opacity = 1;
// Callback function -> Plug-in lifecycle function "callback function & publish subscribe"
config.callback.call(this, imgObj);
};
},
// Listen for the required DOM element
observeAll(refresh) {
let config = this.config,
allImages = config.context.querySelectorAll( ` img[${config.attr}] ` );
[].forEach.call(allImages, item= > {
let imageBox = item.parentNode;
// The list is already listening to the box
if (refresh && this.imageBoxList.includes(imageBox)) return;
// If not already monitored, put it in the list and then monitor it for later comparison with refresh
this.imageBoxList.push(imageBox);
this.ob.observe(imageBox);// Listen on the box
});
},
// Refresh: get the new image that needs lazy loading, and do lazy loading
refresh: function refresh() {
this.observeAll(true); }};// Since our last new is init, we need to specify lazyImage.prototype to init
LazyImage.prototype.init.prototype = LazyImage.prototype;
if (typeof window! = ="undefined") {
window.LazyImage = LazyImage;
}
if (typeof module= = ="object" && typeof module.exports === "object") {
// Support commonJS and ES6Module specifications
module.exports = LazyImage;
}
})();
Copy the code
After writing, use WebPack to package and export a compressed version of min.js for use
use
Introduction:
<script src=".. /dist/LazyImage.min.js"></script>
Copy the code
Commonjs and ES6Module specifications are also supported
Add a ‘lazy-image’ (default, modifiable) attribute to the img tag that needs to be loaded. Setting opacity to 0 for the IMG tag allows you to add a default placeholder if desired. This method LazyImage() is used to lazy-load images on a page.
Supports custom configuration:
context
:document
Specifying contextattr
:'lazy-image'
Which property does it haveimg
Need to do lazy loading (property value is real image address)threshold
:1
When it appears in the viewport before loading1
Stands for complete,0
The representative just showed upspeed
:300
The transition time when the real picture animation appearscallback
:Function.prototype
The callback function that is triggered after the image is successfully loaded
Supported methods:
refresh()
: Reloading lazy Settings for all new images
Such as:
const lz = LazyImage({
threshold:0.5.context:box
});
// Add the image dom
lz.refresh()
Copy the code
example
You can go to the repository and look for examples under the example folder
See example -gitPage
Lazy loading Settings for all images, and scroll to the bottom to rejoin images, and lazy loading Settings for new images
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Examples of lazy loading using pictures</title>
<style>
.wrapper {
width: 236px;
height: 420px;
margin: 0 auto;
background: url(./images/default.gif) center center no-repeat;
padding-bottom: 300px;
display: flex;
justify-content: center;
align-items: center;
}
.wrapper img {
opacity: 0;
}
</style>
</head>
<body>
<div class="img-area">
<div class="wrapper">
<img src="" alt="" lazy-image="./images/1.jpg">
</div>
<div class="wrapper">
<img src="" alt="" lazy-image="./images/1.jpg">
</div>
<div class="wrapper">
<img src="" alt="" lazy-image="./images/1.jpg">
</div>
</div>
<div id="bottom">
bottom
</div>
<script src=".. /dist/LazyImage.min.js"></script>
<script>
const imgArea = document.querySelector('.img-area')
// Lazy loading uses:
const lz = LazyImage({
threshold: 0.5.speed: 1000.callback: function (target) {
console.log(this, target)
}
})
// Scroll to the bottom to load more, and still lazily load the new div
const bottomOb = new IntersectionObserver((changes) = > {
const {isIntersecting, target} = changes[0]
if(isIntersecting){
console.log('Scroll to the bottom, load more')
const div = document.createElement('div')
div.classList.add('wrapper')
div.innerHTML = `<img src="" alt="" lazy-image="./images/1.jpg">`
imgArea.appendChild(div)
lz.refresh()// Perform lazy loading on the newly added DOM}}, {threshold: [0]
})
bottomOb.observe(document.querySelector('#bottom'))
</script>
</body>
</html>
Copy the code
Github (SRC /demo/example)