模仿 Element-UI 框架 el-scrollbar 组件

前言

我们在 Element-UI 的其他组件里发现,有使用这个组件,并且还不错,由于 Element-UI 官方文档并没有写到关于 el-scrollbar 的文档,所以稳定性上可能是有些问题。

于是只好自己写了一个类似于  el-scrollbar 功能的组件。

示例:

首先,需要创建一个 Vue 组件,该组件将包含包裹容器、内容容器和滚动条,然后监听滚动事件并处理用户交互。

<template>
  <div class="el-scrollbar">
    <div class="el-scrollbar__wrap" ref="wrap" @scroll="handleScroll">
      <div class="el-scrollbar__content" :style="{ 'margin-right': verticalScrollbarWidth + 'px' }">
        <!-- Your content goes here -->
      </div>
    </div>
    <div class="el-scrollbar__bar" v-show="vBarVisible" ref="bar" @mousedown="startDrag">
      <div class="el-scrollbar__thumb" ref="thumb" :style="thumbStyle"></div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      size: 0,
      verticalScrollbarWidth: 0,
      isDragging: false,
      startY: 0,
      scrollTop: 0,
      thumbTop: 0,
    };
  },
  computed: {
    vBarVisible() {
      const content = this.$refs.wrap;
      if (content) {
        return content.scrollHeight > content.clientHeight;
      }
      return false;
    },
    thumbStyle() {
      return {
        height: `${this.size}px`,
        top: `${this.thumbTop}px`,
      };
    },
  },
  methods: {
    handleScroll() {
      this.scrollTop = this.$refs.wrap.scrollTop;
      this.updateThumbStyle();
    },
    updateThumbStyle() {
      const heightPercentage = (this.scrollTop / this.$refs.wrap.scrollHeight) * 100;
      this.size = (this.$refs.wrap.clientHeight / this.$refs.wrap.scrollHeight) * 100;
      this.thumbTop = (this.$refs.wrap.clientHeight - this.size) * (heightPercentage / 100);
    },
    startDrag(e) {
      this.isDragging = true;
      this.startY = e.clientY;
      this.startScrollTop = this.scrollTop;
      this.$refs.thumb.classList.add('el-scrollbar__thumb--hover');
      document.addEventListener('mousemove', this.handleDrag);
      document.addEventListener('mouseup', this.endDrag);
      document.addEventListener('mouseleave', this.endDrag);
    },
    handleDrag(e) {
      const deltaY = e.clientY - this.startY;
      const percentage = deltaY / this.$refs.wrap.clientHeight;
      this.$refs.wrap.scrollTop = this.startScrollTop + percentage * this.$refs.wrap.scrollHeight;
    },
    endDrag() {
      this.isDragging = false;
      this.$refs.thumb.classList.remove('el-scrollbar__thumb--hover');
      document.removeEventListener('mousemove', this.handleDrag);
      document.removeEventListener('mouseup', this.endDrag);
      document.removeEventListener('mouseleave', this.endDrag);
    },
  },
  created() {
    this.verticalScrollbarWidth = this.calculateVerticalScrollbarWidth();
  },
  mounted() {
    this.handleScroll();
  },
  updated() {
    this.handleScroll();
  },
  methods: {
    calculateVerticalScrollbarWidth() {
      const outer = document.createElement('div');
      outer.style.visibility = 'hidden';
      outer.style.width = '100px';
      outer.style.position = 'absolute';
      outer.style.top = '-9999px';
      document.body.appendChild(outer);

      const widthNoScroll = outer.offsetWidth;
      outer.style.overflow = 'scroll';

      const inner = document.createElement('div');
      inner.style.width = '100%';
      outer.appendChild(inner);

      const widthWithScroll = inner.offsetWidth;
      outer.parentNode.removeChild(outer);

      return widthNoScroll - widthWithScroll;
    },
  },
};
</script>

<style scoped>
.el-scrollbar {
  position: relative;
  overflow: hidden;
}

.el-scrollbar__wrap {
  overflow: hidden;
  position: relative;
}

.el-scrollbar__content {
  margin-right: 0;
}

.el-scrollbar__bar {
  position: absolute;
  width: 6px;
  right: 2px;
  background: #e2e2e2;
  border-radius: 4px;
  transition: background-color 0.3s, width 0.3s;
}

.el-scrollbar__bar:hover {
  background: #c0c0c0;
}

.el-scrollbar__thumb {
  width: 6px;
  height: 0;
  background: #333;
  border-radius: 4px;
  transition: background-color 0.3s, height 0.3s;
}

.el-scrollbar__thumb.el-scrollbar__thumb--hover {
  background: #000;
}
</style>
这里创建了一个包裹容器(custom-scrollbar)内包含滚动容器(scroll-wrapper)、内容容器(scroll-content)和滚动条(scroll-bar)。滚动条可以被拖动,滚动事件会更新滚动位置。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值