preface

Long list problem in the project has encountered many times, there are also a lot of excellent articles in the community, this time finally in b station big guy’s video entry, the following is a learning summary, beginners recommend to see this video, ensure that you can understand!!

To prepare

1. Use Node to build the service, simulate the effect of the front and back end interface request (back-end code directly use the big guy)

2. Start with a few simple styles for the front end

Analysis of the

Simple answer analysis a ha

For example, is, usually we use equipment data browse news, slide for us to see things in the current equipment visual area for us is the “effective”, and other places we can’t see them anyway, we are not concerned about whether visual area outside of the data load out, as long as the sliding to the visible area, Just show us what’s in the viewable area.

The text is too boring. Let’s try the last picture.

Due to render a page need to parse the data to generate a dom tree, cssom then dom tree and the tree cssom synthesis render tree tree will end, the final render generated but interface (this process is not understood recommend watching this one), if the data quantity is big, the process can be slow, the performance is also low, assuming that no matter how many of the data, We were capricious and just loaded a few pieces of data in the viewable area and the performance was optimized. To achieve the above said “capricious”, that nature requires us to deal with the line.

Do you want a bag?

The core problem here is to find the data that should be rendered on the screen in the returned data.

Wide above code to try a ha

Initialization – first simulate the data (see code – appendix 1)

There is nothing to say here, just request the data from the background and render it to the page.

Step 2 – Several important variables need to be defined

  • OneItemHeight: Height of each item.
  • ContainerHeight: Gets the height of the roll.
  • MaxContainerSize: indicates the maximum number of data items that can be displayed on the screen.
  • containerHeight / oneItemHeight = maxContainerSize
  • During continuous scrolling, you need to know where the data displayed at the top of the scroll container is in all the data
  • Similarly, you need to know where the data displayed at the bottom of the scroll container is in all the data

Calculate maxContainerSize (note the boundary case, the top and bottom are exposed a little bit)

  getContainSize() {
      debugger
      // step 1: Calculate the height of the container
      let height = this.$refs.scrollContainer.offsetHeight;
      //3. Step 2: Height of the whole outer box/height of each = maximum number of bars that can be loaded on each screen
      let num = height / this.oneItemHeight;
      //4. Step 3: Note that if a little bit of the top is shown and a little bit of the bottom is shown, the current amount is the integer +2
      let maxNum = Math.floor(num) + 2;

      this.maxContainerSize = maxNum;
      console.log(this.maxContainerSize)
    }
Copy the code

Calculate startIndex

StartIndex starts at 0, and the value of startIndex changes when scrolling. The pseudo-code is computed as follows

StartIndex = Number of data items to be rolled out -1; Number of data items to be rolled out = Height of data items to be rolled out/height of each data itemCopy the code
  // Calculate the data to roll out
    setStartIndex() {
      let scrollTop = this.$refs.scrollContainer.scrollTop;// Current roll out height
      let currentIndex = Math.floor(
        this.$refs.scrollContainer.scrollTop / this.oneItemHeight // The number of pieces of data to roll out
      );
      if (currentIndex == this.startIndex) {
        return;
      }
      this.startIndex = currentIndex;
      console.log(currentIndex, "= = = = = = =");
    },
    // Count startIndex while listening to the scroll method
    handleScroll() {
      this.setStartIndex();
    }
Copy the code

From startIndex we can calculate endIndex using the calculated property

  • EndIndex: startIndex + The maximum number of entries in the container (however, the value of endIndex is the data length -1 if the display is the last screen and the last screen contains less than the entire screen)
 endIndex() {
      let endIndex = this.startIndex + this.maxContainerSize; //9 Step 9: Note that the bottom of the container may be empty, so we need to make a judgment
     if (endIndex > this.listData.length) {// Consider special cases: the last screen of data does not cover the entire screen
        endIndex = this.allDataList.length - 1; // At this point, the scroll has reached the bottom
      }
      return endIndex;
    },
