In combination with the "Kangxi draft", let's talk about "virtual list"

Posted by skyxmen on Fri, 07 Jan 2022 02:44:29 +0100

I think I speak well. Please give me a compliment. Thank you. Hee hee

scene

Kangxi imperial concubine selection

It is said that this year is the 53rd year of Kangxi. The world is peaceful. No one in the world does not sigh that "Kangxi prosperous age". Kangxi himself is also very happy. "I have struggled for most of my life and can't have fun. I have ordered Zhang Tingyu to see me. I have something to let him do!"

  • Kangxi: Heng Chen (Heng Chen is the word of Zhang Tingyu). How about the prosperous age of Kangxi
  • Zhang Tingyu: the emperor, the emperor, long live the emperor
  • Kangxi: but I'm old, but I can't convince the old. I want to prove it to the people all over the world
  • Zhang Tingyu: the emperor is in his prime. Long live, long live
  • Kangxi: I don't care, I want to choose a concubine, I want to choose a concubine!!!
  • Zhang Tingyu: I tm... You tm're 60, you still choose? Can you carry it? eldest brother!
  • Kangxi: I don't care. Go find it for me. Find 10000 young women to enter the palace. I want to choose a concubine
  • Zhang Tingyu: choose a hair. You can't stand it. - Kangxi: I don't care. I have deer blood. A cup of deer blood has boundless magic power
  • Zhang Tingyu: No, you can't.
  • Kangxi: are you going?
  • Zhang Tingyu: No
  • Kangxi: are you going
  • Zhang Tingyu: I won't go
  • Kangxi: do you still want to enjoy the temple?
  • Zhang Tingyu: long live the emperor. I must be your saint

    A month later, 10000 young women entered the palace. But the problem comes again. With so many women, it is impossible for Kangxi to choose at one time. It must have cost him his eyes.

Zhang Tingyu had an idea: you can let the women enter the hall in batches for the emperor to choose. This can be done specifically:

  • There are two side halls outside the imperial concubine selection hall
  • The palace ladies entered the hall in batches for the emperor to see
  • The palace maids who have been seen enter the left side hall to wait for the result of the imperial concubine selection, and the palace maids who have not been lined up wait in the right side hall

    This not only improves the draft efficiency, but also makes the emperor more relaxed. The benefits are:

  • The emperor doesn't need to see 10000 palace maids at one time. Don't be so tired
  • If the emperor is tired after half of the election, he can rest and choose again the next day. Anyway, what batch has been selected? These have been recorded
  • The emperor can also go back and check if he remembers a palace maid one day

Multi data rendering

Now to solve multi data rendering, I believe you may think of paging, bottom loading, lazy loading, etc., but in fact, virtual list is also an important solution for multi data high-performance loading.

Concept of virtual list

Virtual scrolling is to monitor user sliding or scrolling events according to the list volume of the container's visual area, dynamically intercept part of the long list data and render it to the page, and dynamically fill the contents of the container's up and down scrolling area with blank stations to simulate and realize the original scrolling effect

  • Browser rendering = = = Kangxi draft: rendering 10000 at a time will definitely put great pressure on the browser and cause poor user experience
  • Visual area of container = = = draft Hall: 10000 people queue up to render, such as 10 at a time
  • Upper and lower areas = = = left and right side halls: if you can't render, you can stay in the blank area

realization

Basic implementation

  • Height of visual area
  • The height of the list item
  • Number of list items that can be displayed in the visual area = ~ ~ (height of visual area / height of list items) + 2
  • Start indexing
  • End index
  • Preload (prevent scrolling too fast, causing temporary white screen)
  • According to the start index and end index, the intercepted data is displayed in the visual area
  • Rolling throttle
  • The upper and lower blank areas are implemented by padding
  • Slide to the bottom, request data again and splice
<template>
  <div class="v-scroll" @scroll.passive="doScroll" ref="scrollBox">
    <div :style="blankStyle" style="height: 100%">
      <div v-for="item in tempSanxins" :key="item.id" class="scroll-item">
        <span>{{ item.msg }}</span>
        <img :src="item.src" />
      </div>
    </div>
  </div>
</template>


