263 lines
6.5 KiB
JavaScript
263 lines
6.5 KiB
JavaScript
/**
|
|
* {滚动条组件}
|
|
* @author chensbox <chensbox@foxmail.com>
|
|
* @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`
|
|
<div ref="scroller" class="scroller">
|
|
<slot ref="slot"></slot>
|
|
</div>
|
|
<div ref="vertical" class="scroll-bar vertical"></div>
|
|
<div ref="horizon" class="scroll-bar horizon"></div>
|
|
`
|
|
}
|
|
|
|
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')
|
JavaScript
98.9%
CSS
1.1%