vue2实现列表自动滚动指令

711 阅读1分钟

要在Vue 2中实现具有可控滚动速度、鼠标移入停止滚动和加载更多数据功能的自动滚动列表指令,可以使用自定义指令和组件结合的方式

首先确定要实现的需求点清单。这里要实现的需求点有:
  1. 使用指令的方式来实现。
  2. 可控制滚动速度。
  3. 可设置鼠标移入后停止滚动。
  4. 可以加载更多数据。
  5. 支持循环滚动。
<template>
  <div>
    <ul v-auto-scroll="scrollOptions">
      <li v-for="(item, index) in items" :key="item.id">
        {{ item.name }}
        <span v-if="index === items.length - 1" ref="lastItem"></span>
      </li>
    </ul>
    <button @click="loadMore">加载更多</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [
        { id: 1, name: 'Item 1' },
        { id: 2, name: 'Item 2' },
        { id: 3, name: 'Item 3' },
        // 添加更多项...
      ],
      scrollOptions: {
        speed: 2, // 滚动速度(每毫秒滚动的像素数)
        pauseOnHover: true, // 鼠标移入停止滚动
        loadMoreThreshold: 200, // 距离底部多少像素时加载更多数据
        loop: true // 循环滚动
      }
    };
  },
  directives: {
    'auto-scroll': {
      inserted(el, binding) {
        const options = binding.value;
        let timer = null;
        let scrollHeight = el.scrollHeight;
        let clientHeight = el.clientHeight;
        let loadMoreThreshold = options.loadMoreThreshold;
        let isScrolling = false;

        function startScroll() {
          el.scrollTop += options.speed;
          if (el.scrollTop >= scrollHeight - clientHeight - loadMoreThreshold) {
            clearTimeout(timer);
            if (options.loop) {
              resetScroll();
            } else {
              loadMore();
            }
          } else {
            timer = setTimeout(startScroll, 1);
          }
        }

        function resetScroll() {
          el.scrollTop = 0;
          timer = setTimeout(startScroll, 1000);
        }

        function loadMore() {
          // 模拟加载更多数据的操作
          setTimeout(() => {
            const newItems = [
              { id: 4, name: 'Item 4' },
              { id: 5, name: 'Item 5' },
              // 添加更多项...
            ];
            this.items = this.items.concat(newItems);
            scrollHeight = el.scrollHeight;
          }, 500);
        }

        el.addEventListener('mouseenter', () => {
          if (options.pauseOnHover) {
            clearTimeout(timer);
            isScrolling = false;
          }
        });

        el.addEventListener('mouseleave', () => {
          if (options.pauseOnHover && !isScrolling) {
            timer = setTimeout(startScroll, 1000);
          }
        });

        timer = setTimeout(startScroll, 1000);
        isScrolling = true;

        this.$nextTick(() => {
          const lastItem = el.querySelector('[ref="lastItem"]');
          if (lastItem) {
            lastItem.addEventListener('transitionend', () => {
              isScrolling = true;
              timer = setTimeout(startScroll, 1);
            });
          }
        });
      }
    }
  },
  methods: {
    loadMore() {
      // 手动加载更多数据
      loadMore();
    }
  }
};
</script>

在上述示例中,我们添加了一个名为loop的选项到scrollOptions中,并将其设为true以启用循环滚动功能。

在指令的逻辑中,当滚动到列表底部时,如果循环滚动功能被启用,则会调用resetScroll函数,将滚动位置重置为列表顶部,并重新开始滚动。如果循环滚动功能未启用,则会触发加载更多数据的操作。

为了正确处理循环滚动时的过渡效果,我们使用$nextTick在列表渲染完成后获取最后一个项的元素,并在其过渡结束后重新开始滚动。