likes
comments
collection
share

虚拟列表组件

作者站长头像
站长
· 阅读数 24

<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>