vue-hooks-plus
Version:
Vue hooks library
107 lines (106 loc) • 3.53 kB
JavaScript
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
};