v3-virtual-scroll
Version:
基于 Vue 3 + TypeScript + Vite
172 lines (171 loc) • 5.73 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => {
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
import { defineComponent, ref, computed, onUnmounted, onActivated, nextTick, openBlock, createElementBlock, unref, normalizeStyle, createElementVNode, Fragment, renderList, renderSlot } from "vue";
function debound(fn, t) {
let timer = null;
return (...args) => {
if (timer) {
return;
}
timer = setTimeout(() => {
fn(...args);
timer = null;
}, t);
};
}
class Loader {
constructor(options = {}) {
__publicField(this, "isLoading", false);
__publicField(this, "loadCbs", []);
__publicField(this, "doneCbs", []);
__publicField(this, "load", () => {
this.isLoading = true;
this.doLoad();
});
__publicField(this, "done", () => {
this.isLoading = false;
this.doDone();
});
__publicField(this, "onLoad", (cb) => {
this.loadCbs.push(cb);
});
__publicField(this, "onDone", (cb) => {
this.doneCbs.push(cb);
});
__publicField(this, "doLoad", () => {
this.loadCbs.forEach((cb) => {
cb();
});
});
__publicField(this, "doDone", () => {
this.doneCbs.forEach((cb) => {
cb();
});
});
this.init(options);
}
init(options) {
const { isLoading, loadCbs, doneCbs } = options;
if (isLoading) {
this.isLoading = isLoading;
}
if (loadCbs) {
this.loadCbs.push(...loadCbs);
}
if (doneCbs) {
this.loadCbs.push(...doneCbs);
}
}
}
var Index_vue_vue_type_style_index_0_scoped_true_lang = "";
var _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
target[key] = val;
}
return target;
};
const _sfc_main = /* @__PURE__ */ defineComponent({
props: {
list: { default: () => [] },
key: { default: "id" },
height: { default: window.screen.height },
itemHeight: { default: 0 },
startOffset: { default: 0 },
endOffset: { default: 0 }
},
emits: ["onTouchEnd", "onTouchTop"],
setup(__props, { emit: emits }) {
const props = __props;
let scrollTop = ref(0);
let scrollContainer = ref(null);
const count = computed(() => ~~(props.height / props.itemHeight) + 2);
const lastIndex = computed(() => props.list.length - 1);
const _startIndex = computed(() => ~~(scrollTop.value / props.itemHeight));
const _endIndex = computed(() => Math.min(_startIndex.value + count.value, lastIndex.value));
const startOffset = computed(() => props.startOffset || count.value);
const endOffset = computed(() => props.endOffset || count.value);
const startIndex = computed(() => {
let start = _startIndex.value - startOffset.value;
return start < 0 ? 0 : start;
});
const endIndex = computed(() => {
let end = _endIndex.value + endOffset.value;
return end > lastIndex.value ? lastIndex.value : end;
});
const renderList$1 = computed(() => props.list.slice(startIndex.value, endIndex.value));
const containerStyle = computed(() => ({ height: props.height + "px" }));
const wrapperStyle = computed(() => {
return {
paddingTop: startIndex.value * props.itemHeight + "px",
paddingBottom: (lastIndex.value - endIndex.value) * props.itemHeight + "px"
};
});
const topLoader = new Loader();
const bottomLoader = new Loader();
let curListLength = 0;
topLoader.onLoad(() => {
curListLength = props.list.length;
});
topLoader.onDone(() => {
const { itemHeight, list } = props;
const scrollHeigt = (list.length - curListLength) * itemHeight;
setScroll(scrollHeigt);
});
const onScoll = debound((e) => {
scrollTop.value = e.target.scrollTop;
handleScroll();
}, 30);
function setScroll(value) {
const container = scrollContainer.value;
container.scrollTop = value;
}
function handleScroll() {
if (scrollTop.value === 0 && !topLoader.isLoading) {
topLoader.load();
emits("onTouchTop", topLoader.done);
return;
}
if (endIndex.value >= lastIndex.value && !bottomLoader.isLoading) {
bottomLoader.load();
emits("onTouchEnd", bottomLoader.done);
}
}
onUnmounted(() => {
document.removeEventListener("scroll", onScoll);
});
onActivated(() => {
nextTick(() => {
setScroll(scrollTop.value);
});
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: "virtual-container",
onScroll: _cache[0] || (_cache[0] = (...args) => unref(onScoll) && unref(onScoll)(...args)),
style: normalizeStyle(unref(containerStyle)),
ref_key: "scrollContainer",
ref: scrollContainer
}, [
createElementVNode("div", {
class: "virtual-container__wrapper",
style: normalizeStyle(unref(wrapperStyle))
}, [
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(renderList$1), (item) => {
return openBlock(), createElementBlock("div", {
key: item[__props.key]
}, [
renderSlot(_ctx.$slots, "default", { item }, void 0, true)
]);
}), 128))
], 4)
], 36);
};
}
});
var V3VirtualScroll = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d5797efa"]]);
export { V3VirtualScroll, V3VirtualScroll as default };