UNPKG

saturn-ui

Version:

🪐 一款轻量级、模块化的Web可视化UI组件库(含大屏、GIS、图表、视频、后台等模块) 👍

177 lines (167 loc) 6.43 kB
import { ref, computed, nextTick, watch, defineComponent, openBlock, createElementBlock, Fragment, createCommentVNode, createElementVNode, normalizeStyle, renderList, renderSlot } from 'vue'; const Throttle = (fn, time = 1000) => { let timer = null; return (e) => { !timer && (timer = setTimeout(() => { fn(e); timer = null; }, time)); }; }; const useHandler = (props, emit) => { const scrollBarHeight = ref(0); const scrollTranslateY = ref(0); const listData = ref([]); const startIndex = ref(0); const endIndex = ref(0); const nodes = []; const prevScreen = computed(() => props.remain * props.screen[0]); const nextScreen = computed(() => props.remain * props.screen[1]); const prevCount = computed(() => Math.min(startIndex.value, prevScreen.value)); const nextCount = computed(() => Math.min(props.list.length - endIndex.value, nextScreen.value)); const renderData = computed(() => listData.value.slice(startIndex.value - prevCount.value, endIndex.value + nextCount.value)); const updateHeight = () => { nextTick(() => { if (props.itemHeight === 0) { nodes.forEach(node => { const { height } = node.getBoundingClientRect(); const index = +node.dataset.index; const oldHeight = listData.value[index].height; height && oldHeight !== height && (listData.value[index].height = height); }); } scrollBarHeight.value = listData.value.reduce((prev, item) => prev + item.height, 0); }); }; let onScroll = (e) => { const { scrollTop, clientHeight, scrollHeight } = e.target; if (props.itemHeight) { startIndex.value = ~~(scrollTop / props.itemHeight); endIndex.value = startIndex.value + props.remain; scrollTranslateY.value = (startIndex.value - prevCount.value) * props.itemHeight; } else { let prevSum = 0; const index = listData.value.findIndex(item => { prevSum += item.height; return prevSum > scrollTop; }); startIndex.value = index; endIndex.value = startIndex.value + props.remain; let curPrevSum = 0; const maxindex = startIndex.value - prevCount.value; listData.value.find((item, i) => { if (i >= maxindex) { return true; } curPrevSum += item.height; return false; }); scrollTranslateY.value = curPrevSum; updateHeight(); } nextTick(() => { scrollTop + clientHeight >= scrollHeight - props.distance && emit('on-load-more'); }); }; onScroll = Throttle(onScroll, props.interval); const setItemRef = (el, index) => { el && (nodes[index] = el); }; watch(() => props.list, value => { const height = props.itemHeight || 100; listData.value = value.map((item, virtualId) => ({ ...item, virtualId, height })); updateHeight(); }, { deep: true, immediate: true }); return { scrollBarHeight, scrollTranslateY, renderData, onScroll, setItemRef }; }; var script = defineComponent({ name: 'IceVirtualList', emits: ['on-load-more'], props: { list: { type: Array, default: () => [] }, interval: { type: Number, default: 100 }, height: { type: String, default: '100%' }, itemHeight: { type: Number, default: 0 }, distance: { type: Number, default: 50 }, screen: { type: Array, default: () => [1, 1] }, remain: { type: Number, default: 8 } }, setup(props, { emit }) { const { scrollBarHeight, scrollTranslateY, renderData, onScroll, setItemRef } = useHandler(props, emit); return { scrollBarHeight, scrollTranslateY, renderData, onScroll, setItemRef }; } }); const _hoisted_1 = ["data-index"]; function render(_ctx, _cache, $props, $setup, $data, $options) { return (openBlock(), createElementBlock(Fragment, null, [ createCommentVNode(" 虚拟列表 "), createElementVNode("div", { class: "ice-virtual-list", style: normalizeStyle(`height:${_ctx.height};`), onScrollPassive: _cache[0] || (_cache[0] = (...args) => (_ctx.onScroll && _ctx.onScroll(...args))) }, [ createCommentVNode(" 滚动高度 "), createElementVNode("div", { class: "u-scroll-bar", style: normalizeStyle(`height:${_ctx.scrollBarHeight}px;`) }, null, 4 /* STYLE */), createCommentVNode(" 列表 "), createElementVNode("ul", { class: "m-list-scroll", style: normalizeStyle(`transform:translateY(${_ctx.scrollTranslateY}px);`) }, [ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.renderData, (item) => { return (openBlock(), createElementBlock("li", { key: item.virtualId, "data-index": item.virtualId, ref_for: true, ref: $event => _ctx.setItemRef($event, item.virtualId) }, [ renderSlot(_ctx.$slots, "default", { item: item }) ], 8 /* PROPS */, _hoisted_1)) }), 128 /* KEYED_FRAGMENT */)) ], 4 /* STYLE */), renderSlot(_ctx.$slots, "more") ], 36 /* STYLE, HYDRATE_EVENTS */) ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)) } script.render = render; script.__file = "packages/IceVirtualList/index.vue"; const withInstall = (main, extra) => { main.install = (app) => { for (const comp of [main, ...Object.values(extra ?? {})]) { app.component(comp.name, comp); } }; if (extra) { for (const [key, comp] of Object.entries(extra)) { main[key] = comp; } } return main; }; const IceVirtualList = withInstall(script, { name: 'IceVirtualList' }); export { IceVirtualList, IceVirtualList as default };