feat: add scrollbar

This commit is contained in:
zazzaz
2020-08-11 16:24:42 +08:00
committed by jeremywu
parent 3647512e09
commit f9626ec815
2 changed files with 138 additions and 150 deletions

View File

@@ -1,6 +1,6 @@
import { on, off } from '@element-plus/utils/dom'
import { renderThumbStyle, BAR_MAP } from './util'
import { h } from 'vue'
import { h, computed, ref, getCurrentInstance, onUnmounted, inject, Ref } from 'vue'
export default {
name: 'Bar',
@@ -11,83 +11,76 @@ export default {
move: Number,
},
computed: {
bar() {
return BAR_MAP[this.vertical ? 'vertical' : 'horizontal']
},
wrap() {
return this.$parent.wrap
},
},
render() {
const { size, move, bar } = this
return h('div', {
class: ['el-scrollbar__bar', 'is-' + bar.key],
onMousedown: this.clickTrackHandler,
}, h('div', {
ref: 'thumb',
class: 'el-scrollbar__thumb',
onMousedown: this.clickThumbHandler,
style: renderThumbStyle({
size,
move,
bar,
}),
}))
},
methods: {
clickThumbHandler(e) {
setup(props) {
const instance = getCurrentInstance()
const thumb = ref(null)
const wrap = inject('scroll-bar-wrap', {}) as Ref
const bar = computed(() => {
return BAR_MAP[props.vertical ? 'vertical' : 'horizontal']
})
const barStore = ref({})
const cursorDown = ref(null)
const clickThumbHandler= (e) => {
// prevent click event of right button
if (e.ctrlKey || e.button === 2) {
return
}
this.startDrag(e)
this[this.bar.axis] = (e.currentTarget[this.bar.offset] - (e[this.bar.client] - e.currentTarget.getBoundingClientRect()[this.bar.direction]))
},
startDrag(e)
barStore.value[bar.value.axis] = (e.currentTarget[bar.value.offset] - (e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction]))
}
clickTrackHandler(e) {
const offset = Math.abs(e.target.getBoundingClientRect()[this.bar.direction] - e[this.bar.client])
const thumbHalf = (this.$refs.thumb[this.bar.offset] / 2)
const thumbPositionPercentage = ((offset - thumbHalf) * 100 / this.$el[this.bar.offset])
const clickTrackHandler = (e) => {
const offset = Math.abs(e.target.getBoundingClientRect()[bar.value.direction] - e[bar.value.client])
const thumbHalf = (thumb.value[bar.value.offset] / 2)
const thumbPositionPercentage = ((offset - thumbHalf) * 100 / instance.vnode.el[bar.value.offset])
this.wrap[this.bar.scroll] = (thumbPositionPercentage * this.wrap[this.bar.scrollSize] / 100)
},
startDrag(e) {
wrap.value[bar.value.scroll] = (thumbPositionPercentage * wrap.value[bar.value.scrollSize] / 100)
}
const startDrag = (e) =>{
e.stopImmediatePropagation()
this.cursorDown = true
on(document, 'mousemove', this.mouseMoveDocumentHandler)
on(document, 'mouseup', this.mouseUpDocumentHandler)
cursorDown.value = true
on(document, 'mousemove', mouseMoveDocumentHandler)
on(document, 'mouseup', mouseUpDocumentHandler)
document.onselectstart = () => false
},
}
mouseMoveDocumentHandler(e) {
if (this.cursorDown === false) return
const prevPage = this[this.bar.axis]
const mouseMoveDocumentHandler = (e) => {
if (cursorDown.value === false) return
const prevPage = barStore.value[bar.value.axis]
if (!prevPage) return
const offset = ((this.$el.getBoundingClientRect()[this.bar.direction] - e[this.bar.client]) * -1)
const thumbClickPosition = (this.$refs.thumb[this.bar.offset] - prevPage)
const thumbPositionPercentage = ((offset - thumbClickPosition) * 100 / this.$el[this.bar.offset])
const offset = ((instance.vnode.el.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) * -1)
const thumbClickPosition = (thumb.value[bar.value.offset] - prevPage)
const thumbPositionPercentage = ((offset - thumbClickPosition) * 100 / instance.vnode.el[bar.value.offset])
wrap.value[bar.value.scroll] = (thumbPositionPercentage * wrap.value[bar.value.scrollSize] / 100)
}
this.wrap[this.bar.scroll] = (thumbPositionPercentage * this.wrap[this.bar.scrollSize] / 100)
},
mouseUpDocumentHandler() {
this.cursorDown = false
this[this.bar.axis] = 0
off(document, 'mousemove', this.mouseMoveDocumentHandler)
function mouseUpDocumentHandler(e) {
cursorDown.value = false
barStore.value[bar.value.axis] = 0
off(document, 'mousemove', mouseMoveDocumentHandler)
document.onselectstart = null
},
},
}
destroyed() {
off(document, 'mouseup', this.mouseUpDocumentHandler)
onUnmounted(() => {
off(document, 'mouseup', mouseUpDocumentHandler)
})
return () => h('div', {
class: ['el-scrollbar__bar', 'is-' + bar.value.key],
onMousedown: clickTrackHandler,
},
h('div', {
ref: thumb,
class: 'el-scrollbar__thumb',
onMousedown: clickThumbHandler,
style: renderThumbStyle({
size: props.size,
move: props.move,
bar: bar.value,
}),
}),
)
},
}

View File

