/** * {滚动条组件} * @author chensbox * @date 2023/03/20 15:17:25 */ import { css, bind, html, unbind, Component } from '@bd/core' class Scroll extends Component { static props = { axis: 'xy', distance: 0 } dragging = false hovering = false static styles = [ css` :host { position: relative; overflow: hidden; display: block; } .scroller { position: relative; height: 100%; width: 100%; overflow: auto; &::-webkit-scrollbar { display: none; } scrollbar-width: none; //火狐专属 } .scroll-bar { position: absolute; background: #909399; width: 0; height: 0; opacity: 0; border-radius: 4px; cursor: pointer; transition: opacity ease-in-out 0.2s; &.vertical { width: 8px; top: 0; right: 0; } &.horizon { height: 8px; bottom: 0; left: 0; } &:hover { opacity: 0.5 !important; } } :host([disabled]) { overflow: hidden !important; .scroll-bar { visibility: hidden; } } :host([axis='x']) { overflow-x: auto; overflow-y: hidden; .vertical { display: none; } } :host([axis='y']) { overflow-y: auto; overflow-x: hidden; .horizon { display: none; } } ` ] get scrollTop() { return this.$refs.scroller.scrollTop } set scrollTop(val) { this.$refs.scroller.scrollTop = val } get scrollHeight() { return this.$refs.scroller.scrollHeight } set scrollHeight(val) { this.$refs.scroller.scrollHeight = val } get scrollLeft() { return this.$refs.scroller.scrollLeft } set scrollLeft(val) { this.$refs.scroller.scrollLeft = val } get scrollWidth() { return this.$refs.scroller.scrollWidth } set scrollWidth(val) { this.$refs.scroller.scrollWidth = val } render() { return html`
` } onmouseenter() { this.hovering = true this.$refs.vertical.style.opacity = 0.3 this.$refs.horizon.style.opacity = 0.3 } onmouseleave() { this.hovering = false if (!this.dragging) { this.$refs.vertical.style.opacity = 0 this.$refs.horizon.style.opacity = 0 } } onmousedown(e) { this.dragging = true const { clientHeight, scrollHeight, clientWidth, scrollWidth, verticalHeight, horizonWidth } = this let start, cur const isVerticalScroll = e.target === this.$refs.vertical if (isVerticalScroll) { start = e.clientY cur = this.scrollTop } else { start = e.clientX cur = this.scrollLeft } const onmousemove = bind(document, 'mousemove', e => { let dif, rang, progress if (isVerticalScroll) { dif = e.clientY - start rang = clientHeight - verticalHeight progress = dif / rang this.scrollTop = cur + progress * (scrollHeight - clientHeight) } else { dif = e.clientX - start rang = clientWidth - horizonWidth progress = dif / rang this.scrollLeft = cur + progress * (scrollWidth - clientWidth) } }) this.onmouseup = bind(document, 'mouseup', () => { this.dragging = false if (!this.hovering) { this.$refs.vertical.style.opacity = 0 this.$refs.horizon.style.opacity = 0 } unbind(document, 'mousemove', onmousemove) }) } onscroll(ev) { const { distance, scrollTop, scrollLeft, clientHeight, scrollHeight, clientWidth, scrollWidth, verticalHeight, horizonWidth } = this const { vertical, horizon } = this.$refs const verticalProgress = scrollTop / (scrollHeight - clientHeight) || 0 const horizonProgress = scrollLeft / (scrollWidth - clientWidth) || 0 vertical.style.transform = `translateY(${ (clientHeight - verticalHeight) * verticalProgress }px)` horizon.style.transform = `translateX(${ (clientWidth - horizonWidth) * horizonProgress }px)` if (!ev) { //事件对象不存在,则是手动调用的 不触发事件。 return } if (this.verticalHeight) { if (scrollHeight - scrollTop - clientHeight <= distance) { this.$emit('reach-bottom') } if (scrollTop === 0) { this.$emit('reach-top') } } } initScrollBar() { const { axis, clientHeight, scrollHeight, clientWidth, scrollWidth } = this const { vertical, horizon } = this.$refs // console.log(this.$refs) if (clientHeight !== scrollHeight) { this.verticalHeight = (clientHeight / scrollHeight) * clientHeight } else { this.verticalHeight = 0 } if (clientWidth !== scrollWidth) { this.horizonWidth = (clientWidth / scrollWidth) * clientWidth } else { this.horizonWidth = 0 } if (axis.includes('x')) { horizon.style.width = this.horizonWidth + 'px' bind(horizon, 'mousedown', this.onmousedown.bind(this)) } if (axis.includes('y')) { vertical.style.height = this.verticalHeight + 'px' bind(vertical, 'mousedown', this.onmousedown.bind(this)) } } #watchResize() { const slotList = this.$refs.slot.assignedNodes() if (slotList) { slotList.forEach(element => { if (element.nodeType === 1) { this.resizeObserver.observe(element) } }) } } mounted() { this.$on('mouseenter', this.onmouseenter) this.$on('mouseleave', this.onmouseleave) this.onscroll = bind( this.$refs.scroller, 'scroll', this.onscroll.bind(this) ) this.resizeObserver = new ResizeObserver(() => { this.initScrollBar() this.onscroll() }) this.#watchResize() this.$refs.slot.addEventListener('slotchange', () => this.#watchResize()) } unmounted() { this.resizeObserver.disconnect() unbind(document, 'mouseup', this.onmouseup) this.$of('mouseenter', this.onmouseenter) this.$of('mouseleave', this.onmouseleave) this.$of('scroll', this.onscroll) unbind(this.$refs.vertical, 'mousedown', this.onmousedown) unbind(this.$refs.horizon, 'mousedown', this.onmousedown) } } Scroll.reg('scroll')