<template>
  <div ref="list" :style="{height}" class="infinite-list-container" @scroll="scrollEvent($event)">
    <div ref="phantom" class="infinite-list-phantom"></div>
    <div ref="content" class="infinite-list">
      {{positions.length}}
      <div
        class="infinite-list-item"
        ref="items"
        :id="item.index"
        :key="item.index"
        v-for="item in visibleData"
      >
        <slot ref="slot" :item="item.item"></slot>
      </div>
    </div>
  </div>
</template>


<script>
export default {
  props: {
    //所有列表数据
    listData: {
      type: Array,
      default: () => []
    },
    //预估高度
    estimatedItemSize: {
      type: Number,
      required: true
    },
    //容器高度 100px or 50vh
    height: {
      type: String,
      default: "100%"
    }
  },
  watch: {
    listData() {
      this.initPositions()
      console.log(99999999999)
      this.$nextTick(() => {
        let height = this.positions[this.positions.length - 1].bottom;
        this.$refs.phantom.style.height = height + "px";
      })
    }
  },
  computed: {
    myListData() {
      return this.listData.map((item, index) => {
        return {
          index,
          item
        };
      });
    },
    visibleCount() {
      return Math.ceil(this.screenHeight / this.estimatedItemSize);
    },
    visibleData() {
      return this.myListData.slice(this.start, this.end);
    }
  },
  created() {
    this.initPositions();
  },
  mounted() {
    console.log(this.$el.clientHeight, this.visibleCount)
    this.screenHeight = this.$el.clientHeight;
    this.start = 0;
    this.end = this.start + this.visibleCount;
  },
  updated() {
    console.log('updated')
    this.$nextTick(function() {
      if (!this.$refs.items || !this.$refs.items.length) {
        return;
      }
      //获取真实元素大小,修改对应的尺寸缓存
      this.updateItemsSize();
      //更新列表总高度
      let height = this.positions[this.positions.length - 1].bottom;
      this.$refs.phantom.style.height = height + "px";
      //更新真实偏移量
      this.setStartOffset();
    });
  },
  data() {
    return {
      //可视区域高度
      screenHeight: 0,
      //起始索引
      start: 0,
      //结束索引
      end: 0
    };
  },
  methods: {
    initPositions() {
      this.positions = this.listData.map((d, index) => ({
        index,
        height: this.estimatedItemSize,
        top: index * this.estimatedItemSize,
        bottom: (index + 1) * this.estimatedItemSize
      }));
      console.log(this.positions)
    },
    //获取列表起始索引
    getStartIndex(scrollTop = 0) {
      //二分法查找
      return this.binarySearch(this.positions, scrollTop);
    },
    binarySearch(list, value) {
      let start = 0;
      let end = list.length - 1;
      let tempIndex = null;
      while (start <= end) {
        let midIndex = parseInt((start + end) / 2);
        let midValue = list[midIndex].bottom;
        if (midValue == value) {
          return midIndex + 1;
        } else if (midValue < value) {
          start = midIndex + 1;
        } else if (midValue > value) {
          if (tempIndex == null || tempIndex > midIndex) {
            tempIndex = midIndex;
          }
          end = end - 1;
        }
      }
      return tempIndex;
    },
    //获取列表项的当前尺寸
    updateItemsSize() {
      let nodes = this.$refs.items;
      nodes.forEach(node => {
        let rect = node.getBoundingClientRect();
        let height = rect.height;
        let index = node.id.slice(1) - 0;
        let oldHeight = this.positions[index].height;
        console.log(node.id.slice(1), height, oldHeight)
        let dValue = oldHeight - height;
        //存在差值
        if (dValue) {
          this.positions[index].bottom = this.positions[index].bottom - dValue;
          this.positions[index].height = height;

          for (let k = index + 1; k < this.positions.length; k++) {
            this.positions[k].top = this.positions[k - 1].bottom;
            this.positions[k].bottom = this.positions[k].bottom - dValue;
          }
        }
      });
    },
    //获取当前的偏移量
    setStartOffset() {
      let startOffset =
        this.start >= 1 ? this.positions[this.start - 1].bottom : 0;
      this.$refs.content.style.transform = `translate3d(0,${startOffset}px,0)`;
    },
    //滚动事件
    scrollEvent() {
      //当前滚动位置
      let scrollTop = this.$refs.list.scrollTop;
      //此时的开始索引
      this.start = this.getStartIndex(scrollTop);
      //此时的结束索引
      this.end = this.start + this.visibleCount;
      //此时的偏移量
      this.setStartOffset();
    }
  }
};
</script>


<style scoped>
.infinite-list-container {
  overflow: auto;
  position: relative;
  -webkit-overflow-scrolling: touch;
}

.infinite-list-phantom {
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  z-index: -1;
}

.infinite-list {
  left: 0;
  right: 0;
  top: 0;
  position: absolute;
}

.infinite-list-item {
  /* height:200px; */
}
</style>
                <YunShuLogList v-if="myList.length > 0" :listData="myList" height="260px" :estimatedItemSize="33" v-slot="slotProps">
              <div
                 @click="checkItem(slotProps.item, index)"
                 class="content-child yunshu-checkbox-child"
                 :class="{active:slotProps.item.check}">

              <div v-if="status == 'index'" class="content-child-index">1</div>
              <Checkbox :disabled="readonly" v-if="status == 'checkbox'" :label="slotProps.item.value"><span style="padding-left:10px">{{slotProps.item.title}}</span> </Checkbox>
              <span v-if="status !== 'checkbox'" style="padding-left:10px">{{slotProps.item.title}}</span>
              </div>
            </YunShuLogList>

打工者联盟为了抵抗996、拖欠工资、黑心老板、恶心公司,让我们组成打工者联盟。客观评价自己任职过的公司情况,为其他求职者竖起一座引路的明灯。https://book.employleague.cn/

Logo

前往低代码交流专区

更多推荐