Copy the code
  • Check out the GIF below to get a feel for it

  • Let’s see: To illustrate the change in startIndex and endIndex, I’m using watch to listen on a computed cache

StartIndex and endIndex are calculated, and then go to the request of the data inside the data can be intercepted

Now that only 8 pieces of data are displayed on the page, we can also see that the page can always display the maximum number of pieces of data on the page, and only one piece of data can be rolled out

     //10 Step 10: Define an arraylist element to display from the retrieved array
    showDataList() {
      return this.listData.slice(this.startIndex,this.endIndex)
    },
Copy the code

To solve the above problem, we need to spread out the height of the roll

Add a box to the scrolllist, and use the padding of the box to expand the scrollTop of the outermost box

Let’s try out the padding

The following steps are required:

  • Each scroll to the bottom of the need to request a data, the request out of the old and new data added
  • Computes the padding-spread scrollTop
 // Count startIndex while listening to the scroll method
    async handleScroll() {
      this.setStartIndex();
      StartIndex + contentSize > data.length-1
      console.log(
        this.startIndex,
        this.maxContainerSize,
        this.showDataList.length
      );
      if (
        this.startIndex + this.maxContainerSize >=
        this.listData.length - 1
      ) {
        console.log("Rolled to the bottom.");
        // Append data
        let request = await this.getListData(20);

        this.listData = [...request, ...this.listData]; // rerequest data, new and old data append
        console.log(this.listData); }}Copy the code

The padding to calculate

 //11 Step 11: You need to define a height for the upper blank
    topBlankFill() {
      return this.startIndex * this.oneItemHeight;
    },
    //12 Step 12: You need to define the height of the whitespace
    bottomBlankFill() {
      return (this.listData.length - this.endIndex) * this.oneItemHeight;
    }
Copy the code

See what happens when you keep pulling data down, but the DOM on the right is always only eight data pieces

Here the core code of virtual scrolling has been completed, in fact, it is not too difficult to look like, haha (this step code is attached below -3)

To optimize the

Finished, in fact, has not finished, there are a lot of more important places not optimized

Optimization 1: optimizing frequent rolling events triggered by throttling and anti-shaking

Use the principle of anti-shake: only trigger the scroll function logic every 500s

 // Count startIndex while listening to the scroll method
    handleScroll() {
      this.debounce(this.scrollFun, 500);
    },
    debounce(fn, delay) {
      clearTimeout(this.isScrollStatus);
      this.isScrollStatus = setTimeout(function() {
        fn();
      }, delay);
    },
    async scrollFun() {
      console.log("Rolling.");
      this.setStartIndex();
      if (this.startIndex + this.maxContainerSize >= this.listData.length - 1) {
        // Append data
        let request = await this.getListData(20);
        this.listData = [...request, ...this.listData]; }}Copy the code

Optimization 2 uses requestAnimationFrame to optimize the animation

MDN is explained as follows:

You want to execute an animation and ask the browser to call the specified callback function to update the animation before the next redraw. This method takes as an argument a callback function that performs the requestAnimationFrame comparison before the browser's next redrawsetTimeoutEach render of his animation was consistent, and he had to wait until this render was complete before rendering the next animation, whilesetTimeoutAfter 1000ms, regardless of whether the last animation has been rendered or not, it will be re-renderedCopy the code

RequestAnimationFrame (requestAnimationFrame) is used to render each animation in a consistent manner, and it must wait until the render is complete before rendering the next animation. SetTimeout will re-render after 1000ms regardless of whether the previous animation has been rendered.

requestF() {
      let requestAnimationFrame =
        window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame;
      // Browser anti-vibration optimization: according to the browser FPS using recursive method, queue call requestAnimationFrame method to achieve optimization
      let fps = 30;
      let interval = 1000 / fps;
      let then = Date.now();
      requestAnimationFrame(() = > {
        let now = Date.now();
        let delta = now - then;
        then = now;
        this.scrollFun();
        if (delta >= interval) {
          requestAnimationFrame(arguments.callee); }}); },Copy the code

