vue watch 修改滚动条_使用 Vue 开发 scrollbar 滚动条组件(示例代码)

本文介绍了如何使用 Vue 开发名为 UiScrollbar 的滚动条组件,包括检测和操作 class 的方法、获取滚动条宽度、以及组件的主要属性和方法。内容涉及滚动条的显示、样式计算、滚动事件处理和拖拽功能实现。
摘要由CSDN通过智能技术生成

import {trim, delay, round, throttle } from "lodash";//------------------------------------------------------------------------------

//检测 class

const hasClass = (el = null, cls = ‘‘) =>{if (!el || !cls) { return false; }if (cls.indexOf(‘ ‘) !== -1) { throw new Error(‘className should not contain space.‘); }if (el.classList) { returnel.classList.contains(cls); }return ` ${el.className} `.indexOf(` ${cls} `) > -1;

};//------------------------------------------------------------------------------

//添加 class

const addClass = (element = null, cls = ‘‘) =>{

const el=element;if (!el) { return; }

let curClass=el.className;

const classes= cls.split(‘ ‘);for (let i = 0, j = classes.length; i < j; i += 1) {

const clsName=classes[i];if (!clsName) { continue; }if(el.classList) {

el.classList.add(clsName);

}else if (!hasClass(el, clsName)) {

curClass+= ‘ ‘ +clsName;

}

}if (!el.classList) {

el.className=curClass;

}

};//------------------------------------------------------------------------------

//删除 class

const removeClass = (element, cls) =>{

const el=element;if (!el || !cls) { return; }

const classes= cls.split(‘ ‘);

let curClass=` ${el.className} `;for (let i = 0, j = classes.length; i < j; i += 1) {

const clsName=classes[i];if (!clsName) { continue; }if(el.classList) {

el.classList.remove(clsName);

}else if(hasClass(el, clsName)) {

curClass= curClass.replace(` ${clsName} `, ‘ ‘);

}

}if (!el.classList) {

el.className=trim(curClass);

}

};//------------------------------------------------------------------------------

//获取滚动条宽度

let scrollWidth = 0;

const getScrollWidth= () =>{if (scrollWidth > 0) { returnscrollWidth; }

const block= document.createElement(‘div‘);

block.style.cssText= ‘position:absolute;top:-1000px;width:100px;height:100px;overflow-y:scroll;‘;

document.body.appendChild(block);

const { clientWidth, offsetWidth }=block;

document.body.removeChild(block);

scrollWidth= offsetWidth -clientWidth;returnscrollWidth;

};//scrollSize 值

