UNPKG

vue-hooks-plus

Version:
107 lines (106 loc) 3.53 kB
import { ref, computed, watch, reactive } from "vue"; import useSize from "../useSize"; import { getTargetElement } from "../utils/domTarget"; const useVirtualList = (list, options) => { const containerTarget = ref(); const { wrapperTarget, itemHeight, overscan = 5 } = options; const size = useSize(containerTarget); const targetList = ref([]); const scrollTriggerByScrollToFunc = ref(false); const getVisibleCount = (containerHeight, fromIndex) => { if (typeof itemHeight === "number") { return Math.ceil(containerHeight / itemHeight); } let sum = 0; let endIndex = 0; for (let i = fromIndex; i < list.value.length; i++) { const height = itemHeight(i, list.value[i]); sum += height; endIndex = i; if (sum >= containerHeight) { break; } } return endIndex - fromIndex; }; const getOffset = (scrollTop) => { if (typeof itemHeight === "number") { return Math.floor(scrollTop / itemHeight) + 1; } let sum = 0; let offset = 0; for (let i = 0; i < list.value.length; i++) { const height = itemHeight(i, list.value[i]); sum += height; if (sum >= scrollTop) { offset = i; break; } } return offset + 1; }; const getDistanceTop = (index) => { var _a, _b; if (typeof itemHeight === "number") { const height2 = index * itemHeight; return height2; } const height = (_b = (_a = list.value) == null ? void 0 : _a.slice(0, index)) == null ? void 0 : _b.reduce((sum, _, i) => sum + (itemHeight == null ? void 0 : itemHeight(i, list == null ? void 0 : list.value[index])), 0); return height; }; const totalHeight = computed(() => { if (typeof itemHeight === "number") { return list.value.length * itemHeight; } return list.value.reduce( (sum, _, index) => sum + (itemHeight == null ? void 0 : itemHeight(index, list == null ? void 0 : list.value[index])), 0 ); }); const calculateRange = () => { const container2 = getTargetElement(containerTarget); const wrapper = getTargetElement(wrapperTarget); if (container2 && wrapper) { const { scrollTop, clientHeight } = container2; const offset = getOffset(scrollTop); const visibleCount = getVisibleCount(clientHeight, offset); const start = Math.max(0, offset - overscan); const end = Math.min(list.value.length, offset + visibleCount + overscan); const offsetTop = getDistanceTop(start); wrapper.style.height = totalHeight.value - offsetTop + "px"; wrapper.style.marginTop = offsetTop + "px"; targetList.value = list.value.slice(start, end).map((ele, index) => ({ data: ele, index: index + start })); } }; watch([size == null ? void 0 : size.width, size == null ? void 0 : size.height, list], () => { calculateRange(); }); const scrollTo = (index) => { const container2 = getTargetElement(containerTarget); if (container2) { scrollTriggerByScrollToFunc.value = true; container2.scrollTop = getDistanceTop(index); calculateRange(); } }; const container = reactive({ ref: (ele) => { containerTarget.value = ele; }, onScroll: (e) => { if (scrollTriggerByScrollToFunc.value) { scrollTriggerByScrollToFunc.value = false; return; } e.preventDefault(); calculateRange(); } }); return [targetList, container, scrollTo]; }; export { useVirtualList as default };