Optimization 3: Need to add a screen buffer at the top and bottom

Simulation are not: imagine a ha, if the second request network is slow, but we have to scroll to the bottom, but the data haven’t request, this time we may see a blank area below, so we need to set aside a separately on top and the bottom screen, to recount endIndex to solve this problem

 endIndex() {
      let endIndex = this.startIndex + this.containSize * 2; //9 Step 9: Reserve a buffer for the bottom
      if (endIndex > this.allDataList.length) {
        endIndex = this.allDataList.length - 1;
      }
      return endIndex;
    }
    
Copy the code

ShowList: Reserved at the top and bottom

 //10 Step 10: Define an arraylist element to display from the retrieved array
    showDataList() {
      let startIndex = 0;
      let endIndex = 0;
      if (this.startIndex <= this.containSize) {
        // It has not been rolled out yet
        startIndex = 0;
      } else {
        startIndex = this.startIndex - this.containSize * 2; // Need to subtract roll two screen data
      }
      return this.listData.slice(startIndex, this.endIndex);
    },
Copy the code

TopPadding: Also recalculated, subtracting the reserved screen

//11 Step 11: You need to define a height for the upper blank
    topBlankFill() {
      //20. Step 20: The upper and lower padding is also required
      // Step 19: Set the front and rear buffers
      let paddingTop = 0;
      if (this.startIndex > this.maxContainerSize) {
        // Have already rolled out a screen
        // Fill the space only when the current scroll position is larger than the screen capacity
        paddingTop =
          (this.startIndex - this.maxContainerSize) * this.oneItemHeight;
        console.log(this.startIndex, this.maxContainerSize,paddingTop);
      }
      return paddingTop;
    },
Copy the code

4: There are still some details to deal with, I recommend to watch the video, very detailed

code

Attachment -1: initialization

<template>
  <div class="news-box">
    <! Step 7: Listen for scroll events -->
    <div class="scroll-container" ref="scrollContainer">
      <div v-for="(item, index) in listData" :key="index">
        <router-link class="one-new" :to=" '/article/' + item.title + '/'+ item.reads + '/'+ item.from + '/'+ item.date + '/'+ item.image ">
          <! -- Headlines, comments, sources -->
          <div class="new-left">
            <h3>{{ item.title }}</h3>
            <div>
              <p>
                <img src=".. /assets/icons/msg.png" alt="Evaluation" />
                <span>{{ item.reads }}</span>
                <span>{{ item.from }}</span>
              </p>
              <h4>{{ item.date }}</h4>
            </div>
          </div>
          <! -- Picture on the right side of news -->
          <div class="new-right">
            <! -- <img :src="imgsList[oneItem.image]" alt="PIC" /> -->
          </div>
        </router-link>
      </div>
    </div>
  </div>
</template>

<script>
// Introduce the prepared news image related information
import imgsList from ".. /components/newsImgs.js";
import { setTimeout.clearTimeout } from "timers";
export default {
  data() {
    return {
      listData: []}; },created() {
    let num = this.getListData(20);
    console.log(num, "= = = = = = = = =");
  },
  methods: {
    getListData(num) {
      return this.$axios
        .get("http://localhost:4000/data? num=" + num)
        .then(res= > {
          this.listData = res.data.list;
        })
        .catch(() = > {
          this.msg = "Request failed please try again later...";
          return false; }); }}};</script>

