前言
没有前言。
功能
利用vue全局自定义指令 写了一个让元素上下滚动指定偏移量功能。
自定义指令 ‘scroll-top’
自定义指令模块TickDirective.js
代码如下:
import Vue from 'vue'
//全局自定义指令 给目标元素动画滚动到指定偏移位置 如果绑定值为true 则滚动到顶部
Vue.directive('scroll-top', {
/**
*
* @param {object} el
* @param {object} binding
* binding.value={
* allowScroll:false,是否允许滚动
* offset:0,滚动到距父元素上偏移量
* complete:()=>{},滚动结束的回调,用来初始化/还原绑定值
* }
*/
componentUpdated: function (el, binding, ) {
//是否允许滚动
let allowScroll = false
//目标偏移
let goalOffset = 0
//动画完成的回调
let completeCall = null
//速度因子
let divisor = 5
//如果绑定值是boolean类型 默认滚动到顶部
if (typeof binding.value == "boolean") {
allowScroll = binding.value
}
//如果绑定值是对象类型
if (binding.value && typeof binding.value == "object") {
allowScroll = binding.value.allowScroll || false
goalOffset = binding.value.offset || 0
completeCall = binding.value.complete
divisor = binding.value.divisor || 5
}
//获取html原生元素
const element = el.$el ? el.$el : el
//滚动逻辑
if (allowScroll == true) {
//如果偏移量 和目标偏移量相等 则不滚动 并执行回调方法
if (element.scrollTop == goalOffset) {
if (completeCall) {
//回调函数里记得把绑定值初始化 比如设为false
completeCall()
}
return
}
//判断滚动方向
let direction = 'up'
//目标偏移 大于当前偏移 需向上滚动 增加元素上偏移
if (goalOffset > element.scrollTop) {
//向上
direction = 'up'
} else {
//向下
direction = 'down'
}
//获取当前偏移和目标偏移的绝对差值
let offsetDiff = Math.abs(goalOffset - element.scrollTop)
//计时器 滚动逻辑
let timer = setInterval(() => {
//计算速度 向上舍入 速度最小为 1 这样element.scrollTop == goalOffset 最后必然成立
//注意 当offsetDiff小于divisor后 速度将恒为1 所以 divisor不不宜过大 建议设为5
let ispeed = Math.ceil(offsetDiff / divisor)
//计算剩余差值
offsetDiff = offsetDiff - ispeed
//根据滚动方向 判断偏移量是加是减
if (direction == 'up') {
element.scrollTop = element.scrollTop + ispeed
} else {
element.scrollTop = element.scrollTop - ispeed
}
//当前偏移 等于目标偏移 停止滚动 清除计时器 并执行回调方法
if (element.scrollTop == goalOffset) {
clearInterval(timer)
if (completeCall) {
//回调函数里记得把绑定值初始化 比如设为false
completeCall()
}
}
}, 16)
}
}
})
代码有点多 主要是判断逻辑上。
引用
在main.js
中 引入:
//使自定义全局指令生效
import '@/utils/TickDirective.js'
使用
其中 .install-row-class
是要有css
属性overflow
的元素
data
data() {
return {
scrollTop: false,
}
这样 每次给scrollTop
赋值时 都会执行指令里的逻辑。
需要注意的是: 组件创建时 会执行一次指令逻辑, 所以这里把scrollTop
初始化为false
。因此,要注意对scrollTop
的复原,代码中添加的completeCall
支持回调,用于binding.value
为对象时自行添加复原代码。
复原代码
我的实际应用代码中 子组件调用 祖先组件提供的provide
,使祖先组件滚动。
祖先代码:
provide() {
return {
updateScrollTop:(offset)=> {
this.scrollTop = {
allowScroll: true,
offset,
complete: ()=> {
this.scrollTop = false
}
}
}
}
},
子孙代码:
inject: ['updateScrollTop'],
methods: {
scrollOffset(){
//目标元素 偏移量
this.updateScrollTop(this.$refs.baseProInfo.$el.offsetTop)
}
}
最后
实际效果:
参考文档:
JS中offsetTop、clientTop、scrollTop、offsetTop各位置属性详解