ui/src/scroll/index.js

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')
百搭WCUI组件库, 基于web components开发。面向下一代的UI组件库
JavaScript 98.9%
CSS 1.1%