<style lang="scss" scoped>
.news-box {
  width: 100%;
  max-width: 800px;
  height: 100%;
  .scroll-container {
    width: 100%;
    height: 100%;
    overflow-y: auto;
    .one-new {
      text-decoration: none;
      display: block;
      display: flex;
      flex-direction: row;
      flex-wrap: nowrap;
      justify-content: space-between;
      border-bottom: 1px solid #ddd;
      padding: 14px 10px 5px;
      .new-left {
        height: 80px;
        position: relative;
        h3 {
          padding: 0;
          margin: 0;
          font-size: 16px;
          text-align: justify;
          color: # 555;
        }
        div {
          position: absolute;
          width: 100%;
          bottom: 10px;
          display: flex;
          flex-direction: row;
          flex-wrap: nowrap;
          justify-content: space-between;
          align-items: center;
          p {
            display: flex;
            flex-direction: row;
            flex-wrap: nowrap;
            justify-content: space-between;
            align-items: center;
            img {
              height: 16px;
            }
            span {
              font-size: 12px;
              color: # 555;
              margin-left: 3px;
              margin-right: 3px; }}h4 {
            font-size: 12px;
            color: # 888; }}}.new-right {
        margin-left: 10px;
        img {
          height: 68px; }}}.msg h2 {
      font-size: 18px;
      text-align: center;
      color: # 666;
      padding-top: 58px; }}}</style>

Copy the code

Attachment -2 calculates startIndex during scrolling

<template>
  <div class="news-box">
    <! Step 7: Listen for scroll events -->
    <div class="scroll-container" ref="scrollContainer" @scroll.passive="handleScroll">
      <div v-for="(item, index) in showDataList" :key="index">
        <router-link class="one-new" :to=" '/article/' + item.title + '/'+ item.reads + '/'+ item.from + '/'+ item.date + '/'+ item.image ">
          <! -- Headlines, comments, sources -->
          <div class="new-left">
            <h3>{{item.title}}</h3>
            <div>
              <p>
                <img src=".. /assets/icons/msg.png" alt="Evaluation" />
                <span>{{ item.reads }}</span>
                <span>{{ item.from }}</span>
              </p>
              <h4>{{ item.date }}</h4>
            </div>
          </div>
          <! -- Picture on the right side of news -->
          <div class="new-right">
            <! -- <img :src="imgsList[oneItem.image]" alt="PIC" /> -->
          </div>
        </router-link>
      </div>
    </div>
  </div>
</template>

<script>
// Introduce the prepared news image related information
import imgsList from ".. /components/newsImgs.js";
import { setTimeout.clearTimeout } from "timers";
export default {
  data() {
    return {
      listData: [].oneItemHeight: 100.//2. Step 2: Border + padding +width for each
      containerHeight: 0.maxContainerSize: 0.// The maximum number of data items that can be displayed on the screen
      startIndex: 0
      // endIndex: 0
    };
  },
  created() {
    this.getListData(20);
  },
  mounted() {
    this.getContainSize();
  },
  computed: {
    endIndex() {
      let endIndex = this.startIndex + this.maxContainerSize; //9 Step 9: Note that the bottom of the container may be empty, so we need to make a judgment
      if (endIndex > this.listData.length) {
        // Consider special cases: the last screen of data does not cover the entire screen
        endIndex = this.listData.length - 1; // At this point, the scroll has reached the bottom
      }
      console.log(endIndex, "========endIndex=========");
      console.log(this.maxContainerSize, "========maxContainerSize=========");
      return endIndex;
    },
    //10 Step 10: Define an arraylist element to display from the retrieved array
    showDataList() {

      return this.listData.slice(this.startIndex,this.endIndex)
    },
  },
  watch: {},methods: {
    getListData(num) {
      return this.$axios
        .get("http://localhost:4000/data? num=" + num)
        .then(res= > {
          this.listData = res.data.list;
        })
        .catch(() = > {
          this.msg = "Request failed please try again later...";
          return false;
        });
    },

    getContainSize() {
      // step 1: Calculate the height of the container
      let height = this.$refs.scrollContainer.offsetHeight;
      //3. Step 3: Height of the whole outer box/height of each = maximum number of bars that can be loaded on each screen
      let num = height / this.oneItemHeight;
      //4. Step 4: Note: if a little bit is shown above and a little bit is shown below then the current amount is the integer +2
      let maxNum = Math.floor(num) + 2;

      this.maxContainerSize = maxNum;
      console.log(this.maxContainerSize);
    },
    // Calculate the data to roll out
    setStartIndex() {
      let scrollTop = this.$refs.scrollContainer.scrollTop; // Current roll out height
      let currentIndex = Math.floor(
        this.$refs.scrollContainer.scrollTop / this.oneItemHeight // The number of pieces of data to roll out
      );
      if (currentIndex == this.startIndex) {
        return;
      }
      this.startIndex = currentIndex;
      console.log(this.startIndex, "===startIndex====");
    },
    // Count startIndex while listening to the scroll method
    handleScroll() {
      this.setStartIndex(); }}};</script>

<style lang="scss" scoped>
.news-box {
  width: 100%;
  max-width: 800px;
  height: 100%;
  .scroll-container {
    width: 100%;
    height: 100%;
    overflow-y: auto;
    .one-new {
      text-decoration: none;
      display: block;
      display: flex;
      flex-direction: row;
      flex-wrap: nowrap;
      justify-content: space-between;
      border-bottom: 1px solid #ddd;
      padding: 14px 10px 5px;
      .new-left {
        height: 80px;
        position: relative;
        h3 {
          padding: 0;
          margin: 0;
          font-size: 16px;
          text-align: justify;
          color: # 555;
        }
        div {
          position: absolute;
          width: 100%;
          bottom: 10px;
          display: flex;
          flex-direction: row;
          flex-wrap: nowrap;
          justify-content: space-between;
          align-items: center;
          p {
            display: flex;
            flex-direction: row;
            flex-wrap: nowrap;
            justify-content: space-between;
            align-items: center;
            img {
              height: 16px;
            }
            span {
              font-size: 12px;
              color: # 555;
              margin-left: 3px;
              margin-right: 3px; }}h4 {
            font-size: 12px;
            color: # 888; }}}.new-right {
        margin-left: 10px;
        img {
          height: 68px; }}}.msg h2 {
      font-size: 18px;
      text-align: center;
      color: # 666;
      padding-top: 58px; }}}</style>
Copy the code

Attach -3 drop – down scroll to append display data

<template>
  <div class="news-box">
    <! Step 7: Listen for scroll events -->
    <div class="scroll-container" ref="scrollContainer" @scroll.passive="handleScroll">
      <! Step 13: We need to add a box because our top and bottom contents are temporarily supported by the padding -->
      <div :style="{ paddingTop:topBlankFill+'px', paddingBotom:bottomBlankFill+'px' }">
        <div v-for="(item, index) in showDataList" :key="index">
          <router-link class="one-new" :to=" '/article/' + item.title + '/'+ item.reads + '/'+ item.from + '/'+ item.date + '/'+ item.image ">
            <! -- Headlines, comments, sources -->
            <div class="new-left">
              <h3>{{item.title}}</h3>
              <div>
                <p>
                  <img src=".. /assets/icons/msg.png" alt="Evaluation" />
                  <span>{{ item.reads }}</span>
                  <span>{{ item.from }}</span>
                </p>
                <h4>{{ item.date }}</h4>
              </div>
            </div>
            <! -- Picture on the right side of news -->
            <div class="new-right">
              <! -- <img :src="imgsList[oneItem.image]" alt="PIC" /> -->
            </div>
          </router-link>
        </div>
      </div>

    </div>
  </div>
</template>

<script>
// Introduce the prepared news image related information
import imgsList from ".. /components/newsImgs.js";
import { setTimeout.clearTimeout } from "timers";
export default {
  data() {
    return {
      listData: [].oneItemHeight: 100.//2. Step 2: Border + padding +width for each
      containerHeight: 0.maxContainerSize: 0.// The maximum number of data items that can be displayed on the screen
      startIndex: 0
      // topBlankFill: 50,
      // bottomBlankFill: 20
      // // endIndex: 0,
    };
  },
  created() {},
  async mounted() {
    this.getContainSize();
    this.listData = await this.getListData(20);
    console.log(this.$refs.scrollContainer.scrollTop);
  },
  computed: {
    endIndex() {
      let endIndex = this.startIndex + this.maxContainerSize; //9 Step 9: Note that the bottom of the container may be empty, so we need to make a judgment
      if (endIndex > this.listData.length) {
        // Consider special cases: the last screen of data does not cover the entire screen
        endIndex = this.listData.length - 1; // At this point, the scroll has reached the bottom
      }
      console.log(endIndex, "========endIndex=========");
      console.log(this.maxContainerSize, "========maxContainerSize=========");
      return endIndex;
    },
    //10 Step 10: Define an arraylist element to display from the retrieved array
    showDataList() {
      return this.listData.slice(this.startIndex, this.endIndex);
    },

    //11 Step 11: You need to define a height for the upper blank
    topBlankFill() {
      return this.startIndex * this.oneItemHeight;
    },

    //12 Step 12: You need to define the height of the whitespace
    bottomBlankFill() {
      return (this.listData.length - this.endIndex) * this.oneItemHeight; }},watch: {},
  methods: {
    getListData(num) {
      return this.$axios
        .get("http://localhost:4000/data? num=" + num)
        .then(res= > {
          return res.data.list;
        })
        .catch(() = > {
          this.msg = "Request failed please try again later...";
          return false;
        });
    },
    getContainSize() {
      // step 1: Calculate the height of the container
      let height = this.$refs.scrollContainer.offsetHeight;
      //3. Step 3: Height of the whole outer box/height of each = maximum number of bars that can be loaded on each screen
      let num = height / this.oneItemHeight;
      //4. Step 4: Note: if a little bit is shown above and a little bit is shown below then the current amount is the integer +2
      let maxNum = Math.floor(num) + 2;

      this.maxContainerSize = maxNum;
      console.log(this.maxContainerSize);
    },
    // Calculate the data to roll out
    setStartIndex() {
      let scrollTop = this.$refs.scrollContainer.scrollTop; // Current roll out height
      console.log(this.$refs.scrollContainer.scrollTop, "scrollTop");
      let currentIndex = Math.floor(
        this.$refs.scrollContainer.scrollTop / this.oneItemHeight // The number of pieces of data to roll out
      );
      if (currentIndex == this.startIndex) {
        return;
      }
      this.startIndex = currentIndex;
      console.log(this.startIndex, "===startIndex====");
    },
    // Count startIndex while listening to the scroll method
    async handleScroll() {
      this.setStartIndex();
      StartIndex + contentSize > data.length-1
      console.log(
        this.startIndex,
        this.maxContainerSize,
        this.showDataList.length
      );
      if (
        this.startIndex + this.maxContainerSize >=
        this.listData.length - 1
      ) {
        console.log("Rolled to the bottom.");
        // Append data
        let request = await this.getListData(20);

        this.listData = [...request, ...this.listData];
        console.log(this.listData); }}}};</script>
<style lang="scss" scoped>
.news-box {
  width: 100%;
  max-width: 800px;
  height: 100%;
  .scroll-container {
    width: 100%;
    height: 100%;
    overflow-y: auto;
    .one-new {
      text-decoration: none;
      display: block;
      display: flex;
      flex-direction: row;
      flex-wrap: nowrap;
      justify-content: space-between;
      border-bottom: 1px solid #ddd;
      padding: 14px 10px 5px;
      .new-left {
        height: 80px;
        position: relative;
        h3 {
          padding: 0;
          margin: 0;
          font-size: 16px;
          text-align: justify;
          color: # 555;
        }
        div {
          position: absolute;
          width: 100%;
          bottom: 10px;
          display: flex;
          flex-direction: row;
          flex-wrap: nowrap;
          justify-content: space-between;
          align-items: center;
          p {
            display: flex;
            flex-direction: row;
            flex-wrap: nowrap;
            justify-content: space-between;
            align-items: center;
            img {
              height: 16px;
            }
            span {
              font-size: 12px;
              color: # 555;
              margin-left: 3px;
              margin-right: 3px; }}h4 {
            font-size: 12px;
            color: # 888; }}}.new-right {
        margin-left: 10px;
        img {
          height: 68px; }}}.msg h2 {
      font-size: 18px;
      text-align: center;
      color: # 666;
      padding-top: 58px; }}}</style>

Copy the code

Attached – 4

<template>
  <div class="news-box">
    <! Step 7: Listen for scroll events -->
    <div class="scroll-container" ref="scrollContainer" @scroll.passive="handleScroll">
      <! Step 13: We need to add a box because our top and bottom contents are temporarily supported by the padding -->
      <div :style="{ paddingTop:topBlankFill+'px', paddingBotom:bottomBlankFill+'px' }">
        <div v-for="(item, index) in showDataList" :key="index">
          <router-link class="one-new" :to=" '/article/' + item.title + '/'+ item.reads + '/'+ item.from + '/'+ item.date + '/'+ item.image ">
            <! -- Headlines, comments, sources -->
            <div class="new-left">
              <h3>{{item.title}}</h3>
              <div>
                <p>
                  <img src=".. /assets/icons/msg.png" alt="Evaluation" />
                  <span>{{ item.reads }}</span>
                  <span>{{ item.from }}</span>
                </p>
                <h4>{{ item.date }}</h4>
              </div>
            </div>
            <! -- Picture on the right side of news -->
            <div class="new-right">
              <! -- <img :src="imgsList[oneItem.image]" alt="PIC" /> -->
            </div>
          </router-link>
        </div>
      </div>

    </div>
  </div>
</template>

<script>
// Introduce the prepared news image related information
import imgsList from ".. /components/newsImgs.js";
import { setTimeout.clearTimeout } from "timers";
export default {
  data() {
    return {
      listData: [].oneItemHeight: 100.//2. Step 2: Border + padding +width for each
      containerHeight: 0.maxContainerSize: 0.// The maximum number of data items that can be displayed on the screen
      startIndex: 0.isScrollStatus: null.isRequestStatus: true
      // topBlankFill: 50,
      // bottomBlankFill: 20
      // // endIndex: 0,
    };
  },
  created() {},
  async mounted() {
    this.getContainSize();
    this.listData = await this.getListData(20);
    if(!!!!!this.listData && this.listData.length > 0) {
      this.listData = [...this.listData];
      this.isRequestStatus = false; }},computed: {
    endIndex() {
      let endIndex = this.startIndex + this.maxContainerSize * 2; //9 Step 9: Note that the bottom of the container may be empty, so we need to make a judgment
      if (endIndex > this.listData.length) {
        // Consider special cases: the last screen of data does not cover the entire screen
        endIndex = this.listData.length - 1; // At this point, the scroll has reached the bottom
      }
      return endIndex;
    },
    //10 Step 10: Define an arraylist element to display from the retrieved array
    showDataList() {
      let startIndex = 0;
      let endIndex = 0;
      if (this.startIndex <= this.maxContainerSize) {
        // It has not been rolled out yet
        startIndex = 0;
      } else {
        startIndex = this.startIndex - this.maxContainerSize * 2; // Need to subtract roll two screen data
      }

      return this.listData.slice(startIndex, this.endIndex);
    },

    //11 Step 11: You need to define a height for the upper blank
    topBlankFill() {
      //20. Step 20: The upper and lower padding is also required
      // Step 19: Set the front and rear buffers
      let paddingTop = 0;
      if (this.startIndex > this.maxContainerSize) {
        // Have already rolled out a screen
        // Fill the space only when the current scroll position is larger than the screen capacity
        paddingTop =
          (this.startIndex - this.maxContainerSize) * this.oneItemHeight;
        console.log(this.startIndex, this.maxContainerSize,paddingTop);
      }
      return paddingTop;
    },

    //12 Step 12: You need to define the height of the whitespace
    bottomBlankFill() {
      return (this.listData.length - this.endIndex) * this.oneItemHeight; }},watch: {},
  methods: {
    getListData(num) {
      return this.$axios
        .get("http://localhost:4000/data? num=" + num)
        .then(res= > {
          return res.data.list;
        })
        .catch(() = > {
          this.msg = "Request failed please try again later...";
          return false;
        });
    },

    getContainSize() {
      // step 1: Calculate the height of the container
      let height = this.$refs.scrollContainer.offsetHeight;
      //3. Step 3: Height of the whole outer box/height of each = maximum number of bars that can be loaded on each screen
      let num = height / this.oneItemHeight;
      //4. Step 4: Note: if a little bit is shown above and a little bit is shown below then the current amount is the integer +2
      let maxNum = Math.floor(num) + 2;

      this.maxContainerSize = maxNum;
    },
    // Calculate the data to roll out
    setStartIndex() {
      let scrollTop = this.$refs.scrollContainer.scrollTop; // Current roll out height
      let currentIndex = Math.floor(
        this.$refs.scrollContainer.scrollTop / this.oneItemHeight // The number of pieces of data to roll out
      );
      if (currentIndex == this.startIndex) {
        return;
      }
      this.startIndex = currentIndex;
    },
    // Count startIndex while listening to the scroll method
    handleScroll() {
      // this.debounce(this.scrollFun, 500);
      this.requestF();
      // this.scrollFun();
    },

    requestF() {
      let requestAnimationFrame =
        window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame;
      // Browser anti-vibration optimization: according to the browser FPS using recursive method, queue call requestAnimationFrame method to achieve optimization
      let fps = 30;
      let interval = 1000 / fps;
      let then = Date.now();
      requestAnimationFrame(() = > {
        let now = Date.now();
        let delta = now - then;
        then = now;
        this.scrollFun();
        if (delta >= interval) {
          requestAnimationFrame(arguments.callee); }}); },debounce(fn, delay) {
      clearTimeout(this.isScrollStatus);
      this.isScrollStatus = setTimeout(function() {
        fn();
      }, delay);
    },
    async scrollFun() {
      console.log("Rolling.");
      this.setStartIndex();
      if (this.startIndex + this.maxContainerSize >= this.listData.length - 1) {
        // Append data
        let request = await this.getListData(20);
        this.listData = [...request, ...this.listData]; }}}};</script>

<style lang="scss" scoped>
.news-box {
  width: 100%;
  max-width: 800px;
  height: 100%;
  .scroll-container {
    width: 100%;
    height: 100%;
    overflow-y: auto;
    .one-new {
      text-decoration: none;
      display: block;
      display: flex;
      flex-direction: row;
      flex-wrap: nowrap;
      justify-content: space-between;
      border-bottom: 1px solid #ddd;
      padding: 14px 10px 5px;
      .new-left {
        height: 80px;
        position: relative;
        h3 {
          padding: 0;
          margin: 0;
          font-size: 16px;
          text-align: justify;
          color: # 555;
        }
        div {
          position: absolute;
          width: 100%;
          bottom: 10px;
          display: flex;
          flex-direction: row;
          flex-wrap: nowrap;
          justify-content: space-between;
          align-items: center;
          p {
            display: flex;
            flex-direction: row;
            flex-wrap: nowrap;
            justify-content: space-between;
            align-items: center;
            img {
              height: 16px;
            }
            span {
              font-size: 12px;
              color: # 555;
              margin-left: 3px;
              margin-right: 3px; }}h4 {
            font-size: 12px;
            color: # 888; }}}.new-right {
        margin-left: 10px;
        img {
          height: 68px; }}}.msg h2 {
      font-size: 18px;
      text-align: center;
      color: # 666;
      padding-top: 58px; }}}</style>

Copy the code