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);
},//------------------------------------------------------------------------------
},
};