虚拟列表渲染(不进行后台加载)

import { defineComponent, PropType, computed } from "vue";
import "./style.less";
import Service from "./virtual_list.service";

export default defineComponent({

  name: "VirtualList",

  props: {

    // 容器总高度
    screenHeight: {
      type: Number,
      required: true
    },

    // 容器总宽度
    screenWidth: {
      type: Number,
      required: true
    },

    // 数据 行列 总条数
    total: {
      type: Object as PropType<{ rowTotal: number; colTotal: number }>,
      required: true
    },

    // 数据源
    dataList: {
      type: Array as PropType<Array<Array<number>>>,
      required: true
    }
  },


  emits: ["update-data"],

  setup(props, { emit }) {
    const service = new Service(emit, props.screenHeight, props.screenWidth)

    // 渲染数据
    const renderData = computed(() =>
      props.dataList
        .slice(service.modal.start.sCol, Math.min(service.modal.end.eCol, props.dataList.length))
        .map(item => item.slice(service.modal.start.sRow, Math.min(service.modal.end.eRow, item.length)))
    )

    return () => (
      <div
        class={[
          "virtual-container",
          "scrollbar"
        ]}
        ref={ref => service.modal.dom = ref as Element}
        onScroll={() => service.scroll()}
      >
        {/* 撑出滚动条 */}
        <div
          style={{ height: `${props.total.rowTotal * service.modal.itemSize.col}px`, width: `${props.total.colTotal * service.modal.itemSize.row}px` }}
          class="virtual-scol"
        />

        <div
          class="virtual-list"
          style={{ transform: `translate3d(${service.modal.offset.oRow}px,${service.modal.offset.oCol}px,0)` }}
        >
          {
            renderData.value.map((item, index) => (
              <div
                class="virtual-item-row"
                style={{ height: `${service.modal.itemSize.col}px`, lineHeight: `${service.modal.itemSize.col}px` }}
                key={index}
              >
                {
                  item.map((item2, index2) => (
                    <div
                      key={index2}
                      style={{ height: `${service.modal.itemSize.col}px`, width: `${service.modal.itemSize.row}px` }}
                      class="virtual-item-col"
                    >
                      {item2}
                    </div>
                  ))
                }
              </div>
            ))
          }
        </div>
      </div>

    )
  }

})

import { reactive, computed } from "vue";

type Modal = {

  // 滚动dom
  dom: Element | null

  // 每个单元格尺寸
  itemSize: { col: number, row: number };

  // 渲染行列开始索引
  start: {
    sCol: number,
    sRow: number
  }

  // 渲染最后一条行列索引
  end: {
    eCol: number,
    eRow: number
  }

  // dom可以渲染几行几列
  pageCount: {
    pCol: number,
    pRow: number
  };

  // 偏移量
  offset: {
    oCol: number,
    oRow: number
  }

}

export default class VirtualListService {

  modal: Modal = reactive({

    dom: null,

    itemSize: {
      col: 20,
      row: 50
    },
    start: {
      sCol: 0,
      sRow: 0
    },
    end: computed(() => ({
      eCol: this.modal.start.sCol + this.modal.pageCount.pCol + 10,
      eRow: this.modal.start.sRow + this.modal.pageCount.pRow + 10,
    })),

    pageCount: {
      pCol: 0,
      pRow: 0
    },

    offset: {
      oCol: 0,
      oRow: 0
    }
  })

  constructor(public emit: Function, public screenHeight: number, public screenWidth: number) {
    this.modal.pageCount.pCol = computed(() => Math.ceil(this.screenHeight / this.modal.itemSize.col)).value;
    this.modal.pageCount.pRow = computed(() => Math.ceil(this.screenWidth / this.modal.itemSize.row)).value;
  }

  scroll() {
    if (this.modal.dom) {
      const { scrollTop, scrollLeft } = this.modal.dom;
      this.modal.start.sCol = Math.floor(scrollTop / this.modal.itemSize.col)
      this.modal.offset.oCol = scrollTop - (scrollTop % this.modal.itemSize.col)
      this.modal.start.sRow = Math.floor(scrollLeft / this.modal.itemSize.row)
      this.modal.offset.oRow = scrollLeft - (scrollLeft % this.modal.itemSize.row)

    }
  }
}
.virtual-container {

  width: 100%;
  height: 100%;
  background-color: pink;
  position: relative;
  overflow: auto;

  .virtual-scol {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    z-index: -1;
    background-color: green;
  }

  .virtual-list {
    left: 0;
    right: 0;
    top: 0;
    position: absolute;
    text-align: center;

    .virtual-item-row {
      color: #555;
      box-sizing: border-box;
      white-space: nowrap;

      .virtual-item-col {
        display: inline-block;
        border: 1px solid #ccc;
      }
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值