功能简介
功能比较简陋,仅实现了主要功能,水平滚动条,垂直滚动条,容器高宽改变时动态更改滚动条的大小,自定义颜色,方位等功能可通过添加配置参数实现
一、需要安装 “element-resize-detector”
npm install element-resize-detector
用于监听div宽高变化
二、创建工具类 util.js ,方便事件的绑定以及移除
/**
* 绑定事件
*
* @export
* @param {any} dom
* @param {any} eventType
* @param {any} callback
*/
export function on (dom, eventType, callback) {
if (document.addEventListener) {
dom.addEventListener(eventType, callback)
} else {
dom.attachEvent('on' + eventType, callback)
}
}
/**
* 解绑事件
*
* @export
* @param {any} dom
* @param {any} eventType
* @param {any} callback
*/
export function off (dom, eventType, callback) {
if (document.removeEventListener) {
dom.removeEventListener(eventType, callback)
} else {
dom.detachEvent('on' + eventType, callback)
}
}
三、创建容器 scroll.vue
<template>
<div class="scroll" ref="scroll">
<div class="scroll-container" ref="container">
<div
class="scroll-content"
ref="content"
:style="{'width':'calc(100% + '+this.vSize+'px','height':'calc(100% + '+this.hSize+'px'}"
@scroll.stop="onScroll"
>
<slot></slot>
</div>
</div>
<!--垂直滚动条-->
<scroll-slider
v-if="!hideV"
:scrollTop="scrollTop"
ref="sliderV"
@change="silderVChange"
></scroll-slider>
<!--水平滚动条-->
<scroll-slider
v-if="!hideH"
sliderType="h"
:scrollLeft="scrollLeft"
ref="sliderH"
@change="silderHChange"
></scroll-slider>
</div>
</template>
<script>
import slider from "./slider.vue";
import ElementResizeDetectorMaker from "element-resize-detector";
export default {
name: "scroll",
props: {
//隐藏垂直滚动条
hideV: {
type: Boolean,
default: false
},
//隐藏水平滚动条
hideH: {
type: Boolean,
default: false
},
resize: Boolean
},
components: {
"scroll-slider": slider
},
data() {
return {
//垂直滚动条宽度
vSize: 17,
//水平滚动条高度
hSize: 17,
scrollTop: 0,
scrollLeft: 0,
};
},
mounted() {
this.computeSliderV();
this.computeSliderH();
// 监听slot视图变化, 方法内部会判断是否设置了开启监听resize
this.resizeListener()
},
methods: {
onScroll(event) {
this.scrollTop = this.$refs["content"].scrollTop;
this.scrollLeft = this.$refs["content"].scrollLeft;
},
silderVChange(newval) {
this.$refs["content"].scrollTop = newval;
},
silderHChange(newval) {
this.$refs["content"].scrollLeft = newval;
},
//计算垂直滚动条的长度
computeSliderV() {
if (this.hideV) {
return;
}
let clientEle = this.$refs["scroll"];
let slotEle = this.$slots.default[0]["elm"];
this.$refs.sliderV.computeSlider(slotEle,clientEle);
},
computeSliderH() {
if (this.hideH) {
return;
}
let clientEle = this.$refs["scroll"];
let slotEle = this.$slots.default[0]["elm"];
this.$refs.sliderH.computeSlider(slotEle,clientEle);
},
// slot视图大小变化时的监听
resizeListener() {
// 没开启监听reszie方法
if (!this.resize) return
// 监听slot视图元素resize
let elementResizeDetector = ElementResizeDetectorMaker({strategy: 'scroll',callOnAdd: false})
// 记录视图上次宽高的变化
const ele = this.$refs.scroll;
elementResizeDetector.listenTo(ele, (element) => {
// 初始化百分比
this.computeSliderV();
this.computeSliderH();
this.scrollTop = this.$refs["content"].scrollTop;
this.scrollLeft = this.$refs["content"].scrollLeft;
})
},
}
};
</script>
<style>
.scroll {
position: relative;
width: 100px;
height: 100px;
overflow: hidden;
}
.scroll-container {
width: 100%;
height: 100%;
}
.scroll-content {
width: calc(100% + 17px);
height: calc(100% + 17px);
overflow: scroll;
}
.sliderContainer {
position: absolute;
border-radius: 5px;
}
.scroll-v {
top: 0;
bottom: 0;
right: auto;
width: 8px;
height: 100%;
}
.scroll-h {
left: 0;
bottom: auto;
right: 0;
height: 8px;
width: 100%;
}
.scroll-slider {
position: absolute;
background-color: #000000;
opacity: 0.3;
border-radius: 5px;
cursor: pointer;
transition: opacity .3s;;
}
.scroll-slider:hover {
opacity: 0.5;
}
.scroll-v .scroll-slider {
top: 0;
right: 0;
left: auto;
bottom: auto;
}
.scroll-h .scroll-slider {
top: auto;
right: auto;
left: 0;
bottom: 0;
}
</style>
四、创建滑块 slider.vue
<template>
<div
:style="[isShow]"
class="sliderContainer"
:class="sliderType=='h'?'scroll-h':'scroll-v'"
ref="sliderContainer"
@wheel.capture.stop="handlewheel"
>
<div
class="scroll-slider"
:style="[initSize,initLength,loc]"
@mousedown.stop="sliderMousedown"
></div>
</div>
</template>
<script>
import {on, off} from "./util";
export default {
props: {
//滚动条类型:h:水平滚动条,v:垂直滚动条
sliderType: {
type: String,
default: "v"
},
//滚动条最小长度
minLength: {
type: Number,
default: 20
},
size: {
type: Number,
default: 8
},
scrollTop: {
type: Number,
default: 0
},
scrollLeft: {
type: Number,
default: 0
},
color: {
type: String,
default: "#000000"
},
opacity: {
type: Number,
default: 0.3
}
},
created() {
//水平滚动条配置
let h = {
clientSize: "clientWidth",
loc: "top",
scrollSize: "scrollWidth",
locsSize: "scrollHeight",
loccSize: "clientHeight"
};
let v = {
clientSize: "clientHeight",
loc: "left",
scrollSize: "scrollHeight",
locsSize: "scrollWidth",
loccSize: "clientWidth"
};
this.config = this.sliderType === "h" ? h : v;
},
data() {
return {
isBind: false,//标识是否已经绑定拖动事件,避免重复绑定
percentage: 0,
length: 0,
startMove: false,
offset: 0,
position: {},
config: {},
isShowSilider: true,
locValue: 0
};
},
computed: {
initSize() {
return {
[this.sliderType === "h" ? "height" : "width"]: this.size + "px"
};
},
//初始长度
initLength() {
return {
[this.sliderType === "h" ? "width" : "height"]: this.length + "px"
};
},
//初始位置
loc() {
return {
[this.sliderType === "h" ? "left" : "top"]: this.offset + "px"
};
},
//是否显示,隐藏即放到视野外
isShow() {
return {
[this.config.loc]: this.isShowSilider ? (this.locValue - this.size) + 'px' : '-' + this.siz + 'px'
}
}
},
methods: {
sliderMousedown(event) {
// 只有鼠标左键可以拖动
if (event.button !== 0) {
return;
}
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
this.startMove = true;
this.position.x = event.clientX;
this.position.y = event.clientY;
this.bindEvent();
},
bindEvent() {
if (this.isBind) {
return;
}
on(document, "mouseup", this.mouseup);
on(document, "mousemove", this.mousemove);
this.isBind = true;
this.startMove = true;
},
mouseup() {
this.startMove = false;
this.isBind = false;
off(document, "mouseup", this.mouseup);
off(document, "mousemove", this.mousemove);
},
mousemove(event) {
if (!this.startMove) return;
let x = event.clientX;
let y = event.clientY;
let moveX = x - this.position.x;
let moveY = y - this.position.y;
this.position.x = x;
this.position.y = y;
let move = this.sliderType === "h" ? moveX : moveY;
this.computePosition(move);
},
computePosition(move) {
let newloc = this.offset + move;
if (newloc > this.maxOffset) {
newloc = this.maxOffset;
}
if (newloc < 0) newloc = 0;
this.offset = newloc;
this.$emit("change", newloc / this.percentage);
},
//计算滑块的一些属性
computeSlider(scroll, client) {
//内容真实长度或宽度
let scrollSize = scroll[this.config.scrollSize];
//容器的长度或宽度
let clientSize = client[this.config.clientSize];
let containerSize = this.$refs.sliderContainer[this.config.clientSize];
this.length = containerSize * (clientSize / scrollSize);
if (this.length < this.minLength) {
this.length = this.minLength;
}
if (clientSize >= scrollSize) {
this.length = 0;
}
//最大偏移距离
this.maxOffset = containerSize - this.length;
if(scrollSize - clientSize==0){
this.percentage=0;
}else{
this.percentage = this.maxOffset / (scrollSize - clientSize);
}
this.isShowSilider = this.percentage > 0 && this.percentage < 1;
let locsSize = scroll[this.config.locsSize];
let loccSize = client[this.config.loccSize];
//防止内容宽度比容器小的时候垂直滚动条和内容有距离
this.locValue = locsSize < loccSize ? locsSize : loccSize;
},
handlewheel(event) {
console.log(event);
}
},
watch: {
scrollTop() {
this.offset = this.scrollTop * this.percentage;
},
scrollLeft() {
this.offset = this.scrollLeft * this.percentage;
}
},
destroyed() {
off(document, "mouseup", this.mouseup);
off(document, "mousemove", this.mousemove);
}
};
</script>
五、demo
<template>
<div>
<scroll :style="{width:width+'px',height:height+'px'}" resize>
<div style="width: 100%;height: 100%;">
<div>水龙吟·次韵章质夫杨花词</div>
<div>似花还似非花,也无人惜从教坠。</div>
<div>抛家傍路,思量却是,无情有思。</div>
<div>萦损柔肠,困酣娇眼,欲开还闭。</div>
<div>梦随风万里,寻郎去处,又还被、莺呼起。</div>
<div>不恨此花飞尽,恨西园、落红难缀。晓来雨过,遗踪何在?</div>
<div>一池萍碎。</div>
<div>春色三分,二分尘土,一分流水。细看来,不是杨花,点点是离人泪。</div>
</div>
</scroll>
<div>
<span>scroll宽度:</span><input type="text" v-model="width"/>
<span>scroll高度:</span><input type="text" v-model="height"/>
</div>
</div>
</template>
<script>
import scroll from "./component/scroll.vue";
export default {
components: {
scroll
},
data() {
return {
width: 300,
height: 200
}
}
};
</script>
网友评论