There are several ways to implement this lazy image loading method
- each
img
Elements of thetop
Value is compared to the height of the screen, if so, replaced by the image to load. - Direct call
window
On theIntersectionObserver
Etc.API
, currently supported by most browsers. It can be called directly in the callbackAPI
You can tell if the current picture is on the screen.
The first method involves knowledge points
- Custom attributes
- The throttle
- Compare the top value of the picture on the screen with the height of the screen
The second method involves knowledge points
I’m familiar with a few apis on Windows
IntersectionObserver
Is the constructorIntersectionObserverEntry
Is the constructorintersectionRatio
:IntersectionObserverEntry.prototype
On the properties of the
The first method is the overall idea
- Get a few images, and write the SRC and data-src values in advance. If they are returned by the interface, assign them after they are returned.
- For each picture to make a judgment, the picture in the screen top value < screen height.
- This needs to be optimized, because once some images are loaded, they don’t need to be judged, so you can re-fetch the imgList list to do the loop.
- If imgList. Length === 0, remove the listener.
<template>
<div>
<div class="container" v-for="(item, i) in imgsList" :key="i">
<img class="lazy" :src="item.default" :data-src="item.showPath" />
</div>
</div>
</template>
<script>
export default {
data() {
return {
flag: false.imgArr: [],}; },computed: {
// Write the data to death in advance
imgsList() {
let arr = [],
brr = new Array(7);
brr.fill(0);
arr = brr.map((e, i) = > {
return {
default: require(`./imgs/8.webp`),
showPath: require(`./imgs/${i + 1}.webp`),}; });returnarr; }},created() {
thisShowImgs ();// By default, images on the screen are loaded
window.addEventListener("scroll", _.throttle(this.showImgs,200)); // Load it while scrolling
},
mounted() {
// Get images that need lazy loading
this.imgArr = document.getElementsByClassName("lazy");
// Class arrays are converted to arrays
this.imgArr = [].slice.call(this.imgArr, 0);
// console.log(this.imgArr.classList);
},
methods: {
showImgs() {
this.imgArr.forEach((e) = > {
// -300 is for observation
if (e.getBoundingClientRect().top <= window.innerHeight - 300) {
// console.log(e.classList);
// classList is an array of class attributes on the img element. Use remove to remove the associated class names.
// Remove the lazy class name from the img element found.
e.classList.remove("lazy");
// Custom attribute assignment
e.src = e.dataset.src;
/* console.log(this.imgArr,e); // the first img is removed,e=> remove the class e */
this.imgArr = this.imgArr.filter((el) = >el ! = = e);if (this.imgArr.length === 0) {
// Remove scroll event after loading
window.removeEventListener("scroll".this.showImgs); }}});// this.imgArr.forEach((e)=>{
// }),}}};</script>
<style lang='stylus' scoped>
.container {
height: 300px;
img {
height: 100%; }}</style>
Copy the code
One thing to note: why can I get a collection of undeleted.lazy attributes?
this.imgArr = this.imgArr.filter((el) => el ! == e);
Take a look at this example:
<div><img src="" alt="" class="lazy"></div>
<div><img src="" alt="" class="lazy"></div>
<div><img src="" alt="" class="lazy"></div>
<div><img src="" alt="" class="lazy"></div>
<div><img src="" alt="" class="lazy"></div>
Copy the code
let imgs = document.getElementsByClassName('lazy')
imgs = [].slice.call(imgs,0) ,,...
imgs.forEach((e,i) = >{
if(i === 0){
e.classList.remove('lazy')
console.log(e)
let brr = imgs.filter((el) = > {
console.log('e',e)
console.log('el',el) el ! == e// Why can be compared with each other, because store DOM elements, not objects.})}})Copy the code
Deleted items are filtered out by comparison in the original array.
Throttling and anti-shaking
The showImgs() method can be executed once at 200ms, using throttling, in the event of triggering a scroll.
Now let’s talk about the principle of throttling and anti-shaking.
Throttling: performed once in a period of XX. If you fire an event within this time frame, you can return it.
Anti-shake: Perform this operation once in xx period. But if you fire events within this time frame, start from scratch. Let’s say a button, if it’s within 5s if you click it a little bit, then 5s after the last click, doesn’t count. You can implement some requirements, like, keep scrolling, and when you stop scrolling, do something.
<button id="btn">Click on the</button>
Copy the code
let btn = document.getElementById('btn')
// btn.addEventListener('click', throttle(fn, 2000, 20, 30))
btn.addEventListener('click', debounce(fn, 2000.20.30))
Copy the code
Throttling:
function throttle(fun, time, ... args) {
let timer = null
return function proxy() {
if (timer) {
// If timer has a value, return directly
return
}
// timer: timeoutID is a positive integer, indicating the timer number
timer = setTimeout(() = >{ fun(... args) timer =null // After executing, you can continue to click
}, time)
}
}
Copy the code
Stabilization:
function debounce(fun,time,... args){
let timer = null
return function proxy(){
if(timer){
// If there is a timer, reset the timer
clearTimeout(timer)
}
timer = setTimeout(() = >{ fun(... args) timer =null
},time)
}
}
Copy the code
The second implementation code
methods:{
lazyLoad() {
console.time() // Calculate the code runtime
// Check browser compatibility
if (
"IntersectionObserver" in window &&
"IntersectionObserverEntry" in window &&
"intersectionRatio" in window.IntersectionObserverEntry.prototype
) {
// Create an instance of IntersectionObserver and pass the anonymous callback function
let lazyImageLoader = new IntersectionObserver(function (entries, observer) {
// console.log(entries); // The location of each image
entries.forEach((e) = > {
// Check whether the image is in view
if (e.isIntersecting) {
// console.log('yes')
let lazyImage = e.target;
lazyImage.src = lazyImage.dataset.src;
// Clear the class name after loading and cancel monitoring
lazyImage.classList.remove("lazy"); lazyImageLoader.unobserve(lazyImage); }}); });console.timeEnd();/ / 0.04736328125 ms
this.imgArr.forEach((e) = > {
// console.log(lazyImageLoader)
lazyImageLoader.(e); // No return value, add monitoring to each image
/ * the understanding is IntersectionObserver. Prototype. Observe method can perform new IntersectionObserver (function () {... }) callback */}); }}},Copy the code
Make a request based on your ability to judge the project
// Obtain the DOM by id. When invoking the observe() method, pass an element, that is, a DOM element.
checkGetQues: _.throttle(function (ele, callback) {{if ("IntersectionObserver" in window &&
"IntersectionObserverEntry" in window &&
"intersectionRatio" in window.IntersectionObserverEntry.prototype) {
const lazyImageLoader = new IntersectionObserver((entries, observer) = > {
// console.log('entries', entries)
if (entries[0].isIntersecting) {
callback();
// Unmonitor
lazyImageLoader.unobserve(ele)
}
}, {
rootMargin: "100px"
})
lazyImageLoader.observe(ele)
}
}
}, 1000),
Copy the code
Note: If you create new IntersectionObserver() and invoke the observe() method, the window object will automatically detect it without having to write the scroll method yourself.
The last
In a real project, a third-party library, LoDash, could be used for throttling and shock protection.
btn.onclick = _.throttle(fn, 1000);
btn.onclick = _.debounce(fn, 1000);
Copy the code
Using throttling and stabilization in VUE:
handleScroll: _.throttle(function () {
// dosomething
}, 200),},Copy the code