@@ -2,7 +2,7 @@
import { addResizeListener, removeResizeListener } from '@element-plus/utils/resize-event'
import scrollbarWidth from '@element-plus/utils/scrollbar-width'
import { toObject } from '@element-plus/utils/util'
import { h } from 'vue'
import { h, ref, onMounted, onBeforeUnmount, nextTick, provide } from 'vue'
import Bar from './bar'
export default {
@@ -23,98 +23,93 @@ export default {
},
},
data() {
return {
sizeWidth: '0',
sizeHeight: '0',
moveX: 0,
moveY: 0,
setup(props, ctx) {
const sizeWidth = ref('0')
const sizeHeight = ref('0')
const moveX = ref(0)
const moveY = ref(0)
const wrap = ref(null)
const resize = ref(null)
provide('scroll-bar-wrap', wrap)
const handleScroll = () => {
moveY.value = ((wrap.value.scrollTop * 100) / wrap.value.clientHeight)
moveX.value = ((wrap.value.scrollLeft * 100) / wrap.value.clientWidth)
}
},
computed: {
wrap() {
return this.$refs.wrap
},
},
const update = () => {
if (!wrap.value) return
render() {
const gutter = scrollbarWidth()
let style = this.wrapStyle
const heightPercentage = (wrap.value.clientHeight * 100 / wrap.value.scrollHeight)
const widthPercentage = (wrap.value.clientWidth * 100 / wrap.value.scrollWidth)
if (gutter) {
const gutterWith = `-${gutter}px`
const gutterStyle = `margin-bottom: ${gutterWith}; margin-right: ${gutterWith};`
sizeHeight.value = (heightPercentage < 100) ? (heightPercentage + '%') : ''
sizeWidth.value = (widthPercentage < 100) ? (widthPercentage + '%') : ''
}
if (Array.isArray(this.wrapStyle)) {
style = toObject(this.wrapStyle)
style.marginRight = style.marginBottom = gutterWith
} else if (typeof this.wrapStyle === 'string') {
style += gutterStyle
} else {
style = gutterStyle
onMounted(() => {
if (props.native) return
nextTick(update)
!props.noresize && addResizeListener(resize.value, update)
})
onBeforeUnmount(() => {
if (props.native) return
!props.noresize && removeResizeListener(resize.value, update)
})
return () => {
const gutter = scrollbarWidth()
let style = props.wrapStyle
if (gutter) {
const gutterWith = `-${gutter}px`
const gutterStyle = `margin-bottom: ${gutterWith}; margin-right: ${gutterWith};`
if (Array.isArray(props.wrapStyle)) {
style = toObject(props.wrapStyle)
style.marginRight = style.marginBottom = gutterWith
} else if (typeof props.wrapStyle === 'string') {
style += gutterStyle
} else {
style = gutterStyle
}
}
}
const view = h(this.tag, {
class: ['el-scrollbar__view', this.viewClass],
style: this.viewStyle,
ref: 'resize',
}, this.$slots.default())
const wrap = h('div', {
ref: 'wrap',
style: style,
onScroll: this.handleScroll,
class: [this.wrapClass, 'el-scrollbar__wrap', gutter ? '' : 'el-scrollbar__wrap--hidden-default'],
}, [view])
let nodes
if (!this.native) {
nodes = [wrap, h(Bar, {
move: this.moveX,
size: this.sizeWidth,
}), h(Bar, {
vertical: true,
move: this.moveY,
size: this.sizeHeight,
})]
} else {
nodes = h('div', {
ref: 'wrap',
class: [this.wrapClass, 'el-scrollbar__wrap'],
style: style,
const view = h(props.tag, {
class: ['el-scrollbar__view', props.viewClass],
style: props.viewStyle,
ref: resize,
}, ctx.slots.default())
const _wrap = h('div', {
ref: wrap,
style,
onScroll: handleScroll,
class: [props.wrapClass, 'el-scrollbar__wrap', gutter ? '' : 'el-scrollbar__wrap--hidden-default'],
}, [view])
let nodes
if (!props.native) {
nodes = [_wrap,h(Bar,{
move: moveX.value,
size: sizeWidth.value,
}),h(Bar,{
vertical: true,
move: moveY.value,
size: sizeHeight.value,
}),
]
} else {
nodes = [
h('div',{
ref: wrap,
class: [props.wrapClass, 'el-scrollbar__wrap'],
style,
}, [view]),
]
}
return h('div', { class: 'el-scrollbar' }, nodes)
}
return h('div', { class: 'el-scrollbar' }, nodes)
},
methods: {
handleScroll() {
const wrap = this.wrap
this.moveY = ((wrap.scrollTop * 100) / wrap.clientHeight)
this.moveX = ((wrap.scrollLeft * 100) / wrap.clientWidth)
},
update() {
const wrap = this.wrap
if (!wrap) return
const heightPercentage = (wrap.clientHeight * 100 / wrap.scrollHeight)
const widthPercentage = (wrap.clientWidth * 100 / wrap.scrollWidth)
this.sizeHeight = (heightPercentage < 100) ? (heightPercentage + '%') : ''
this.sizeWidth = (widthPercentage < 100) ? (widthPercentage + '%') : ''
},
},
mounted() {
if (this.native) return
this.$nextTick(this.update)
!this.noresize && addResizeListener(this.$refs.resize, this.update)
},
beforeDestroy() {
if (this.native) return
!this.noresize && removeResizeListener(this.$refs.resize, this.update)
},
}