There are several ways to implement this lazy image loading method

  • eachimgElements of thetopValue is compared to the height of the screen, if so, replaced by the image to load.
  • Direct callwindowOn theIntersectionObserverEtc.API, currently supported by most browsers. It can be called directly in the callbackAPIYou can tell if the current picture is on the screen.

The first method involves knowledge points

  1. Custom attributes
  2. The throttle
  3. 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

  1. IntersectionObserverIs the constructor
  2. IntersectionObserverEntryIs the constructor
  3. intersectionRatio:IntersectionObserverEntry.prototypeOn the properties of the

The first method is the overall idea

  1. 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.
  2. For each picture to make a judgment, the picture in the screen top value < screen height.
  3. 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.
  4. 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