乐趣区

关于vue.js:虚拟列表组件

<script>
export default {
name: “VirtualList”,
props: {

templateRender: Function,
list: {
  type: Array,
  default: () => [],
},
remainList: {
  type: Array,
  default: () => [],
},
itemHeight: {
  type: Number,
  default: 34,
},
wrapHeight: {
  type: Number,
  default: 262,
},
group: {
  type: Number,
  default: 1,
},

},
data() {

return {
  pointer: 0,
  translateY: 0,
};

},
watch: {

remainList: {handler(v) {
    this.translateY = v.length * this.itemHeight;
    let scrollTop = this.$refs["virtual-list-wrapper"]?.scrollTop;
    if (scrollTop !== undefined) {
      this.handleScroll({
        srcElement: {scrollTop,},
      });
    }
  },
  immediate: true,
},

},
computed: {

scrollHeight() {
  return (// (this.groupRemainList.length + this.groupList.length) * this.itemHeight
    this.groupList.length * this.itemHeight
  );
},
displayCount() {return Math.ceil(this.wrapHeight / this.itemHeight);
},
groupRemainList() {return this.convertByGroup(this.remainList);
},
groupList() {return this.convertByGroup(this.list);
},
displayList() {
  return this.groupList.slice(
    this.pointer,
    this.pointer + this.displayCount + 1
  );
},
remainHeight() {return 0; //this.remainList.length * this.itemHeight;},

},
methods: {

convertByGroup(list) {let result = [];
  let temp = [];
  list.forEach((data) => {temp.push(data);
    if (temp.length >= this.group) {result.push(temp);
      temp = [];}
  });
  if (temp.length > 0) {result.push(temp);
  }
  return result;
},
renderListItem(h, datas, index, startTranslateY, tag) {
  return h(
    "div",
    {
      class: "virtual-list-item",
      style: {
        height: this.itemHeight + "px",
        transform: `translate3d(0, ${startTranslateY + index * this.itemHeight}px, 0)`,
        display: tag === "remian" ? "none" : "block",
      },
      key: tag + index,
    },
    datas.map((data) => {return this.templateRender(h, data, tag);
    })
  );
},

handleScroll(e) {
  let scrollTop = e.srcElement.scrollTop;
  // let remainHeight = this.remainList.length * this.itemHeight;
  if (scrollTop <= this.remainHeight) {
    this.pointer = 0;
    this.translateY = this.remainHeight;
  } else {
    // this.translateY = scrollTop;
    this.pointer = Math.floor((scrollTop - this.remainHeight) / this.itemHeight
    );
    this.translateY = this.pointer * this.itemHeight + this.remainHeight;
  }
},

},
render(h) {

let children = [
  // 要保留的数据
  ...this.groupRemainList.map((datas, index) => {return this.renderListItem(h, datas, index, 0, "remian");
  }),
  // 全副数据
  ...this.displayList.map((datas, index) => {return this.renderListItem(h, datas, index, this.translateY, "list");
  }),
];
return h(
  "div",
  {
    class: "virtual-list-wrapper",
    style: {height: this.wrapHeight + "px",},
    on: {scroll: this.handleScroll,},
    ref: "virtual-list-wrapper",
  },
  [
    h(
      "div",
      {
        style: {
          height: this.scrollHeight + "px",
          position: "relative",
        },
      },
      children
    ),
  ]
);

},
};
</script>
<style>
.virtual-list-wrapper {
overflow: auto;
}
.virtual-list-item {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
</style>

退出移动版