不怎么擅长写文档,先上效果图
背景: 在使用element-ui 开发项目中,有一个隐藏的组件 el-scrollbar 可以用来美化滚动条,往往有这种需求,需要滚动到一定的位置,并且可以加一些过渡的动画,为此写了一个组件和大家分享一下。
思路:
- vue 自定义指令
- scrollHeight/clientHeight/scrollTop 等dom上的属性 用于偏移量的计算
- window.requestAnimateframe 动画 不平滑
代码:
import Vue from 'vue'
/**
* description: el-scrollbar 滚动条滚动指令
* directive-name: roll
* @param el: 指令所绑定的元素,可以用来直接操作 DOM
* @param distance 偏移距离 px/number 注:0 顶部 -1 底部,其余按照实际值,超过边界滚到底部
* @param time 过渡时间 ms/number
* @param flag 是否开启滚动 boolean
*/
Vue.directive('roll', function (el, { arg: { distance, time, flag } }) {
if (
distance !== null &&
time !== null &&
flag
) {
const wrap = el.querySelector('.el-scrollbar__wrap')
// 滚动条最大偏移距离
const maxDistance = wrap.scrollHeight - wrap.clientHeight
// 处理边界
distance = (distance < 0 || distance > maxDistance) ? maxDistance : distance
// 每帧需要偏移的距离 默认60fps/s 偏移量向上取整
const _distance = Math.ceil(Math.abs(wrap.scrollTop - distance) * 1000 / time / 60)
// 初始偏移量
const initScrollTop = wrap.scrollTop
// 执行动画
const keyframe = () => {
if (initScrollTop - distance > 0) wrap.scrollTop -= _distance
else wrap.scrollTop = wrap.scrollTop + _distance
// 判断中止条件
if (Math.abs(wrap.scrollTop - distance) > _distance) requestAnimationFrame(keyframe)
else wrap.scrollTop = distance
}
keyframe()
}
})
测试demo
<template>
<div class="test">
<el-row>
<el-col :span="8">
<el-input v-model="distance" placeholder="偏移距离">
<template slot="append">px</template>
</el-input>
</el-col>
<el-col :span="8">
<el-input v-model="time" placeholder="滚动时间">
<template slot="append">ms</template>
</el-input>
</el-col>
<el-col :span="8">
<el-button type="primary" @click="scroll">开始滚动</el-button>
<el-button type="primary" @click="addCount">增加行数</el-button>
</el-col>
</el-row>
<div style="margin-top: 20px; height: 300px;">
<el-scrollbar
ref="scrollbar"
v-roll:[scrollData]="scrollData"
style="height: 100%; background: #ffffff;"
wrap-style="overflow-x: hidden;"
>
<ul>
<li
v-for="item in rowCount"
:key="item"
>{{ item }}你好你好你好你好你好你好你好你好你好你好你好你好你好你好你好</li>
</ul>
</el-scrollbar>
</div>
</div>
</template>
<script>
export default {
name: 'Test',
data () {
return {
rowCount: 30,
distance: null,
time: null,
// 滚动条支持事件
scrollData: {
distance: null, // 滚动距离
time: null, // 动画时间
flag: false, // 是否开启滚动
},
}
},
methods: {
scroll () {
const distance = Number(this.distance) || 0
const time = Number(this.time) || 0
this.scrollData = {
flag: true,
distance: distance,
time: time
}
},
async addCount () {
this.scrollData.flag = false
await this.$nextTick()
this.rowCount++
},
},
}
</script>
<style>
html,body {
background: rgb(247, 247, 247);
}
.test {
width: 50%;
margin: 0 auto;
}
</style>