This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
bytedo
/
wcui
Archived
1
0
Fork 0

重构滚动条;markd组件

old
宇天 2021-05-06 19:55:38 +08:00
parent 3cf0ada1a5
commit 43b6faecbc
2 changed files with 165 additions and 234 deletions

View File

@ -1,5 +1,5 @@
<template> <template>
<slot /> <div class="container"></div>
</template> </template>
<style lang="scss"> <style lang="scss">
@ -9,9 +9,7 @@
color: var(--color-dark-1); color: var(--color-dark-1);
font-size: 14px; font-size: 14px;
} }
slot {
display: block;
}
a { a {
text-decoration: underline; text-decoration: underline;
color: var(--color-teal-2); color: var(--color-teal-2);
@ -197,20 +195,21 @@ export default class Markd {
__init__() { __init__() {
/* render */ /* render */
// var elem = this.root.children[1] var elem = this.root.children[1]
// this.__BOX__ = elem this.__BOX__ = elem
} }
__parse__() { __parse__() {
var txt = this.textContent.trim() this.md = this.textContent.trim()
this.value = txt
} }
set value(txt) { set md(txt) {
if (txt) { if (txt) {
this.innerHTML = markd(txt) this.__BOX__.innerHTML = markd(txt)
// this.textContent = '' } else {
this.__BOX__.innerHTML = ''
} }
this.dispatchEvent(new CustomEvent('ready', { bubbles: true }))
} }
clear() { clear() {
@ -221,29 +220,27 @@ export default class Markd {
mounted() { mounted() {
this.__parse__() this.__parse__()
// this._headClickFn = $.bind(this.__BOX__, 'click', ev => { this._headClickFn = $.bind(this.__BOX__, 'click', ev => {
// if (ev.target.className === 'md-head-link') { if (ev.target.className === 'md-head-link') {
// var ot = ev.target.offsetTop ev.target.scrollIntoView()
// console.log(ot) }
// document.documentElement.scrollTop = ot })
// }
// })
// this.__observer = new MutationObserver(_ => { this.__observer = new MutationObserver(_ => {
// this.__parse__() this.__parse__()
// }) })
// this.__observer.observe(this, { this.__observer.observe(this, {
// childList: true, childList: true,
// subtree: true, subtree: true,
// characterData: true characterData: true
// }) })
} }
unmount() { unmount() {
// $.unbind(this.__BOX__, 'click', this._headClickFn) $.unbind(this.__BOX__, 'click', this._headClickFn)
// this.__observer.disconnect() this.__observer.disconnect()
// this.clear() this.clear()
} }
} }
</script> </script>

View File

@ -1,17 +1,25 @@
<template> <template>
<div class="container"><slot /></div> <div class="container">
<div class="wrapper"><slot /></div>
<div class="is-horizontal"><span class="thumb"></span></div> <div class="is-horizontal"><span class="thumb"></span></div>
<div class="is-vertical"><span class="thumb"></span></div> <div class="is-vertical"><span class="thumb"></span></div>
</div>
</template> </template>
<style lang="scss"> <style lang="scss">
:host { :host {
overflow: hidden; display: block;
position: relative;
display: flex;
width: 100%; width: 100%;
height: 100%;
.container { .container {
overflow: hidden;
position: relative;
width: 100%;
height: 100%;
}
.wrapper {
overflow: auto; overflow: auto;
position: relative; position: relative;
width: calc(100% + 18px); width: calc(100% + 18px);
@ -49,7 +57,7 @@
.is-horizontal { .is-horizontal {
flex-direction: column; flex-direction: column;
left: 0; left: 0;
bottom: 1px; bottom: 0;
width: 100%; width: 100%;
height: 10px; height: 10px;
@ -66,7 +74,7 @@
/* 纵向 */ /* 纵向 */
.is-vertical { .is-vertical {
top: 0; top: 0;
right: 1px; right: 0;
width: 10px; width: 10px;
height: 100%; height: 100%;
@ -89,11 +97,26 @@
} }
:host([axis='x']) { :host([axis='x']) {
.wrapper {
overflow-y: hidden;
}
.is-vertical { .is-vertical {
display: none; display: none;
} }
} }
:host([axis='y']) { :host([axis='y']) {
.wrapper {
overflow-x: hidden;
}
.is-horizontal {
display: none;
}
}
:host([disabled]) {
.wrapper {
overflow: hidden;
}
.is-vertical,
.is-horizontal { .is-horizontal {
display: none; display: none;
} }
@ -108,37 +131,47 @@ const IS_FF = !!window.sidebar
/* */ /* */
export default class Scroll { export default class Scroll {
props = { props = {
thumbX: 0,
thumbY: 0,
disabled: false,
axis: 'xy', // 滚动方向, 默认x轴和y轴都可以滚动 axis: 'xy', // 滚动方向, 默认x轴和y轴都可以滚动
delay: 1000, // 节流延迟 delay: 1000, // 节流延迟
distance: 1 // 触发距离阀值, 单位像素 distance: 1 // 触发距离阀值, 单位像素
} }
state = {
width: 0, // 滚动组件的真实宽度
height: 0, // 滚动组件的真实高度
scrollX: 0, // 滚动组件的滚动宽度
scrollY: 0, // 滚动组件的滚动高度
xBar: 0, // 横轴长度
yBar: 0, // 纵轴长度
thumbX: 0, //横向条滚动距离
thumbY: 0 // 纵向条滚动距离
}
__init__() { __init__() {
/* render */ /* render */
this.__BOX__ = this.root.children[1] var elem = this.root.children[1]
this.__X__ = this.root.children[2].children[0] this.__BOX__ = elem.children[0]
this.__Y__ = this.root.children[3].children[0] this.__X__ = elem.children[1].children[0]
this.__Y__ = elem.children[2].children[0]
this.__last__ = 0 this.__last__ = 0
} }
// get scrollTop() { get scrollTop() {
// return this.__BOX__.scrollTop return this.__BOX__.scrollTop
// } }
// set scrollTop(n) { set scrollTop(n) {
// n = +n n = +n
// if (n === n) { if (n === n) {
// var { sh, oh, yh } = this.props // var { scrollY, height, yh } = this.props
// this.__BOX__.scrollTop = n this.__BOX__.scrollTop = n
// var fixedY = (this.__BOX__.scrollTop / (sh - oh)) * (oh - yh) // var fixedY = (this.__BOX__.scrollTop / (scrollY - height)) * (height - yh)
// this.props.thumbY = fixedY // this.props.thumbY = fixedY
// this.__Y__.style.transform = `translateY(${fixedY}px)` // this.__Y__.style.transform = `translateY(${fixedY}px)`
// } }
// } }
get scrollLeft() { get scrollLeft() {
return this.__BOX__.scrollLeft return this.__BOX__.scrollLeft
@ -147,13 +180,13 @@ export default class Scroll {
set scrollLeft(n) { set scrollLeft(n) {
n = +n n = +n
if (n === n) { if (n === n) {
var { sw, ow, xw } = this.props // var { scrollX, width, xw } = this.props
this.__BOX__.scrollLeft = n this.__BOX__.scrollLeft = n
var fixedX = (this.__BOX__.scrollLeft / (sw - ow)) * (ow - xw) // var fixedX = (this.__BOX__.scrollLeft / (scrollX - width)) * (width - xw)
this.props.thumbX = fixedX // this.props.thumbX = fixedX
this.__X__.style.transform = `translateX(${fixedX}px)` // this.__X__.style.transform = `translateX(${fixedX}px)`
} }
} }
@ -165,54 +198,35 @@ export default class Scroll {
return this.__BOX__.scrollWidth return this.__BOX__.scrollWidth
} }
get disabled() {
return this.props.disabled
}
set disabled(val) {
var type = typeof val
if (val === this.props.disabled) {
return
}
if ((type === 'boolean' && val) || type !== 'boolean') {
this.props.disabled = true
this.setAttribute('disabled', '')
} else {
this.props.disabled = false
this.removeAttribute('disabled')
}
}
_fetchScrollX(moveX) { _fetchScrollX(moveX) {
var { sw, ow, xw } = this.props var { scrollX, width, xw } = this.props
if (moveX < 0) { if (moveX < 0) {
moveX = 0 moveX = 0
} else if (moveX > ow - xw) { } else if (moveX > width - xw) {
moveX = ow - xw moveX = width - xw
} }
this.__BOX__.scrollLeft = (sw - ow) * (moveX / (ow - xw)) this.__BOX__.scrollLeft = (scrollX - width) * (moveX / (width - xw))
this.__X__.style.transform = `translateX(${moveX}px)` this.__X__.style.transform = `translateX(${moveX}px)`
return moveX return moveX
} }
_fetchScrollY(moveY) { _fetchScrollY(moveY) {
var { sh, oh, yh } = this.props var { scrollY, height, yh } = this.props
if (moveY < 0) { if (moveY < 0) {
moveY = 0 moveY = 0
} else if (moveY > oh - yh) { } else if (moveY > height - yh) {
moveY = oh - yh moveY = height - yh
} }
this.__BOX__.scrollTop = (sh - oh) * (moveY / (oh - yh)) this.__BOX__.scrollTop = (scrollY - height) * (moveY / (height - yh))
this.__Y__.style.transform = `translateY(${moveY}px)` this.__Y__.style.transform = `translateY(${moveY}px)`
return moveY return moveY
} }
_fireReachEnd(action = 'reach-bottom') { _fireReachEnd(action = 'reach-bottom') {
var { sh, oh, delay, disabled } = this.props var { scrollY, height, delay, disabled } = this.props
var top = this.__BOX__.scrollTop var top = this.__BOX__.scrollTop
var now = Date.now() var now = Date.now()
@ -221,7 +235,7 @@ export default class Scroll {
} }
if (now - this.__last__ > delay) { if (now - this.__last__ > delay) {
if (action === 'reach-bottom') { if (action === 'reach-bottom') {
if (oh + top < sh) { if (height + top < scrollY) {
return return
} }
} else { } else {
@ -236,166 +250,113 @@ export default class Scroll {
} }
mounted() { mounted() {
/* // 初始化滚动条的位置和长度 // 初始化滚动条的位置和长度
this._initFn = $.bind(this.__BOX__, 'mouseenter', ev => { this._initFn = $.catch(this, 'ready', ev => {
// 禁用状态, 不允许滚动 // 需要减去因为隐藏原生滚动条修正的18像素
if (this.disabled) { var width = this.__BOX__.clientWidth - 18
return var height = this.__BOX__.clientHeight - 18
} var scrollX = this.__BOX__.scrollWidth - 18
var ow = this.__BOX__.offsetWidth var scrollY = this.__BOX__.scrollHeight - 18
var sw = this.__BOX__.scrollWidth
var oh = this.__BOX__.offsetHeight
var sh = this.__BOX__.scrollHeight
var yh = ((oh * oh) / sh) >> 0 // 滚动条的高度 var yBar = (height * (height / scrollY)) >> 0 // 滚动条的高度
var xw = ((ow * ow) / sw) >> 0 // 滚动条的宽度 var xBar = (width * (width / scrollX)) >> 0 // 滚动条的宽度
if (yh < 50) { if (yBar < 50) {
yh = 50 yBar = 50
} }
if (xw < 50) { if (xBar < 50) {
xw = 50 xBar = 50
} }
// 100%或主体高度比滚动条还短时不显示 // 100%或主体高度比滚动条还短时不显示
if (xw >= ow) { if (xBar >= width) {
xw = 0 xBar = 0
} }
if (yh >= oh) { if (yBar >= height) {
yh = 0 yBar = 0
} }
this.props.oh = oh this.state.height = height
this.props.sh = sh this.state.width = width
this.props.ow = ow this.state.scrollY = scrollY
this.props.sw = sw this.state.scrollX = scrollX
this.props.yh = yh this.state.yBar = yBar
this.props.xw = xw this.state.xBar = xBar
this.__X__.style.width = xw + 'px' this.__X__.style.width = xBar + 'px'
this.__Y__.style.height = yh + 'px' this.__Y__.style.height = yBar + 'px'
this._active = true
})
this._inactiveFn = $.bind(this.__BOX__, 'mouseleave', ev => {
delete this._active
}) })
// 鼠标滚动事件 // 鼠标滚动事件
this._wheelFn = $.bind(this.__BOX__, 'wheel', ev => { this._wheelFn = $.bind(this.__BOX__, 'scroll', ev => {
// 禁用状态, 不允许滚动 // 禁用状态, 不允许滚动
if (this.disabled) { if (this.disabled) {
return return
} }
var { sh, oh, yh, sw, ow, xw } = this.props var { axis } = this.props
var {
xBar,
yBar,
thumbX,
thumbY,
scrollY,
scrollX,
width,
height
} = this.state
var currTop = this.__BOX__.scrollTop
var currLeft = this.__BOX__.scrollLeft
var now = Date.now()
// x轴 y轴 都为0时, 不作任何处理 // x轴 y轴 都为0时, 不作任何处理
if (!xw && !yh) { if (xBar === 0 && yBar === 0) {
return return
} }
//校正兼容苹果鼠标在 chrome和FF下滚动的精度
var deltaX
var deltaY
var now = Date.now()
if (!this.stamp || now - this.stamp > 800) {
this.stamp = now
this.times = 1
}
if (IS_FF) {
// 区分是触摸板还是普通鼠标
deltaX = ev.deltaMode ? 10 * ev.deltaX : ev.deltaX
deltaY = ev.deltaMode ? 10 * ev.deltaY : ev.deltaY
} else {
var delta = Math.abs(ev.wheelDelta)
// 精度高的(小于120), 一般是触摸板或苹果的鼠标
if (delta < 120) {
deltaX = ev.deltaX
deltaY = ev.deltaY
} else {
deltaX = ev.deltaX / (delta / 120)
deltaY = ev.deltaY / (delta / 120)
}
}
if (now - this.stamp < 20) {
this.times += 0.05
if (this.times > 3) {
this.times = 3
}
}
deltaX *= this.times
deltaY *= this.times
// //
if (this.props.axis !== 'x') { if (axis === 'y' || axis === 'xy') {
this.__BOX__.scrollTop += deltaY if (yBar) {
if (yh) {
// 修正滚动条的位置 // 修正滚动条的位置
// 滚动比例 y 滚动条的可移动距离 // 滚动比例 y 滚动条的可移动距离
var fixedY = (this.__BOX__.scrollTop / (sh - oh)) * (oh - yh) let fixedY = (currTop / (scrollY - height)) * (height - yBar)
fixedY = fixedY >> 0 fixedY = fixedY >> 0
if ( if ((fixedY === 0 || height - yBar === fixedY) && fixedY === thumbY) {
(fixedY === 0 || oh - yh === fixedY) &&
fixedY === this.props.thumbY
) {
return return
} }
ev.preventDefault() this.state.thumbY = fixedY
ev.stopPropagation()
this.props.thumbY = fixedY
this.__Y__.style.transform = `translateY(${fixedY}px)` this.__Y__.style.transform = `translateY(${fixedY}px)`
if (Math.abs(deltaY) > this.props.distance) { if (Math.abs(fixedY - thumbY) > this.props.distance) {
this._fireReachEnd(deltaY > 0 ? 'reach-bottom' : 'reach-top') this._fireReachEnd(fixedY > thumbY ? 'reach-bottom' : 'reach-top')
} }
} }
} }
if (this.props.axis !== 'y') { if (axis === 'x' || axis === 'xy') {
this.__BOX__.scrollLeft += deltaX if (xBar) {
if (xw) {
// 修正滚动条的位置 // 修正滚动条的位置
// 滚动比例 x 滚动条的可移动距离 // 滚动比例 x 滚动条的可移动距离
var fixedX = (this.__BOX__.scrollLeft / (sw - ow)) * (ow - xw) let fixedX = (currLeft / (scrollX - width)) * (width - xBar)
fixedX = fixedX >> 0 fixedX = fixedX >> 0
if ( if ((fixedX === 0 || width - xBar === fixedX) && fixedX === thumbX) {
(fixedX === 0 || ow - xw === fixedX) &&
fixedX === this.props.thumbX
) {
return return
} }
ev.preventDefault() this.state.thumbX = fixedX
ev.stopPropagation()
this.props.thumbX = fixedX
this.__X__.style.transform = `translateX(${fixedX}px)` this.__X__.style.transform = `translateX(${fixedX}px)`
} }
} }
this._active = true
this.stamp = now this.stamp = now
this.dispatchEvent( this.dispatchEvent(new CustomEvent('scroll'))
new CustomEvent('scroll', {
detail: { x: this.props.thumbX, y: this.props.thumbY }
})
)
}) })
var startX, /* var startX,
startY, startY,
moveX, moveX,
moveY, moveY,
@ -448,40 +409,14 @@ export default class Scroll {
$.bind(document, 'mouseup', mouseupFn) $.bind(document, 'mouseup', mouseupFn)
}) })
$.catch(document, 'keydown', ev => { */
if (this._active) {
var { oh, sh } = this.props
var exec = false
switch (ev.keyCode) {
case 33: // pageUp
exec = true
this.scrollTop -= oh
break
case 34: // pageDown
exec = true
this.scrollTop += oh
break
case 35: // End
exec = true
this.scrollTop = sh
break
case 36: // Home
exec = true
this.scrollTop = 0
break
}
if (exec) {
ev.preventDefault()
}
}
})
this.__observer = new MutationObserver(this._initFn) // this.__observer = new MutationObserver(this._initFn)
this.__observer.observe(this, { // this.__observer.observe(this, {
childList: true, // childList: true,
subtree: true, // subtree: true,
characterData: true // attributeFilter: ['style']
}) */ // })
} }
unmount() { unmount() {
@ -495,14 +430,13 @@ export default class Scroll {
watch() { watch() {
switch (name) { switch (name) {
case 'axis': case 'axis':
this.props.axis = val this.props.axis = val || 'xy'
break
case 'disabled':
this[name] = true
break break
case 'delay': case 'delay':
this.props.delay = +val || 1000 this.props.delay = +val || 1000
break break
case 'distance': case 'distance':
this.props.distance = +val || 1 this.props.distance = +val || 1
break break