vue如何实现虚拟滚动

本文小编为大家详细介绍“vue如何实现虚拟滚动”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue如何实现虚拟滚动”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

创新互联公司成立于2013年,先为凤阳等服务建站,凤阳等地企业,进行企业商务咨询服务。为凤阳企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。

效果:

vue如何实现虚拟滚动

滚动原理

为了理解虚拟滚动的实现原理,首先观察下面图片.手指向下滑动时,HTML页面也会随之向上滚动.
通过图片标记的距离,我们可以得出这样的结论.当屏幕视口的上边沿和id为item的div元素上边沿重合时,item元素距离长列表顶部的距离刚好等于页面的滚动距离scrollTop(这个结论会在后面计算距离时用到).

虚拟滚动为了模拟出逼真的滚动效果,首先应该满足以下两个要求.

vue如何实现虚拟滚动

  • 虚拟滚动列表的滚动条和普通列表保持一致.比如列表包含1000条数据,当浏览器使用普通渲染的方式,假设滚动条需要向下滚动5000px才能贴底.那么应用虚拟滚动技术后,滚动条也应该保证具备相同的特征,向下滚动5000px才能贴底.

  • 虚拟滚动只会渲染视口以及上下两侧的部分Dom元素.随着滚动条往下滑动,视图的内容要实时更新,保证同普通渲染长列表时,看到的内容一致.

为了满足上面的要求,html设计结构如下.

.wrapper是最外层的容器元素,position设置成absolute或relative,子元素依据它做定位.

子元素.background和.list是实现虚拟滚动的关键..background是一个空的div,但它需要设置高度,高度值等于长列表所有列表项高度相加的总和.另外还要将其设置成绝对定位,z-index的值置为-1.

.list内部负责动态渲染视口观察到的Dom元素,position设置成absolute.



.wrapper {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  top: 60px;
  overflow-y: scroll;
  .background {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: -1;
  }
  .list {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
  }
}

假如上面代码total_height等于10000px,页面运行效果图如下所示.
由于子元素.background设置了高度,父元素.wrapper就会被子元素支撑起来,同时会出现滚动条.
如果此时向下滑动,两个子元素.background和.list会同时向上滚动.当滚动距离达到了9324px,滚动条也抵达了底部.
这是因为父元素.wrapper本身高度为676px,加上滑动距离9324px,结果就刚好等于列表总高度10000px.
通过观察以上行为可知,.background虽然只是一个空的div,但是通过给它赋予列表的总高度,可以让右侧的滚动条和普通长列表渲染产生的滚动条保持外观和行为上一致.

vue如何实现虚拟滚动

滚动条的问题解决了,但随着滚动条往下滑,数据列表随之上移,列表全部移出了屏幕之后,接下来的滑动全是白屏.
为了解决白屏问题,视口必须始终展现出滑动的数据.那么.list元素要根据滑动的距离动态更新自身绝对定位的top值,这样就能确保.list不被划出屏幕之外.同时还要依据滑动的距离动态渲染当前视口应该展示的数据.

观察下面动效图,右侧Dom结构展示了滑动时的变化.

滚动条往下快速滑动后,列表的Dom元素快速渲染刷新.此时除了.list内部的Dom元素不断的更换,.list元素自身也在不断修改transform: translate3d(0, ? px ,0)样式值(修改translate3d能达到和修改top属性值相似的效果).

vue如何实现虚拟滚动

经过上面的讲解,虚拟滚动的实现逻辑已经清晰.首先js监听滚动条的滑动事件,再通过滑动距离计算出.list元素要渲染哪些子元素,其次更新.list元素位置.滚动条不断滑动时,子元素和位置也不断更新,视口上便模拟出了滚动效果.

实现

开发的Demo页面如下图所示.列表项包含了以下三种结构:

  • 小型列表项,城市首字母单独成一行,高度为50px;

  • 普通列表项,左侧英文名,右侧中文名,高度为100px;

  • 大型列表项,左侧英文名,中间中文名,右侧一张图片,高度为150px;

列表数据city_data的json结构类似如下,type为1代表采用小型列表项的样式结构渲染,2代表普通列表项,3代表大型列表项.

[{"name":"A","value":"","type":1},{"name":"Al l'Ayn","value":"艾因","type":2},{"name":"Aana","value":"阿纳","type":3} ... ]

vue如何实现虚拟滚动

city_data包含了长列表的所有数据,city_data获取后先遍历调整每一项的数据结构(代码如下).

通过以下方法处理,每一个列表项最终都包含一个top和height值.top表示该项距离长列表顶部的长度,而height值指该项的高度.
total_height即整个列表的总高度,最终要赋予上文提及的.background元素.处理完后的数据赋予this.list存储,并记录下最小列表项的高度this.min_height.

  mounted () {
     function getHeight (type) { // 根据 type 值返回高度
        switch (type) {
          case 1: return 50;
          case 2: return 100;
          case 3: return 150;
          default:
            return "";
        }
      }
      let total_height = 0;
      const list = city_data.map((data, index) => {
        const height = getHeight(data.type);
        const ob = {
          index,
          height,
          top: total_height,
          data
        }
        total_height += height;
        return ob;
      })
      this.total_height = total_height; //  列表总高度
      this.list = list;
      this.min_height = 50; // 最小高度是50
      //屏幕最大能容纳的列表项数量,containerHeight是父容器高度,按照最小高度来计算
      this.maxNum = Math.ceil(containerHeight / this.min_height);
  }

html根据type值渲染不同的样式结构(代码如下).父容器.wrapper绑定一个滑动事件onScroll,列表元素.list内部不是遍历this.list数组,因为this.list是原始数据,包含了所有的列表项.