import { defineComponent, ref, computed, onMounted, watch, onUnmounted, nextTick, openBlock, createElementBlock, Fragment, createElementVNode, normalizeStyle, renderList, renderSlot, mergeProps } from 'vue'; function throttle(delay, func) { let lastCall = 0; return function (...args) { const now = Date.now(); if (now - lastCall >= delay) { lastCall = now; func.apply(this, args); } }; } function generateUUID() { const template = 'xxxxxxxxxxxxaxxxyxxxxxxxxxxxxxxx'; return template.replace(/[xy]/g, function (char) { const random = Math.random() * 16 | 0; const value = char === 'x' ? random : (random & 0x3) | 0x8; return value.toString(16); }); } const uuid = () => { return generateUUID().replaceAll('-', ''); }; function duplicateId(list) { const seen = new Set(); const duplicates = new Set(); list.forEach(item => { if (seen.has(item.id)) { duplicates.add(item); } else { seen.add(item.id); } }); if (duplicates.size > 0) { list.forEach(item => { item.id = uuid(); }); } return list; } /** * * @param {Array} list * @returns {Array} */ function listMap(list) { return list.map((data, index) => { return { id: uuid(), index, data } }) } var script$2 = defineComponent({ name: 'VerticalScroll', emits: ['offset', 'count'], inheritAttrs: false, props: { // 是否开启自动滚动 modelValue: { type: Boolean, default: true, }, list: { type: Array, required: true, }, // 是否开启鼠标悬停 hover: { type: Boolean, default: false, }, // 单步停止等待时间 (默认值 1000ms) singleWaitTime: { type: Number, default: 1000, }, // 开启鼠标悬停时支持滚轮滚动 wheel: { type: Boolean, default: false, }, // 启用单行滚动 singleLine: { type: Boolean, default: false, }, step: { type: Number, default: 0.5, }, visibleCount: { type: Number, }, ease: { type: String, default: 'cubic-bezier(0.03, 0.76, 1, 0.16)' }, direction: { type: String, default: 'up', }, delay: { type: Number, default: 0, } }, setup(props, { expose, emit }) { /** * @type {import('vue').Ref} */ const realWrapperRef = ref(null); /** * @type {import('vue').Ref} */ const realWrapperHiddenRef = ref(null); const funArgs = ref([]); const offset = ref(0); const isHover = ref(false); const direction = ref(props.direction); const isScroll = computed(() => props.hover ? !isHover.value && props.modelValue : props.modelValue); const testList = ref([]); const targetList = listMap(props.list); const visibleCount = ref(props.visibleCount === (undefined) ? 0 : props.visibleCount); let total = 0; let count = 0; let totalCount = 0; let childrenHeightList = []; let bufferTotalHeight = 0; let bufferSize = 0; let cursorIndex = -1; let realBoxHeight = 0; let reqFrame = null; let singleState = false; let singleOffset = 0; let tempOffset = 0; let listCanScroll = false; const cancle = () => { cancelAnimationFrame(reqFrame); reqFrame = null; }; const updateCursorIndex = () => { const totalIndex = cursorIndex + bufferSize; if (totalIndex >= targetList.length) { const tempIndex = totalIndex - targetList.length; const tempFuns = ['splice', [0, bufferSize], [cursorIndex], [0, tempIndex]]; funArgs.value = tempFuns; if (tempIndex > targetList.length) { cursorIndex = targetList.length; } else { cursorIndex = tempIndex; } } else { const tempFuns = ['splice', [0, bufferSize], [cursorIndex, totalIndex]]; funArgs.value = tempFuns; cursorIndex = totalIndex; } }; const initHeight = () => { const children = Array.from(realWrapperRef.value.children); const subChildren = direction.value === 'up' ? children.slice(0, bufferSize) : children.slice(visibleItems.value.length - bufferSize, visibleItems.value.length); childrenHeightList = subChildren.map((c) => c.offsetHeight); bufferTotalHeight = childrenHeightList .reduce((a, b) => a + b, 0); }; const animation = (isWheel, step) => { cancle(); if (!listCanScroll) { return; } const scrollFun = () => { const singleHeight = childrenHeightList[0]; let singleDone = false; if (singleOffset >= singleHeight) { singleDone = true; } if (singleDone) { childrenHeightList.shift(); singleOffset = 0; count += 1; } if (count === total) { totalCount += 1; emit('count', totalCount); count = 0; } if (props.singleLine && singleDone) { singleState = true; setTimeout(() => { singleState = false; if (!isWheel) { animation(false, props.step); } }, props.singleWaitTime); } else { if (!isWheel) { animation(false, props.step); } } }; if (isScroll.value && !singleState || isWheel) { reqFrame = requestAnimationFrame(() => { tempOffset += step; singleOffset += step; if (direction.value === 'up') { offset.value += step; } else { offset.value -= step; } if (tempOffset > bufferTotalHeight) { emit('offset', bufferSize, targetList); updateCursorIndex(); nextTick(() => { offset.value = direction.value === 'up' ? 0 : getFullHeight() - realBoxHeight; tempOffset = 0; initHeight(); scrollFun(); }); } else { scrollFun(); } }); } }; const onMouseenter = () => { isHover.value = true; }; const onMouseleave = () => { isHover.value = false; if (props.hover) { animation(false, props.step); } }; const throttleFunc = throttle(30, (e) => { animation(true, 10); }); const onWheel = (e) => { if (props.hover && props.wheel) { throttleFunc(e); e.preventDefault(); e.stopPropagation(); } }; /** * @type {import('vue').ComputedRef>} */ const visibleItems = computed(() => { let tempList = []; if (funArgs.value.length === 0) { tempList = targetList.slice(0, visibleCount.value); } else if (funArgs.value[0] === 'splice') { tempList = direction.value === 'up' ? visibleItems.value : visibleItems.value.reverse(); tempList.splice(...funArgs.value[1]); funArgs.value.slice(2).forEach(args => { tempList.push(...targetList.slice(...args)); }); } else { funArgs.value.slice(1).forEach(args => { tempList.push(...targetList.slice(...args)); }); } if (!(direction.value === 'up')) { tempList.reverse(); } return duplicateId(tempList); }); const initCursorIndex = () => { cursorIndex = visibleCount.value + bufferSize; if (cursorIndex >= targetList.length) { const tempIndex = cursorIndex - targetList.length; const tempFunArgs = ['slice', [0, cursorIndex], [0, tempIndex]]; if (tempIndex > targetList.length) { cursorIndex = targetList.length; } else { cursorIndex = tempIndex; } return tempFunArgs; } else { return ['slice', [0, cursorIndex]]; } }; const getFullHeight = () => { return Array.from(realWrapperRef.value.children) .map((c) => c.offsetHeight).reduce((a, b) => a + b, 0); }; const initVisibleCount = (cb) => { if (props.visibleCount === (undefined)) { testList.value = listMap([props.list[0]]); nextTick(() => { visibleCount.value = Math.ceil(realBoxHeight / realWrapperHiddenRef.value.offsetHeight) + 2; testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { cb(); }); }); } else { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { cb(); }); } }; onMounted(() => { if (!!realWrapperRef.value) { realWrapperRef.value.parentElement.addEventListener('mouseenter', onMouseenter); realWrapperRef.value.parentElement.addEventListener('mouseleave', onMouseleave); realWrapperRef.value.parentElement.addEventListener('wheel', onWheel); realBoxHeight = realWrapperRef.value.parentElement.offsetHeight; if (props.list.length > 0) { initVisibleCount(() => { const hasVerticalScroll = realWrapperHiddenRef.value.offsetHeight > realBoxHeight; if (hasVerticalScroll) { bufferSize = getBufferSize(); funArgs.value = initCursorIndex(); nextTick(() => { offset.value = direction.value === 'up' ? 0 : getFullHeight() - realBoxHeight; initHeight(); listCanScroll = true; setTimeout(() => { animation(false, props.step); }, props.delay); }); } else { init(); } testList.value = []; }); } } total = targetList.length; }); const init = () => { count = 0; totalCount = 0; listCanScroll = false; bufferSize = 0; funArgs.value = []; nextTick(() => { offset.value = direction.value === 'up' ? 0 : getFullHeight() - realBoxHeight; tempOffset = 0; singleOffset = 0; }); }; const getBufferSize = () => { let tempBufferSize = targetList.length - visibleCount.value; tempBufferSize = Math.max(1, tempBufferSize); tempBufferSize = Math.min(5, tempBufferSize); return tempBufferSize; }; const add = (index, values, cb) => { if (!!values && values.length > 0) { if (index > targetList.length) { index = targetList.length; } if (index < 0) { index = 0; } const findIndexs = []; if (values.length === 1) { visibleItems.value.forEach((v, i) => { if (v.index === index) { findIndexs.push(i); } }); } const datas = []; values.forEach((v) => { datas.push({ id: uuid(), data: v }); }); targetList.splice(index, 0, ...datas); targetList.forEach((v, i) => { v.index = i; }); const tempBufferSize = getBufferSize(); if (listCanScroll) { if (index < cursorIndex) { cursorIndex += 1; if (cursorIndex > targetList.length) { cursorIndex = 0; } } if (values.length === 1 && findIndexs.length > 0) { findIndexs.forEach((i) => { visibleItems.value[i] = datas[0]; }); } if (tempBufferSize !== bufferSize) { bufferSize = tempBufferSize; } } else { const fun = () => { const hasVerticalScroll = realWrapperHiddenRef.value.offsetHeight > realBoxHeight; if (hasVerticalScroll) { bufferSize = tempBufferSize; funArgs.value = initCursorIndex(); nextTick(() => { offset.value = direction.value === 'up' ? 0 : getFullHeight() - realBoxHeight; tempOffset = 0; singleOffset = 0; initHeight(); listCanScroll = true; animation(false, props.step); }); } else { if (props.visibleCount === (undefined)) { visibleCount.value = 0; } init(); } testList.value = []; }; if (visibleCount.value === 0 && props.visibleCount === (undefined)) { initVisibleCount(() => { fun(); }); } else { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { fun(); }); } } } if (!!cb && typeof cb === 'function') { cb(targetList); } total = targetList.length; }; const remove = (index, num = 1, cb) => { if (index >= 0 && index < targetList.length) { targetList.splice(index, num); if (listCanScroll) { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { const hasVerticalScroll = realWrapperHiddenRef.value.offsetHeight > realBoxHeight; if (hasVerticalScroll) { const tempBufferSize = getBufferSize(); if (tempBufferSize !== bufferSize) { bufferSize = tempBufferSize; } funArgs.value = initCursorIndex(); } else { init(); } testList.value = []; }); } else { init(); } } if (!!cb && typeof cb === 'function') { cb(targetList); } total = targetList.length; }; const reset = () => { nextTick(() => { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { const hasVerticalScroll = realWrapperHiddenRef.value.offsetHeight > realBoxHeight; if (hasVerticalScroll) { if (funArgs.value[0] === 'splice') { updateCursorIndex(); nextTick(() => { initHeight(); }); } else { funArgs.value = initCursorIndex(); nextTick(() => { initHeight(); }); } } else { init(); } }); }); }; const update = (index, value, cb) => { if (index >= 0 && index < targetList.length) { const findIndexs = []; visibleItems.value.forEach((v, i) => { if (v.index === index) { findIndexs.push(i); } }); const data = { id: uuid(), index: index, data: value }; targetList[index] = data; if (findIndexs.length > 0) { findIndexs.forEach((i) => { visibleItems.value[i] = data; }); } } if (!!cb && typeof cb === 'function') { cb(targetList); } }; expose({ add, remove, update, reset }); watch(() => props.modelValue, (val) => { if (val) { animation(false, props.step); } }); onUnmounted(() => { cancelAnimationFrame(reqFrame); if (realWrapperRef.value) { realWrapperRef.value.parentElement.removeEventListener('mouseenter', onMouseenter); realWrapperRef.value.parentElement.removeEventListener('mouseleave', onMouseleave); realWrapperRef.value.parentElement.removeEventListener('wheel', onWheel); } }); return { realWrapperRef, visibleItems, offset, testList, realWrapperHiddenRef } } }); const _hoisted_1 = { style: {"position":"absolute !important","top":"-999999px !important"}, ref: "realWrapperHiddenRef" }; function render$2(_ctx, _cache, $props, $setup, $data, $options) { return (openBlock(), createElementBlock(Fragment, null, [ createElementVNode("div", { class: "vue3-seamless-vertical-wrapper", ref: "realWrapperRef", style: normalizeStyle({ transition: `transform ${_ctx.ease}`, transform: `translateY(-${_ctx.offset}px)` }) }, [ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.visibleItems, (item) => { return renderSlot(_ctx.$slots, "default", { key: item.id, data: item.data, index: item.index }) }), 128 /* KEYED_FRAGMENT */)) ], 4 /* STYLE */), createElementVNode("div", _hoisted_1, [ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.testList, (item) => { return renderSlot(_ctx.$slots, "default", { key: item.id, data: item.data, index: item.index }) }), 128 /* KEYED_FRAGMENT */)) ], 512 /* NEED_PATCH */) ], 64 /* STABLE_FRAGMENT */)) } function styleInject(css, ref) { if ( ref === undefined ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z$2 = "\n.vue3-seamless-vertical-wrapper[data-v-70134dc7] {\r\n width: 100%;\n}\r\n"; styleInject(css_248z$2); script$2.render = render$2; script$2.__scopeId = "data-v-70134dc7"; script$2.__file = "packages/VerticalScroll.vue"; var script$1 = defineComponent({ name: 'HorizontalScroll', emits: ['offset', 'count'], inheritAttrs: false, props: { // 是否开启自动滚动 modelValue: { type: Boolean, default: true, }, list: { type: Array, required: true, }, // 是否开启鼠标悬停 hover: { type: Boolean, default: false, }, // 单步停止等待时间 (默认值 1000ms) singleWaitTime: { type: Number, default: 1000, }, // 开启鼠标悬停时支持滚轮滚动 wheel: { type: Boolean, default: false, }, // 启用单行滚动 singleLine: { type: Boolean, default: false, }, step: { type: Number, default: 0.5, }, visibleCount: { type: Number, }, ease: { type: String, default: 'cubic-bezier(0.03, 0.76, 1, 0.16)' }, direction: { type: String, default: 'left', }, delay: { type: Number, default: 0, } }, setup(props, { emit, expose }) { /** * @type {import('vue').Ref} */ const realWrapperRef = ref(null); /** * @type {import('vue').Ref} */ const realWrapperHiddenRef = ref(null); const funArgs = ref([]); const offset = ref(0); const isHover = ref(false); const direction = ref(props.direction); const isScroll = computed(() => props.hover ? !isHover.value && props.modelValue : props.modelValue); const testList = ref([]); const visibleCount = ref(props.visibleCount === (undefined) ? 0 : props.visibleCount); const targetList = listMap(props.list); let total = 0; let count = 0; let totalCount = 0; let childrenWidthList = []; let bufferTotalWidth = 0; let bufferSize = 0; let cursorIndex = -1; let realBoxWidth = 0; let reqFrame = null; let singleState = false; let singleOffset = 0; let tempOffset = 0; let listCanScroll = false; const cancle = () => { cancelAnimationFrame(reqFrame); reqFrame = null; }; const updateCursorIndex = () => { const totalIndex = cursorIndex + bufferSize; if (totalIndex >= targetList.length) { const tempIndex = totalIndex - targetList.length; const tempFuns = ['splice', [0, bufferSize], [cursorIndex], [0, tempIndex]]; funArgs.value = tempFuns; if (tempIndex > targetList.length) { cursorIndex = targetList.length; } else { cursorIndex = tempIndex; } } else { const tempFuns = ['splice', [0, bufferSize], [cursorIndex, totalIndex]]; funArgs.value = tempFuns; cursorIndex = totalIndex; } }; const initWidth = () => { const children = Array.from(realWrapperRef.value.children); const subChildren = direction.value === 'left' ? children.slice(0, bufferSize) : children.slice(visibleItems.value.length - bufferSize, visibleItems.value.length); childrenWidthList = subChildren.map((c) => c.offsetWidth); bufferTotalWidth = childrenWidthList .reduce((a, b) => a + b, 0); }; const animation = (isWheel, step) => { cancle(); if (!listCanScroll) { return; } const scrollFun = () => { const singleWidth = childrenWidthList[0]; let singleDone = false; if (singleOffset >= singleWidth) { singleDone = true; } if (singleDone) { childrenWidthList.shift(); singleOffset = 0; count += 1; } if (count === total) { totalCount += 1; emit('count', totalCount); count = 0; } if (props.singleLine && singleDone) { singleState = true; setTimeout(() => { singleState = false; if (!isWheel) { animation(false, props.step); } }, props.singleWaitTime); } else { if (!isWheel) { animation(false, props.step); } } }; if (isScroll.value && !singleState || isWheel) { reqFrame = requestAnimationFrame(() => { tempOffset += step; singleOffset += step; if (props) { offset.value += step; } else { offset.value -= step; } if (tempOffset > bufferTotalWidth) { emit('offset', bufferSize, targetList); updateCursorIndex(); nextTick(() => { offset.value = direction.value === 'left' ? 0 : getFullWidth() - realBoxWidth; tempOffset = 0; initWidth(); scrollFun(); }); } else { scrollFun(); } }); } }; const onMouseenter = () => { isHover.value = true; }; const onMouseleave = () => { isHover.value = false; if (props.hover) { animation(false, props.step); } }; const throttleFunc = throttle(30, (e) => { animation(true, 10); }); const onWheel = (e) => { if (props.hover && props.wheel) { throttleFunc(e); e.preventDefault(); e.stopPropagation(); } }; /** * @type {import('vue').ComputedRef>} */ const visibleItems = computed(() => { let tempList = []; if (funArgs.value.length === 0) { tempList = targetList.slice(0, visibleCount.value); } else if (funArgs.value[0] === 'splice') { tempList = direction.value === 'left' ? visibleItems.value : visibleItems.value.reverse(); tempList.splice(...funArgs.value[1]); funArgs.value.slice(2).forEach(args => { tempList.push(...targetList.slice(...args)); }); } else { funArgs.value.slice(1).forEach(args => { tempList.push(...targetList.slice(...args)); }); } if (!(direction.value === 'left')) { tempList.reverse(); } return duplicateId(tempList); }); const initCursorIndex = () => { cursorIndex = visibleCount.value + bufferSize; if (cursorIndex >= targetList.length) { const tempIndex = cursorIndex - targetList.length; const tempFunArgs = ['slice', [0, cursorIndex], [0, tempIndex]]; if (tempIndex > targetList.length) { cursorIndex = targetList.length; } else { cursorIndex = tempIndex; } return tempFunArgs; } else { return ['slice', [0, cursorIndex]]; } }; const getFullWidth = () => { return Array.from(realWrapperRef.value.children) .map((c) => c.offsetWidth).reduce((a, b) => a + b, 0); }; const initVisibleCount = (cb) => { if (props.visibleCount === (undefined)) { testList.value = listMap([props.list[0]]); nextTick(() => { visibleCount.value = Math.ceil(realBoxWidth / realWrapperHiddenRef.value.offsetWidth) + 2; testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { cb(); }); }); } else { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { cb(); }); } }; onMounted(() => { if (!!realWrapperRef.value) { realWrapperRef.value.parentElement.addEventListener('mouseenter', onMouseenter); realWrapperRef.value.parentElement.addEventListener('mouseleave', onMouseleave); realWrapperRef.value.parentElement.addEventListener('wheel', onWheel); realBoxWidth = realWrapperRef.value.parentElement.offsetWidth; if (props.list.length > 0) { initVisibleCount(() => { const hasVerticalScroll = realWrapperHiddenRef.value.offsetWidth > realBoxWidth; if (hasVerticalScroll) { bufferSize = getBufferSize(); funArgs.value = initCursorIndex(); nextTick(() => { offset.value = direction.value === 'left' ? 0 : getFullWidth() - realBoxWidth; initWidth(); listCanScroll = true; setTimeout(() => { animation(false, props.step); }, props.delay); }); } else { init(); } testList.value = []; }); } } total = targetList.length; }); const init = () => { count = 0; totalCount = 0; listCanScroll = false; bufferSize = 0; funArgs.value = []; nextTick(() => { offset.value = direction.value === 'left' ? 0 : getFullWidth() - realBoxWidth; tempOffset = 0; singleOffset = 0; }); }; const getBufferSize = () => { let tempBufferSize = targetList.length - visibleCount.value; tempBufferSize = Math.max(1, tempBufferSize); tempBufferSize = Math.min(5, tempBufferSize); return tempBufferSize; }; const add = (index, values, cb) => { if (!!values && values.length > 0) { if (index > targetList.length) { index = targetList.length; } if (index < 0) { index = 0; } const findIndexs = []; if (values.length === 1) { visibleItems.value.forEach((v, i) => { if (v.index === index) { findIndexs.push(i); } }); } const datas = []; values.forEach((v) => { datas.push({ id: uuid(), data: v }); }); targetList.splice(index, 0, ...datas); targetList.forEach((v, i) => { v.index = i; }); const tempBufferSize = getBufferSize(); if (listCanScroll) { if (index < cursorIndex) { cursorIndex += 1; if (cursorIndex > targetList.length) { cursorIndex = 0; } } if (values.length === 1) { findIndexs.forEach((i) => { visibleItems.value[i] = datas[0]; }); } if (tempBufferSize !== bufferSize) { bufferSize = tempBufferSize; } } else { const fun = () => { const hasVerticalScroll = realWrapperHiddenRef.value.offsetWidth > realBoxWidth; if (hasVerticalScroll) { bufferSize = tempBufferSize; funArgs.value = initCursorIndex(); nextTick(() => { offset.value = direction.value === 'left' ? 0 : getFullWidth() - realBoxWidth; tempOffset = 0; singleOffset = 0; initWidth(); listCanScroll = true; animation(false, props.step); }); } else { if (props.visibleCount === (undefined)) { visibleCount.value = 0; } init(); } testList.value = []; }; if (visibleCount.value === 0 && props.visibleCount === (undefined)) { initVisibleCount(() => { fun(); }); } else { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { fun(); }); } } } if (!!cb && typeof cb === 'function') { cb(targetList); } total = targetList.length; }; const remove = (index, num = 1, cb) => { if (index >= 0 && index < targetList.length) { targetList.splice(index, num); if (listCanScroll) { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { const hasVerticalScroll = realWrapperHiddenRef.value.offsetWidth > realBoxWidth; if (hasVerticalScroll) { const tempBufferSize = getBufferSize(); if (tempBufferSize !== bufferSize) { bufferSize = tempBufferSize; } funArgs.value = initCursorIndex(); } else { init(); } testList.value = []; }); } else { init(); } } if (!!cb && typeof cb === 'function') { cb(targetList); } total = targetList.length; }; const reset = () => { nextTick(() => { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { const hasVerticalScroll = realWrapperHiddenRef.value.offsetWidth > realBoxWidth; if (hasVerticalScroll) { if (funArgs.value[0] === 'splice') { updateCursorIndex(); nextTick(() => { initWidth(); }); } else { funArgs.value = initCursorIndex(); nextTick(() => { initWidth(); }); } } else { init(); } }); }); }; const update = (index, value, cb) => { if (index >= 0 && index < targetList.length) { const findIndexs = []; visibleItems.value.forEach((v, i) => { if (v.index === index) { findIndexs.push(i); } }); const data = { id: uuid(), index: index, data: value }; targetList[index] = data; if (findIndexs.length > 0) { findIndexs.forEach((i) => { visibleItems[i] = data; }); } } if (!!cb && typeof cb === 'function') { cb(targetList); } }; expose({ add, remove, update, reset }); watch(() => props.modelValue, (val) => { if (val) { animation(false, props.step); } }); onUnmounted(() => { cancelAnimationFrame(reqFrame); if (!!realWrapperRef.value) { realWrapperRef.value.parentElement.removeEventListener('mouseenter', onMouseenter); realWrapperRef.value.parentElement.removeEventListener('mouseleave', onMouseleave); realWrapperRef.value.parentElement.removeEventListener('wheel', onWheel); } }); return { visibleItems, offset, testList, realWrapperRef, realWrapperHiddenRef } } }); function render$1(_ctx, _cache, $props, $setup, $data, $options) { return (openBlock(), createElementBlock(Fragment, null, [ createElementVNode("div", mergeProps({ class: "vue3-seamless-horizontal-wrapper", ref: "realWrapperRef", style: { transition: `transform ${_ctx.ease}`, transform: `translateX(-${_ctx.offset}px)` } }, _ctx.$attrs), [ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.visibleItems, (item) => { return renderSlot(_ctx.$slots, "default", { key: item.id, data: item.data, index: item.index, style: "color: brown;" }) }), 128 /* KEYED_FRAGMENT */)) ], 16 /* FULL_PROPS */), createElementVNode("div", mergeProps({ style: {"position":"absolute !important","top":"-999999px !important"}, ref: "realWrapperHiddenRef" }, _ctx.$attrs), [ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.testList, (item) => { return renderSlot(_ctx.$slots, "default", { key: item.id, data: item.data, index: item.index }) }), 128 /* KEYED_FRAGMENT */)) ], 16 /* FULL_PROPS */) ], 64 /* STABLE_FRAGMENT */)) } var css_248z$1 = "\n.vue3-seamless-horizontal-wrapper[data-v-530d6df5] {\n height: 100%;\n}\n"; styleInject(css_248z$1); script$1.render = render$1; script$1.__scopeId = "data-v-530d6df5"; script$1.__file = "packages/HorizontalScroll.vue"; var script = defineComponent({ name: 'Vue3SeamlessScroll', emits: ['offset', 'count'], inheritAttrs: false, props: { // 是否开启自动滚动 modelValue: { type: Boolean, default: true, }, list: { type: Array, required: true, }, // 是否开启鼠标悬停 hover: { type: Boolean, default: false, }, // 单步停止等待时间 (默认值 1000ms) singleWaitTime: { type: Number, default: 1000, }, // 开启鼠标悬停时支持滚轮滚动 wheel: { type: Boolean, default: false, }, // 启用单行滚动 singleLine: { type: Boolean, default: false, }, step: { type: Number, default: 0.5, }, visibleCount: { type: Number, }, ease: { type: String, default: 'cubic-bezier(0.03, 0.76, 1, 0.16)' }, // up,down,left,right direction: { type: String, default: 'up', }, delay: { type: Number, default: 0, } }, setup(props, { expose, emit }) { /** * @type {import('vue').Ref} */ const realBoxRef = ref(null); /** * @type {import('vue').Ref} */ const realWrapperRef = ref(null); /** * @type {import('vue').Ref} */ const realWrapperHiddenRef = ref(null); const funArgs = ref([]); const offset = ref(0); const isHover = ref(false); const direction = ref(props.direction); const isScroll = computed(() => props.hover ? !isHover.value && props.modelValue : props.modelValue); const testList = ref([]); const visibleCount = ref(props.visibleCount === (undefined) ? 0 : props.visibleCount); const targetList = listMap(props.list); let total = 0; let count = 0; let totalCount = 0; let childrenWHList = []; let bufferTotalWH = 0; let bufferSize = 0; let cursorIndex = -1; let realBoxWH = 0; let reqFrame = null; let singleState = false; let singleOffset = 0; let tempOffset = 0; let listCanScroll = false; const transform = computed(() => { if (direction.value === 'up' || direction.value === 'down') { return `translateY(-${offset.value}px)`; } return `translateX(-${offset.value}px)`; }); const cancle = () => { cancelAnimationFrame(reqFrame); reqFrame = null; }; const updateCursorIndex = () => { const totalIndex = cursorIndex + bufferSize; if (totalIndex >= targetList.length) { const tempIndex = totalIndex - targetList.length; const tempFuns = ['splice', [0, bufferSize], [cursorIndex], [0, tempIndex]]; funArgs.value = tempFuns; if (tempIndex > targetList.length) { cursorIndex = targetList.length; } else { cursorIndex = tempIndex; } } else { const tempFuns = ['splice', [0, bufferSize], [cursorIndex, totalIndex]]; funArgs.value = tempFuns; cursorIndex = totalIndex; } }; const initWH = () => { const children = Array.from(realWrapperRef.value.children); const subChildren = (direction.value === 'up' || direction.value === 'left') ? children.slice(0, bufferSize) : children.slice(visibleItems.value.length - bufferSize, visibleItems.value.length); childrenWHList = subChildren.map((c) => { if (direction.value === 'left' || direction.value === 'right') { return c.offsetWidth; } else { return c.offsetHeight; } }); bufferTotalWH = childrenWHList .reduce((a, b) => a + b, 0); }; const updateOffset = () => { if (direction.value === 'up' || direction.value === 'left') { offset.value = 0; } else { offset.value = getFullWH() - realBoxWH; } }; const animation = (isWheel, step) => { cancle(); if (!listCanScroll) { return; } const scrollFun = () => { const singleWH = childrenWHList[0]; let singleDone = false; if (singleOffset >= singleWH) { singleDone = true; } if (singleDone) { childrenWHList.shift(); singleOffset = 0; count += 1; } if (count === total) { totalCount += 1; emit('count', totalCount); count = 0; } if (props.singleLine && singleDone) { singleState = true; setTimeout(() => { singleState = false; if (!isWheel) { animation(false, props.step); } }, props.singleWaitTime); } else { if (!isWheel) { animation(false, props.step); } } }; if (isScroll.value && !singleState || isWheel) { reqFrame = requestAnimationFrame(() => { tempOffset += step; singleOffset += step; if (direction.value === 'up' || direction.value === 'left') { offset.value += step; } else { offset.value -= step; } if (tempOffset > bufferTotalWH) { emit('offset', bufferSize, targetList); updateCursorIndex(); nextTick(() => { updateOffset(); tempOffset = 0; initWH(); scrollFun(); }); } else { scrollFun(); } }); } }; const onMouseenter = () => { isHover.value = true; }; const onMouseleave = () => { isHover.value = false; if (props.hover) { animation(false, props.step); } }; const throttleFunc = throttle(30, (e) => { animation(true, 10); }); const onWheel = (e) => { if (props.hover && props.wheel) { throttleFunc(e); e.preventDefault(); e.stopPropagation(); } }; /** * @type {import('vue').ComputedRef>} */ const visibleItems = computed(() => { let tempList = []; if (funArgs.value.length === 0) { tempList = targetList.slice(0, visibleCount.value); } else if (funArgs.value[0] === 'splice') { tempList = (direction.value === 'up' || direction.value === 'left') ? visibleItems.value : visibleItems.value.reverse(); tempList.splice(...funArgs.value[1]); funArgs.value.slice(2).forEach(args => { tempList.push(...targetList.slice(...args)); }); } else { funArgs.value.slice(1).forEach(args => { tempList.push(...targetList.slice(...args)); }); } if (!((direction.value === 'up' || direction.value === 'left'))) { tempList.reverse(); } return duplicateId(tempList); }); const initCursorIndex = () => { cursorIndex = visibleCount.value + bufferSize; if (cursorIndex >= targetList.length) { const tempIndex = cursorIndex - targetList.length; const tempFunArgs = ['slice', [0, cursorIndex], [0, tempIndex]]; if (tempIndex > targetList.length) { cursorIndex = targetList.length; } else { cursorIndex = tempIndex; } return tempFunArgs; } else { return ['slice', [0, cursorIndex]]; } }; const getFullWH = () => { const wh = Array.from(realWrapperRef.value.children) .map((c) => { if (direction.value === 'left' || direction.value === 'right') { return c.offsetWidth; } else { return c.offsetHeight; } }).reduce((a, b) => a + b, 0); return wh; }; const initVisibleCount = (cb) => { if (props.visibleCount === (undefined)) { testList.value = listMap([props.list[0]]); nextTick(() => { if (direction.value === 'left' || direction.value === 'right') { visibleCount.value = Math.ceil(realBoxWH / realWrapperHiddenRef.value.offsetWidth) + 2; } else { visibleCount.value = Math.ceil(realBoxWH / realWrapperHiddenRef.value.offsetHeight) + 2; } testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { cb(); }); }); } else { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { cb(); }); } }; onMounted(() => { if (!!realWrapperRef.value) { realWrapperRef.value.parentElement.addEventListener('mouseenter', onMouseenter); realWrapperRef.value.parentElement.addEventListener('mouseleave', onMouseleave); realWrapperRef.value.parentElement.addEventListener('wheel', onWheel); realBoxWH = realWrapperRef.value.parentElement.offsetHeight; if (direction.value === 'left' || direction.value === 'right') { realBoxWH = realWrapperRef.value.parentElement.offsetWidth; } initVisibleCount(() => { let hasVerticalScroll = realWrapperHiddenRef.value.offsetHeight > realBoxWH; if (direction.value === 'left' || direction.value === 'right') { hasVerticalScroll = realWrapperHiddenRef.value.offsetWidth > realBoxWH; } if (hasVerticalScroll) { bufferSize = getBufferSize(); funArgs.value = initCursorIndex(); nextTick(() => { updateOffset(); initWH(); listCanScroll = true; setTimeout(() => { animation(false, props.step); }, props.delay); }); } else { init(); } testList.value = []; }); } total = targetList.length; }); const init = () => { count = 0; totalCount = 0; listCanScroll = false; bufferSize = 0; funArgs.value = []; nextTick(() => { updateOffset(); tempOffset = 0; singleOffset = 0; }); }; const getBufferSize = () => { let tempBufferSize = targetList.length - visibleCount.value; tempBufferSize = Math.max(1, tempBufferSize); tempBufferSize = Math.min(5, tempBufferSize); return tempBufferSize; }; const add = (index, values, cb) => { if (!!values && values.length > 0) { if (index > targetList.length) { index = targetList.length; } if (index < 0) { index = 0; } const findIndexs = []; if (values.length === 1) { visibleItems.value.forEach((v, i) => { if (v.index === index) { findIndexs.push(i); } }); } const datas = []; values.forEach((v) => { datas.push({ id: uuid(), data: v }); }); targetList.splice(index, 0, ...datas); targetList.forEach((v, i) => { v.index = i; }); const tempBufferSize = getBufferSize(); if (listCanScroll) { if (index < cursorIndex) { cursorIndex += 1; if (cursorIndex > targetList.length) { cursorIndex = 0; } } if (values.length === 1 && findIndexs.length > 0) { findIndexs.forEach((i) => { visibleItems[i] = datas[0]; }); } if (tempBufferSize !== bufferSize) { bufferSize = tempBufferSize; } } else { const fun = () => { let hasVerticalScroll = realWrapperHiddenRef.value.offsetHeight > realBoxWH; if (direction.value === 'left' || direction.value === 'right') { hasVerticalScroll = realWrapperHiddenRef.value.offsetWidth > realBoxWH; } if (hasVerticalScroll) { bufferSize = tempBufferSize; funArgs.value = initCursorIndex(); nextTick(() => { updateOffset(); tempOffset = 0; singleOffset = 0; initWH(); listCanScroll = true; animation(false, props.step); }); } else { if (props.visibleCount === (undefined)) { visibleCount.value = 0; } init(); } testList.value = []; }; if (visibleCount.value === 0 && props.visibleCount === (undefined)) { initVisibleCount(() => { fun(); }); } else { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { fun(); }); } } } if (!!cb && typeof cb === 'function') { cb(targetList); } total = targetList.length; }; const remove = (index, num = 1, cb) => { if (index >= 0 && index < targetList.length) { targetList.splice(index, num); if (listCanScroll) { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { let hasVerticalScroll = realWrapperHiddenRef.value.offsetHeight > realBoxWH; if (direction.value === 'left' || direction.value === 'right') { hasVerticalScroll = realWrapperHiddenRef.value.offsetWidth > realBoxWH; } if (hasVerticalScroll) { const tempBufferSize = getBufferSize(); if (tempBufferSize !== bufferSize) { bufferSize = tempBufferSize; } funArgs.value = initCursorIndex(); } else { init(); } testList.value = []; }); } else { init(); } } if (!!cb && typeof cb === 'function') { cb(targetList); } total = targetList.length; }; const reset = () => { nextTick(() => { testList.value = targetList.slice(0, visibleCount.value); nextTick(() => { let hasVerticalScroll = realWrapperHiddenRef.value.offsetHeight > realBoxWH; if (direction.value === 'left' || direction.value === 'right') { hasVerticalScroll = realWrapperHiddenRef.value.offsetWidth > realBoxWH; } if (hasVerticalScroll) { if (funArgs.value[0] === 'splice') { updateCursorIndex(); nextTick(() => { initWH(); }); } else { funArgs.value = initCursorIndex(); nextTick(() => { initWH(); }); } } else { init(); } }); }); }; const update = (index, value, cb) => { if (index >= 0 && index < targetList.length) { const findIndexs = []; visibleItems.value.forEach((v, i) => { if (v.index === index) { findIndexs.push(i); } }); const data = { id: uuid(), index: index, data: value }; targetList[index] = data; if (findIndexs.length > 0) { findIndexs.forEach((i) => { visibleItems[i] = data; }); } } if (!!cb && typeof cb === 'function') { cb(targetList); } }; expose({ add, remove, update, reset }); watch(() => props.modelValue, (val) => { if (val) { animation(false, props.step); } }); onUnmounted(() => { cancelAnimationFrame(reqFrame); if (!!realWrapperRef.value) { realWrapperRef.value.parentElement.removeEventListener('mouseenter', onMouseenter); realWrapperRef.value.parentElement.removeEventListener('mouseleave', onMouseleave); realWrapperRef.value.parentElement.removeEventListener('wheel', onWheel); } }); return { realBoxRef, realWrapperRef, visibleItems, offset, testList, realWrapperHiddenRef, transform, } } }); function render(_ctx, _cache, $props, $setup, $data, $options) { return (openBlock(), createElementBlock(Fragment, null, [ createElementVNode("div", mergeProps({ class: "vue3-seamless-wrapper", ref: "realWrapperRef", style: { transition: `transform ${_ctx.ease}`, transform: _ctx.transform } }, _ctx.$attrs), [ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.visibleItems, (item) => { return renderSlot(_ctx.$slots, "default", { key: item.id, data: item.data, index: item.index }) }), 128 /* KEYED_FRAGMENT */)) ], 16 /* FULL_PROPS */), createElementVNode("div", mergeProps({ style: {"position":"absolute !important","left":"-999999px !important"}, ref: "realWrapperHiddenRef" }, _ctx.$attrs), [ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.testList, (item) => { return renderSlot(_ctx.$slots, "default", { key: item.id, data: item.data, index: item.index }) }), 128 /* KEYED_FRAGMENT */)) ], 16 /* FULL_PROPS */) ], 64 /* STABLE_FRAGMENT */)) } var css_248z = "\n.vue3-seamless-wrapper[data-v-b156a416] {\r\n width: 100%;\r\n height: 100%;\n}\r\n"; styleInject(css_248z); script.render = render; script.__scopeId = "data-v-b156a416"; script.__file = "packages/Vue3SeamlessScroll.vue"; const install = function (app, options = {}) { app.component(options.name || script.name, script); }; function index (app) { app.use(install); } export { script$1 as HorizontalScroll, script$2 as VerticalScroll, script as Vue3SeamlessScroll, index as default };