<template>
<div class="the-scrollbar" ref="el" @mouseenter="onEnter" @mouseleave="onLeave">
<div ref="wrap" class="the-scrollbar-wrap" :style="wrapStyle">
<slot></slot>
</div>
<p
class="the-scrollbar-thumb"
ref="thumbY"
:style="thumbStyle.y"
v-show="showThumb1"
></p>
<p
class="the-scrollbar-thumb"
ref="thumbX"
:style="thumbStyle.x"
v-show="showThumb1"
></p>
</div>
</template>
<script>
const scrollbarSize = (function() {
const el = document.createElement("div");
el.style.width = "100px";
el.style.height = "100px";
el.style.overflow = "scroll";
document.body.appendChild(el);
const width = el.offsetWidth - el.clientWidth;
document.body.removeChild(el)
return width;
})();
export default {
name: "Scrollbar",
props: {
thumbColor: {
type: String,
default: "#ccc"
},
thumbSize: {
type: Number,
default: 8
},
clickUpdateDelay: {
type: Number,
default: 0
},
},
data() {
return {
wrapStyle: {
height: "",
width: ""
},
thumbStyle: {
x: {
width: "",
height: "",
left: "",
bottom: "",
transform: "",
borderRadius: "",
backgroundColor: this.thumbColor
},
y: {
width: "",
height: "",
top: "",
right: "",
transform: "",
borderRadius: "",
backgroundColor: this.thumbColor
}
},
showThumb1: false,
isDrag: false,
vertical: false,
deviation: 0,
timer: ''
}
},
methods: {
updateWrapStyle() {
const parent = this.$refs.el.parentNode
parent.style.overflow = "hidden";
const css = getComputedStyle(parent);
this.wrapStyle.width = `calc(100% + ${scrollbarSize}px + ${css.borderLeftWidth} + ${css.borderRightWidth})`;
this.wrapStyle.height = `calc(100% + ${scrollbarSize}px + ${css.borderTopWidth} + ${css.borderBottomWidth})`;
},
initThumbStyle() {
this.thumbStyle.y.right = this.thumbStyle.y.top = "0px";
this.thumbStyle.y.width = this.thumbSize + "px";
this.thumbStyle.x.bottom = this.thumbStyle.x.left = "0px";
this.thumbStyle.x.height = this.thumbSize + "px";
this.thumbStyle.x.borderRadius = this.thumbStyle.y.borderRadius = `${this.thumbSize / 2}px`;
},
updateThumbStyle() {
const wrapEl = this.$refs.wrap;
if (wrapEl) {
let height = wrapEl.clientHeight / wrapEl.scrollHeight * 100;
if (height >= 100) {
height = 0;
}
this.thumbStyle.y.height = height + "%";
this.thumbStyle.y.transform = `translate3d(0, ${wrapEl.scrollTop / wrapEl.scrollHeight * wrapEl.clientHeight}px, 0)`;
let width = (wrapEl.clientWidth / wrapEl.scrollWidth) * 100;
if (width >= 100) {
width = 0;
}
this.thumbStyle.x.width = width + "%";
this.thumbStyle.x.transform = `translate3d(${wrapEl.scrollLeft / wrapEl.scrollWidth * wrapEl.clientWidth}px, 0, 0)`;
}
},
onDragStart(event) {
const _thumbX = this.$refs.thumbX;
const _thumbY = this.$refs.thumbY;
const target = event.target;
if (_thumbX.contains(target)) {
this.isDrag = true;
this.vertical = false;
this.deviation = event.clientX - _thumbX.getBoundingClientRect().left;
}
if (_thumbY.contains(target)) {
this.isDrag = true;
this.vertical = true;
this.deviation = event.clientY - _thumbY.getBoundingClientRect().top;
}
},
onDragMove(event) {
if (!this.isDrag) return;
const wrapEl = this.$refs.wrap;
if (this.vertical) {
const wrapTop = wrapEl.getBoundingClientRect().top;
const wrapHeight = wrapEl.clientHeight;
let value = event.clientY - wrapTop;
wrapEl.scrollTop = (value - this.deviation) / wrapHeight * wrapEl.scrollHeight;
} else {
const wrapLeft = wrapEl.getBoundingClientRect().left;
const wrapWidth = wrapEl.clientWidth;
let value = event.clientX - wrapLeft;
wrapEl.scrollLeft = (value - this.deviation) / wrapWidth * wrapEl.scrollWidth;
}
},
onDragEnd(event) {
this.isDrag = false;
if (this.$refs.el.contains(event.target)) {
if (this.clickUpdateDelay > 0) {
this.timer && clearTimeout(this.timer);
this.timer = setTimeout(this.updateThumbStyle, this.clickUpdateDelay);
}
} else {
this.showThumb = false;
}
},
onEnter() {
this.showThumb = true;
this.updateThumbStyle();
},
onLeave() {
if (!this.isDrag) {
this.showThumb = false;
}
}
},
mounted () {
this.updateWrapStyle();
this.initThumbStyle();
this.$refs.wrap && this.$refs.wrap.addEventListener("scroll", this.updateThumbStyle);
document.addEventListener("mousedown", this.onDragStart);
document.addEventListener("mousemove", this.onDragMove);
document.addEventListener("mouseup", this.onDragEnd);
this.showThumb1 = true
},
destroyed () {
this.$refs.wrap && this.$refs.wrap.removeEventListener("scroll", this.updateThumbStyle);
document.removeEventListener("mousedown", this.onDragStart);
document.removeEventListener("mousemove", this.onDragMove);
document.removeEventListener("mouseup", this.onDragEnd);
this.timer && clearTimeout(this.timer);
}
}
</script>
<style lang="less">
.the-scrollbar {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
.the-scrollbar-wrap {
overflow: scroll;
}
.the-scrollbar-thumb {
position: absolute;
z-index: 10;
outline: none;
border: none;
}
}
</style>