const SCROLLBARSIZE =getScrollWidth();/**

* UiScrollbar Component

* @author zhangmao 19/4/3*/exportdefault{

name:‘UiScrollbar‘,

props: {

size: { type: String,default: ‘normal‘ }, //small

//主要是为了解决在 dropdown 隐藏的情况下无法获取当前容器的真实 width height 的问题

show: { type: Boolean, default: false},

width: { type: Number,default: 0},

height: { type: Number,default: 0},

maxWidth: { type: Number,default: 0},

maxHeight: { type: Number,default: 0},

},

data() {return{

prevPageX:0, //缓存的鼠标横向位置

prevPageY: 0, //缓存的鼠标垂直位置

cursorDown: false, //鼠标拖拽标记

minBarSize: 5, //滚动条的最小快读和高度

xScroll: 0, //当前滚动条的横向位置

yScroll: 0, //当前滚动条的垂直位置

realWidth: 0, //内容的真实宽度

realHeight: 0, //内容的真实高度

xBarWidth: 0, //水平滚动条发宽度

yBarHeight: 0, //垂直滚动条的高度

xBarLastWidth: 0, //水平滚动条的最终宽度

yBarLastHeight: 0, //垂直滚动条最终的高度

containerWidth: 0, //容器的宽度

containerHeight: 0, //容器的高度

scrollWidth: 0, //滚动容器的宽度

scrollHeight: 0, //滚动容器的高度

scrollTopMax: 0, //垂直最大滚动距离限制

scrollLeftMax: 0, //水平最大滚动距离限制

trackTopMax: 0, //垂直步长最大限制

trackLeftMax: 0, //水平步长最大限制

};

},

computed: {

yBarShow() {return this.getYBarShow(); },

xBarShow() {return this.getXBarShow(); },

yBarStyle() {return{

height: `${this.yBarLastHeight}px`,

msTransform: `translateY(${this.yScroll}px)`,

webkitTransform: `translate3d(0, ${this.yScroll}px, 0)`,

transform: `translate3d(0, ${this.yScroll}px, 0)`,

};

},

xBarStyle() {return{

width: `${this.xBarLastWidth}px`,

msTransform: `translateX(0, ${this.xScroll}px, 0)`,

webkitTransform: `translate3d(${this.xScroll}px, 0, 0)`,

transform: `translate3d(${this.xScroll}px, 0, 0)`,

};

},

scrollClass() {return [‘scrollbar-view‘, {‘scrollbar-view-x‘: this.xBarShow,‘scrollbar-view-y‘: this.yBarShow,

}];

},

scrollStyle() {//注意这里是相反的

const hasWidth = this.yBarShow || this.realWidth > this.containerWidth;

const hasHeight= this.xBarShow || this.realHeight > this.containerHeight;return{

width: hasWidth&& this.scrollWidth > 0 ? `${this.scrollWidth}px` : ‘‘,

height: hasHeight&& this.scrollHeight > 0 ? `${this.scrollHeight}px` : ‘‘,

};

},

containerClass() {return [‘scrollbar-container scrollbar-autoshow‘, {‘cssui-scrollbar--s‘: this.size === ‘small‘,

}];

},

containerStyle() {if (this.xBarShow || this.yBarShow) {return{

width:this.containerWidth > 0 ? `${this.containerWidth}px` : ‘‘,

height:this.containerHeight > 0 ? `${this.containerHeight}px` : ‘‘,

};

}return{};

},

},

watch: {

show:‘showChange‘,

width:‘initail‘,

height:‘initail‘,

maxWidth:‘initail‘,

maxHeight:‘initail‘,

},

created() {this.dftData();this.initEvent();

},

mounted() {this.delayInit(); },

methods: {//------------------------------------------------------------------------------

//外部调用方法

scrollX(x) { this.$refs.scrollEl.scrollLeft =x; },

scrollY(y) {this.$refs.scrollEl.scrollTop =y; },

scrollTop() {this.$refs.scrollEl.scrollTop = 0; },

scrollBottom() {this.$refs.scrollEl.scrollTop = this.$refs.contentEl.offsetHeight; },//------------------------------------------------------------------------------

//默认隐藏 异步展示的情况

showChange(val) { if (val) { this.delayInit(); } },//------------------------------------------------------------------------------

delayInit() {this.$nextTick(() => { delay(() => { this.initail(); }, 10); });

},//------------------------------------------------------------------------------

//检测是否需要展示垂直的滚动条

getYBarShow() {if (this.height > 0) { return this.realHeight > this.height; }if (this.maxHeight > 0) { return this.realHeight > this.maxHeight; }return this.realHeight > this.containerHeight;

},//------------------------------------------------------------------------------

//检测是否需要展示横向的滚动条

getXBarShow() {if (this.width > 0) { return this.realWidth > this.width; }if (this.maxWidth > 0) { return this.realWidth > this.maxWidth; }return this.realWidth > this.containerWidth;

},//------------------------------------------------------------------------------

//内容大小改变

resizeHandle({ width, height }) {this.realWidth =width;this.realHeight =height;this.delayInit();

},//------------------------------------------------------------------------------

//设置容器大小 初始化滚动条位置

initail() {this.setContainerSize();this.setScrollSize();this.setContentSize();this.toUpdate();

},//------------------------------------------------------------------------------

//设置整个容器的大小

setContainerSize() {

const { offsetWidth= 0, offsetHeight = 0 } = this.$el;this.containerHeight = this.height || this.maxHeight ||offsetHeight;this.containerWidth = this.width || this.maxWidth ||offsetWidth;

},//------------------------------------------------------------------------------

//设置滚动容器的大小

setScrollSize() {this.scrollWidth = this.containerWidth +SCROLLBARSIZE;this.scrollHeight = this.containerHeight +SCROLLBARSIZE;

},//------------------------------------------------------------------------------

//设置内容区域的大小

setContentSize() {if (this.$refs.contentEl) {

const { offsetWidth= 0, offsetHeight = 0 } = this.$refs.contentEl;this.realWidth =offsetWidth;this.realHeight =offsetHeight;

}

},//------------------------------------------------------------------------------

//更新滚动条相关的大小位置

toUpdate() {if (this.realWidth > 0) {//水平滚动条的宽度

this.xBarWidth = round(this.containerWidth / this.realWidth * this.containerWidth);this.scrollLeftMax = this.realWidth - this.containerWidth;

}if (this.realHeight > 0) {//垂直方向滚动条的高度

this.yBarHeight = round(this.containerHeight / this.realHeight * this.containerHeight);this.scrollTopMax = this.realHeight - this.containerHeight;

}//设置滚动条最终的大小

this.xBarLastWidth = Math.max(this.xBarWidth, this.minBarSize);this.yBarLastHeight = Math.max(this.yBarHeight, this.minBarSize);this.trackTopMax = this.containerHeight - this.yBarLastHeight;this.trackLeftMax = this.containerWidth - this.xBarLastWidth;this.scrollHandler();

},//------------------------------------------------------------------------------

scrollHandler() {if (this.$refs.scrollEl) {

const {

scrollLeft= 0,

scrollTop= 0,

clientHeight= 0,

scrollHeight= 0,

clientWidth= 0,

scrollWidth= 0,

}= this.$refs.scrollEl;this.xScroll = round(scrollLeft * this.trackLeftMax / this.scrollLeftMax) || 0;this.yScroll = round(scrollTop * this.trackTopMax / this.scrollTopMax) || 0;this.triggerEvent(scrollLeft, scrollTop, scrollWidth, scrollHeight, clientWidth, clientHeight);

}return false;

},//------------------------------------------------------------------------------

//触发事件

triggerEvent(sLeft, sTop, sWidth, sHeight, cWidth, cHeight) {this.throttledScroll();if (this.xBarShow) {if (sLeft === 0) {this.throttleLeft();

}else if (sLeft + cWidth ===sWidth) {this.throttleRight();

}

}if (this.yBarShow) {if (sTop === 0) {this.throttleTop();

}else if (sTop + cHeight ===sHeight) {this.throttleBottom();

}

}

},//------------------------------------------------------------------------------

verticalHandler({ target, currentTarget, offsetY }) {if (target !== currentTarget) { return; }

const offset= offsetY - this.yBarHeight / 2;

const barTop= offset / this.containerHeight * 100;this.$refs.scrollEl.scrollTop = round(barTop * this.realHeight / 100);

},//------------------------------------------------------------------------------

horizontalHandler({ target, currentTarget, offsetX }) {if (target !== currentTarget) { return; }

const offset= offsetX - this.xBarWidth / 2;

const barLeft= offset / this.containerWidth * 100;this.$refs.scrollEl.scrollLeft = round(barLeft * this.realWidth / 100);

},//------------------------------------------------------------------------------

verticalBarHandler(e) {this.startDrag();this.prevPageY = this.yBarLastHeight -e.offsetY;

},//------------------------------------------------------------------------------

horizontalBarHandler(e) {this.startDrag();this.prevPageX = this.xBarLastWidth -e.offsetX;

},//------------------------------------------------------------------------------

startDrag() {this.cursorDown = true;

addClass(document.body,‘disable-selection‘);

document.addEventListener(‘mousemove‘, this.throttleMoving, false);

document.addEventListener(‘mouseup‘, this.mouseUpHandler, false);

document.onselectstart= () => false;

},//------------------------------------------------------------------------------

mouseUpHandler() {this.cursorDown = false;this.prevPageY = 0;this.prevPageX = 0;

removeClass(document.body,‘disable-selection‘);

document.removeEventListener(‘mousemove‘, this.throttleMoving);

document.removeEventListener(‘mouseup‘, this.mouseUpHandler);

document.onselectstart= null;

},//------------------------------------------------------------------------------

mouseMoveHandler({ clientY, clientX }) {

let offset;

let barPosition;if (this.yBarShow && this.prevPageY) {

offset= clientY - this.$refs.vertical.getBoundingClientRect().top;

barPosition= this.yBarLastHeight - this.prevPageY;

const top= this.scrollTopMax * (offset - barPosition) / this.trackTopMax;this.$refs.scrollEl.scrollTop =round(top);

}if (this.xBarShow && this.prevPageX) {

offset= clientX - this.$refs.horizontal.getBoundingClientRect().left;

barPosition= this.xBarLastWidth - this.prevPageX;

const left= this.scrollLeftMax * (offset - barPosition) / this.trackLeftMax;this.$refs.scrollEl.scrollLeft =round(left);

}

},//------------------------------------------------------------------------------

dftData() {this.throttledScroll = null;this.throttleLeft = null;this.throttleRight = null;this.throttleTop = null;this.throttleBottom = null;this.throttleMoving = throttle(this.mouseMoveHandler, 10);

},//------------------------------------------------------------------------------

//注册事件

initEvent() {

const opt= { trailing: false};this.turnOn(‘winResize‘, this.initail);this.throttleTop = throttle(() => this.$emit(‘top‘), 1000, opt);this.throttleLeft = throttle(() => this.$emit(‘left‘), 1000, opt);this.throttleRight = throttle(() => this.$emit(‘right‘), 1000, opt);this.throttleBottom = throttle(() => this.$emit(‘bottom‘), 1000, opt);this.throttledScroll = throttle(() => this.$emit(‘scroll‘), 1000, opt);

},//------------------------------------------------------------------------------

},

};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值