<script>
import { throttle } from "../../utils/tools";
export default {
  data() {
    return {
      allSanxins: [], // All data
      itemHiehgt: 150, // The width of each item in the list
      boxHeight: 0, // Height of visual area
      startIndex: 0, // Element start index
    };
  },
  created() {
    // Analog request data
    this.getAllSanxin(30);
  },
  mounted() {
    // Gets the height of the visible area when mounted
    this.getScrollBoxHeight();
    // Monitor screen changes and rotations to regain the height of the visual area
    window.onresize = this.getScrollBoxHeight;
    window.onorientationchange = this.getScrollBoxHeight;
  },
  methods: {
    getAllSanxin(count) {
      // Analog acquisition data
      const length = this.allSanxins.length;
      for (let i = 0; i < count; i++) {
        this.allSanxins.push({
          id: `sanxin${length + i}`,
          msg: `I am three hearts ${length + i}number`,
          // Just choose any picture here
          src: require("../../src/asset/images/sanxin.jpg").default,
        });
      }
    },
    // Use throttling to improve performance
    doScroll: throttle(function () {
      // Listen for scrolling events in the visual area
      // Formula: ~ ~ (scrolling distance / list item), you can calculate how many list items have been scrolled, and you can know the current startIndex
      // For example, if I scroll over 160px, the index is 1, because the first list item has been rolled up, and the index of the first item in the visual area is 1
      const index = ~~(this.$refs.scrollBox.scrollTop / this.itemHiehgt);
      if (index === this.startIndex) return;
      this.startIndex = index;
      if (this.startIndex + this.itemNum > this.allSanxins.length - 1) {
        this.getAllSanxin(30);
      }
    }, 200),
    getScrollBoxHeight() {
      // Gets the height of the visible area
      this.boxHeight = this.$refs.scrollBox.clientHeight;
    },
  },
  computed: {
    itemNum() {
      // How many list items can the visual area display? Calculation formula: ~ ~ (visualization area height / list item height) + 2
      // ~~Is a downward rounding operator, equivalent to math Floor(), why + 2, because the top and bottom elements may only show part
      return ~~(this.boxHeight / this.itemHiehgt) + 2;
    },
    endIndex() {
      // Calculation formula of endIndex: (start index + how many list items can be displayed in the visual area * 2)
      // For example, the visual area can display 8 list items. If startIndex is 0, endIndex is 0 + 8 * 2 = 16, if startIndex is 1, endIndex is 1 + 8 * 2 = 17, and so on
      // Why multiply it by 2? In this way, one page of data can be preloaded to prevent too fast scrolling and temporary white screen
      let index = this.startIndex + this.itemNum * 2;
      if (!this.allSanxins[index]) {
         // In the end, for example, if startIndex is 99995, endIndex should be 99995 + 8 * 2 = 10011
        // However, the total number of list data is only 10000. At this time, you need to make endIndex = (list data length - 1)
        index = this.allSanxins.length - 1;
      }
      return index;
    },
    tempSanxins() {
      //   The intercepted data displayed in the visual area uses the slice method of the array, which can be intercepted without changing the original array
      let startIndex = 0;
      if (this.startIndex <= this.itemNum) {
        startIndex = 0;
      } else {
        startIndex = this.startIndex + this.itemNum;
      }
      return this.allSanxins.slice(startIndex, this.endIndex + 1);
    },
    blankStyle() {
      // Use padding in the upper and lower spaces to act as
      let startIndex = 0;
      if (this.startIndex <= this.itemNum) {
        startIndex = 0;
      } else {
        startIndex = this.startIndex - this.itemNum;
      }
      return {
        // Height calculation formula in the upper blank: (start index * list item height)
        // For example, if you roll over three list items, the height of the upper blank area is 3 * 150 = 450, so you can pretend to scroll 10000 data
        paddingTop: startIndex * this.itemHiehgt + "px",
         // The height calculation formula in the space below: (number of total data - end index - 1) * list item height
        // For example, if the end index is 100, the blank height below is: (10000 - 100 - 1) * 150 = 1484850
        paddingBottom:
          (this.allSanxins.length - this.endIndex - 1) * this.itemHiehgt + "px",
          // Don't forget to add px
      };
    },
  },
};
</script>

<style lang="scss" scoped>
.v-scroll {
  height: 100%;
  /* padding-bottom: 500px; */
  overflow: auto;

  .scroll-item {
    height: 148px;
    /* width: 100%; */
    border: 1px solid black;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 20px;

    img {
      height: 100%;
    }
  }
}
</style>

Conclusion!

I'm Lin Sanxin, an enthusiastic front-end rookie programmer. If you are self-motivated, like the front end and want to learn from the front end, we can make friends and fish together. Ha ha, fish school, add me, please note [Si no]

Topics: Front-end Interview Optimize