组件源码:
https://github.com/AntJavascript/widgetUI/tree/master/Scroll
自定义组件我主要是用来解决手机端路由返回的时候,页面会自动滚动到顶部的问题
组件页面结构:
<template>
<div class="wt-scroll" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd">
<div class="scroll-wrapper" v-if="direction.toLocaleUpperCase() === 'V'" :style="{transform: 'translate3d(0px, '+ distance +'px, 0px)'}">
<slot></slot>
</div>
<div class="scroll-wrapper horizontal" v-else :style="{transform: 'translate3d('+ -distance +'px, 0px, 0px)'}">
<slot></slot>
</div>
<div v-show="scrollbar"
v-if="direction.toLocaleUpperCase() === 'V'"
class="scroll-scrollbar"
:style="{'height':scrollHeight * 0.98 + 'px', 'top': scrollHeight * 0.01 + 'px', 'opacity':status != '' ? 1 : autoHide ? 0 : 1}">
<div class="scroll-scrollbar-drag" :style="{'height':scrollHeight * scrollRatio + 'px', transform: 'translate3d(0px, '+ -distance * scrollRatio +'px, 0px)'}"></div>
</div>
<div v-show="scrollbar" v-else class="scroll-scrollbar horizontal" :style="{'opacity':status != '' ? 1 : autoHide ? 0 : 1}">
<div class="scroll-scrollbar-drag"
:style="{'width':scrollWidth + 'px', transform: 'translate3d('+ distance * scrollRatio +'px, 0px, 0px)'}">
</div>
</div>
</div>
</template>
代码分析:
props参数:
props: {
scrollbar: { // 是否显示滚动条
type: Boolean,
default: () => {
return false;
}
},
direction: { // 组件滚动方向("V" 代表垂直滚动,"H" 代表水平滚动)
type: String,
default: () => {
return 'v';
}
},
autoHide: { // 滚动条是否会自动隐藏
type: Boolean,
default: () => {
return false;
}
}
}
data参数:
data () {
return {
speed: 4, // 速度
translate: 0, // 滑动距离
isTop: true, // 是否滑动到了顶部
isBottom: false, // 是否滑动到了底部
distance: 0, // 滑动距离
hisdistance: 0, // 上一次滑动距离
maxDistance: '', // 最大滑动距离
start: { // 触摸坐标
X: 0,
Y: 0
},
move: { // 移动坐标
X: 0,
Y: 0
},
scrollHeight: '', // 滚动条背景高度
scrollWidth: '', // 滚动条背景宽度
scrollRatio: '', // 滚动条比例
startTime: '', // 触摸开始时间
endTime: '', // 触摸结束时间
transtionTime: '0', // 过渡时间
timer: '', // 定时器
status: '' // 状态
};
}
touch事件:
touchStart // 手指接触屏幕触发
touchStart () {
// 触摸坐标
this.start.X = event.touches[0].clientX;
this.start.Y = event.touches[0].clientY;
this.startTime = new Date().getTime();
}
touchmove // 滑动手指的时候触发
touchMove () {
var self = this;
event.preventDefault();
self.setTranstionTime('0ms');
// 清除定时器,不清除的话,连续滑动会导致滚动条渐隐渐现
clearTimeout(self.timer);
// 滑动时候的坐标
this.move.X = event.touches[0].clientX;
this.move.Y = event.touches[0].clientY;
// 滑动距离
var tance = '';
if (this.direction.toLocaleUpperCase() === 'H') {
tance = -(this.move.X - this.start.X); // 本次滑动距离
} else {
tance = this.move.Y - this.start.Y; // 本次滑动距离
}
// 如果当前处于顶部或者底部,就增加阻力
if (this.hisdistance === 0 || Math.abs(this.hisdistance) === this.maxDistance) {
tance = tance * 0.5;
}
this.distance = this.hisdistance + tance; // 页面滑动距离(上一次滑动距离 + 本次滑动距离)
this.status = 'moveing'
}
touchend // 手指离开触发
touchEnd () {
var self = this;
self.endTime = new Date().getTime();
// this.move.Y 说明没有滑动
if (this.direction.toLocaleUpperCase() === 'V') {
if (this.move.Y === 0) {
return;
}
}
// 如果触摸时间超过500ms,则不加速滑动
if (self.endTime - self.startTime > 500) {
self.speed = 1;
} else {
self.speed = 4;
}
var thisTanceX = this.move.X - this.start.X; // 本次滑动是X距离
var thisTanceY = this.move.Y - this.start.Y; // 本次滑动是Y距离
// 设置过渡时间
self.setTranstionTime('1000ms');
if (this.direction.toLocaleUpperCase() === 'H') {
// 水平滚动
// 如果是触顶或者触底了,就把设置时间设置为300ms
if (this.hisdistance + -thisTanceX * self.speed <= 0 || Math.abs(this.hisdistance + -thisTanceX * self.speed) >= this.maxDistance) {
self.setTranstionTime('300ms');
}
this.distance = this.hisdistance + -thisTanceX * self.speed;
} else {
// 垂直滚动
// 如果是触顶或者触底了,就把设置时间设置为300ms
if (this.hisdistance + thisTanceY * self.speed >= 0 || Math.abs(this.hisdistance + thisTanceY * self.speed) >= this.maxDistance) {
self.setTranstionTime('300ms');
}
this.distance = this.hisdistance + thisTanceY * self.speed;
}
if (this.direction.toLocaleUpperCase() === 'H') {
// 水平滚动
if (self.distance <= 0) {
self.distance = 0;
self.hisdistance = 0; // 清除上一次滑动距离
self.isTop = true; // 滑动到了顶部
} else if (Math.abs(self.distance) >= this.maxDistance) {
self.isBottom = true;
self.distance = this.maxDistance;
self.hisdistance = self.distance; // 记录上次滑动的位置
} else {
self.isTop = false;
self.isBottom = false;
self.hisdistance = self.distance; // 记录上次滑动的位置
}
} else {
// 垂直滚动
// self.distance >= 0 说明滑动到了顶部
if (self.distance >= 0) {
self.distance = 0; // 滑动距离等于0
self.hisdistance = 0; // 清除上一次滑动距离
self.isTop = true; // 滑动到了顶部
} else if (Math.abs(self.distance) >= this.maxDistance) {
self.isBottom = true; // 滑动到了底部
self.distance = -this.maxDistance; // 滑动距离等于最大滑动距离
self.hisdistance = self.distance; // 记录上次滑动的位置
} else {
self.isTop = false;
self.isBottom = false;
self.hisdistance = self.distance; // 记录上次滑动的位置
}
}
if (this.direction.toLocaleUpperCase() === 'H') {
// 不能设置为0,否则触摸手机边界会有问题
// self.move.X = 0;
} else {
self.move.Y = 0;
}
self.timer = setTimeout ( () => {
this.status = '' // 清空状态
}, ~~self.transtionTime + 300)
}
mounted 生命周期,初始化工作
mounted () {
var el = this.$el;
var wrapper = el.firstChild;
// 如果是横向滚动
if (this.direction.toLocaleUpperCase() === 'H') {
this.$nextTick(() => {
var itemConutWidth = 0; // wrapper总宽度
var len = wrapper.childElementCount; // wrapper 的子节点数量
// 这个for循环用于计算 wrapper 所有子节点的宽度
for (let i = 0; i < len; i++) {
// 获取元素的marginLeft值
var marginL= parseFloat(getComputedStyle(wrapper.children[i], false)['marginLeft'].replace('px', ''));
// 获取元素的marginRight值
var marginR= parseFloat(getComputedStyle(wrapper.children[i], false)['marginRight'].replace('px', ''));
itemConutWidth+= wrapper.children[i].offsetWidth + marginL + marginR;
}
wrapper.style.width = itemConutWidth - el.offsetWidth + 'px'; // 设置 wrapper 的宽度
this.maxDistance = itemConutWidth - el.offsetWidth; // 设置可滚动的最大距离
this.scrollWidth = (el.offsetWidth / this.maxDistance) * el.offsetWidth; // 设置滚动条宽度(水平滚动时可用到)
this.scrollRatio = (el.offsetWidth / itemConutWidth); // 设置滚动条比例
});
wrapper.style.display = 'flex'; // 设置display属性为 "flex"
} else {
/* 垂直滚动设置 */
// 如果高度大于整个屏幕高度,则滚动区域高度等于屏幕高度
var elHeight = el.clientHeight < MediaQuery.height ? el.clientHeight : MediaQuery.height;
this.scrollHeight = elHeight * 0.98; // 设置滚动条高度
this.maxDistance = wrapper.offsetHeight - elHeight; // 设置可滚动的最大距离
this.scrollRatio = this.scrollHeight / wrapper.offsetHeight; // 设置滚动条比例
}
}
组件源码:
https://github.com/AntJavascript/widgetUI/tree/master/Scroll