UNPKG

vue3-seamless-scroll

Version:
1,887 lines 53 kB
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<HTMLDivElement>}
    */
    const realWrapperRef = ref(null);
    /**
    * @type {import('vue').Ref<HTMLDivElement>}
    */
    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<Array<any>>}
     */
    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<HTMLDivElement>}
    */
    const realWrapperRef = ref(null);
    /**
    * @type {import('vue').Ref<HTMLDivElement>}
    */
    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<Array<any>>}
    */
    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<HTMLDivElement>}
    */
    const realBoxRef = ref(null);
    /**
    * @type {import('vue').Ref<HTMLDivElement>}
    */
    const realWrapperRef = ref(null);
    /**
    * @type {import('vue').Ref<HTMLDivElement>}
    */
    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<Array<any>>}
     */
    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 };