vue3实现隐藏滚动条并实现滚动

2,135 阅读1分钟

背景

在写 tabs 组件时会遇到内容的长度超出屏幕的情况。参考 antd 的虚拟滚动实现 vue 版本。

antd tabs 虚拟滚动

原理

未命名.png

橙色为容器(可视区),绿色为内容区(宽度超过容器)。通过设置容器 overflow: hidden 隐藏滚动条,监听内容的 wheel 滚轮事件,设置内容 tranform: translateX 模拟滚动。

源码

import { ref, onMounted } from 'vue'

/**
 * 水平方向隐藏滚动条, 实现滚动.
 * * 需要将容器设置 overflow-hidden
 * @returns container ref
 */
export default function useScrollX() {
  const content = ref<HTMLElement | null>(null)

  onMounted(() => {
    const el = content.value
    const clientWidth = el?.clientWidth || 0 // 视图宽度
    const scrollWidth = el?.scrollWidth || 0 // 内容展开的宽度

    if (scrollWidth > clientWidth && el) { // 是否需要滚动
      el.onwheel = wheel
    }
  })

  function wheel(e) {
    const el = content.value
    const clientWidth = el?.clientWidth || 0
    const scrollWidth = el?.scrollWidth || 0
    const maxTranslate = scrollWidth - clientWidth // 向右滚的边界

    // wheelDelta 正负表示方向, 大小表示速度. + 向下滚动 | - 向上滚动 | 0 不动
    const speed = e.wheelDelta > 0 ? e.wheelDelta : -e.wheelDelta
    const step = parseInt(`${clientWidth / 3000 * speed}`)
    const currTranslate = +(/\d+/.exec(el?.style.transform || '') || 0) // 获取当前的 translate 值

    let finalTranslate = currTranslate
    if (e.wheelDelta > 0) finalTranslate = currTranslate - step < 0 ? 0 : currTranslate - step
    if (e.wheelDelta < 0) finalTranslate = currTranslate + step > maxTranslate ? maxTranslate : currTranslate + step

    if (el) el.style.transform = `translateX(-${finalTranslate}px)`
  }

  return {
    content,
  }
}

使用

useScrollX 返回值为 content 的 ref, 只需要在 HTML 里将内容属性里添加 ref="content" 即可。

希望对大家有所帮助