简要说明:依赖于angular6框架,也可以延伸到其他框架。
scroll-bar.directive.ts
import { Directive, HostBinding, ElementRef, Host, HostListener } from '@angular/core';
import { hostElement } from '@angular/core/src/render3/instructions';
import { Overlay } from '@angular/cdk/overlay';
@Directive({
selector: '[appScrollBar]',
})
export class ScrollBarDirective {
target: ElementRef;
scrollContainer: HTMLDivElement = document.createElement('div');
scrollBar: HTMLDivElement = document.createElement('div');
inited = false;
visible = false;
scale = 1;
triggerClick = false;
clientHeight = 0;
scrollHieght = 0;
scrollBarWidth = 0;
dragLength = 0;
preTick = 0;
timer = 0;
step = 100; // 定义拖拽的速度,如果设置的过小,会一定概率出现误差
@HostBinding('style.overflow-y') OverlayY = 'hidden';
@HostBinding('style.margin-right') marginRight = '0';
@HostListener('mouseenter') initScrollBar(e) {
if (!this.inited) {
this.initEvent();
this.initScrollHeight();
this.append();
this.inited = true;
}
this.scrollContainer.style.visibility = 'visible';
}
@HostListener('mouseleave') hiddenScroll() {
this.scrollContainer.style.visibility = 'hidden';
}
constructor(private e: ElementRef) {
this.scrollBarWidth = this.getScrollbarWidth();
this.target = e;
this.scrollContainer.setAttribute('class', 'scroll-container');
this.scrollBar.setAttribute('class', 'scroll-bar');
}
initScrollHeight() {
const ele = this.target.nativeElement, clientHeight = this.clientHeight = ele.clientHeight,
scrollHeight = this.scrollHieght = ele.scrollHeight;
this.scale = clientHeight / scrollHeight;
if (this.scale >= 1) {
this.OverlayY = 'hidden';
this.hiddenScroll();
} else {
this.marginRight = `-${this.scrollBarWidth}px`;
this.OverlayY = 'scroll';
}
ele.style.position = 'relative';
this.scrollBar.style.height = clientHeight * this.scale + 'px';
this.scrollContainer.style.height = clientHeight * 1 + 'px';
}
append() {
this.scrollContainer.appendChild(this.scrollBar);
this.target.nativeElement.appendChild(this.scrollContainer);
}
initEvent() {
window.onresize = () => {
this.inited = false;
};
const ele = this.target.nativeElement;
this.scrollBar.onmouseup = this.removeTrigger;
this.scrollBar.onmouseleave = this.removeTrigger;
this.target.nativeElement.onscroll = (e) => {
if(ele.scrollHeight !== this.scrollHieght) {
this.inited = false;
this.initScrollHeight();
}
this.scrollContainer.style.top = ele.scrollTop * 1 + 'px';
this.scrollBar.style.top = ele.scrollTop * this.scale + 'px';
};
this.scrollBar.draggable = true;
this.scrollBar.ondragstart = (e) => {
this.triggerClick = true;
this.preTick = e.clientY || e.y;
};
this.scrollBar.ondrag = (e) => {
if (Date.now() - this.timer < 15) {
return false;
}
const height = e.clientY || e.y;
if (height === 0 && this.preTick > this.step) {
console.log(this.preTick);
return false;
}
this.dragLength = height - this.preTick;
this.timer = Date.now();
ele.scrollTop += this.dragLength / this.scale;
this.preTick = height;
};
this.scrollBar.ondragover = (e) => {
const height = e.clientY || e.y;
this.dragLength = height - this.preTick;
ele.scrollTop += this.dragLength / this.scale;
e.preventDefault();
};
}
removeTrigger = (e) => {
this.triggerClick = false;
}
getScrollbarWidth() {
const div: ElementDiv = document.createElement('div'),
styles = {
width: '100px',
height: '100px',
overflowY: 'scroll'
};
let scrollbarWidth;
for (const i in styles) {
if (i) {
div.style[i] = styles[i];
}
}
document.body.appendChild(div);
scrollbarWidth = div.offsetWidth - div.clientWidth;
try {
div.remove();
} catch (error) {
div.removeNode(true);
}
return scrollbarWidth;
}
}
interface ElementDiv extends HTMLElement {
removeNode?: (e) => {};
}
[appScrollBar] {
position: relative;
overflow-x: hidden;
overflow-y: hidden;
.scroll-container {
position: absolute;
width: 8px;
border-radius: 3px;
background: rgba($color: #000000, $alpha: 0.2);
top: 0px;
right: 0px;
z-index: 999;
.scroll-bar {
position: absolute;
width: 8px;
border-radius: 3px;
background: rgba(77, 119, 119, 0.8);
top: 0px;
right: 0px;
z-index: 1000;
cursor: pointer;
}
}
}
app.moddule.ts
三:简单效果图