mirror of
https://github.com/element-plus/element-plus.git
synced 2024-12-21 02:50:11 +08:00
perf(scrollbar): when scrollbar isn't shown, hide the bar (#1342)
* perf(scrollbar): when scrollbar isn't shown, hide the bar * fix: fix right click bar will return top * perf: store and restore onselectstart * perf: update
This commit is contained in:
parent
08421fab23
commit
903c05a848
@ -1,86 +0,0 @@
|
||||
import { defineComponent, h, computed, ref, getCurrentInstance, onUnmounted, inject, Ref } from 'vue'
|
||||
import { on, off } from '@element-plus/utils/dom'
|
||||
import { renderThumbStyle, BAR_MAP } from './util'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Bar',
|
||||
|
||||
props: {
|
||||
vertical: Boolean,
|
||||
size: String,
|
||||
move: Number,
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
const instance = getCurrentInstance()
|
||||
const thumb = ref(null)
|
||||
const wrap = inject('scroll-bar-wrap', {} as Ref<Nullable<HTMLElement>>)
|
||||
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
|
||||
}
|
||||
startDrag(e)
|
||||
barStore.value[bar.value.axis] = (e.currentTarget[bar.value.offset] - (e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction]))
|
||||
}
|
||||
|
||||
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])
|
||||
|
||||
wrap.value[bar.value.scroll] = (thumbPositionPercentage * wrap.value[bar.value.scrollSize] / 100)
|
||||
}
|
||||
const startDrag = e => {
|
||||
e.stopImmediatePropagation()
|
||||
cursorDown.value = true
|
||||
on(document, 'mousemove', mouseMoveDocumentHandler)
|
||||
on(document, 'mouseup', mouseUpDocumentHandler)
|
||||
document.onselectstart = () => false
|
||||
}
|
||||
|
||||
const mouseMoveDocumentHandler = e => {
|
||||
if (cursorDown.value === false) return
|
||||
const prevPage = barStore.value[bar.value.axis]
|
||||
|
||||
if (!prevPage) return
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
function mouseUpDocumentHandler() {
|
||||
cursorDown.value = false
|
||||
barStore.value[bar.value.axis] = 0
|
||||
off(document, 'mousemove', mouseMoveDocumentHandler)
|
||||
document.onselectstart = null
|
||||
}
|
||||
|
||||
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,
|
||||
}),
|
||||
}),
|
||||
)
|
||||
},
|
||||
})
|
125
packages/scrollbar/src/bar.vue
Normal file
125
packages/scrollbar/src/bar.vue
Normal file
@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<transition name="el-scrollbar-fade">
|
||||
<div
|
||||
v-show="visible"
|
||||
ref="instance"
|
||||
:class="['el-scrollbar__bar', 'is-' + bar.key]"
|
||||
@mousedown="clickTrackHandler"
|
||||
>
|
||||
<div
|
||||
ref="thumb"
|
||||
class="el-scrollbar__thumb"
|
||||
:style="thumbStyle"
|
||||
@mousedown="clickThumbHandler"
|
||||
></div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, inject, onBeforeUnmount, onMounted, ref, Ref } from 'vue'
|
||||
import { off, on } from '@element-plus/utils/dom'
|
||||
import { BAR_MAP, renderThumbStyle } from './util'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Bar',
|
||||
props: {
|
||||
vertical: Boolean,
|
||||
size: String,
|
||||
move: Number,
|
||||
},
|
||||
setup(props) {
|
||||
const instance = ref(null)
|
||||
const thumb = ref(null)
|
||||
const scrollbar = inject('scrollbar', {} as Ref<Nullable<HTMLElement>>)
|
||||
const wrap = inject('scrollbar-wrap', {} as Ref<Nullable<HTMLElement>>)
|
||||
const bar = computed(() => BAR_MAP[props.vertical ? 'vertical' : 'horizontal'])
|
||||
const barStore = ref({})
|
||||
const cursorDown = ref(null)
|
||||
const visible = ref(false)
|
||||
let onselectstartStore = null
|
||||
|
||||
const clickThumbHandler = e => {
|
||||
// prevent click event of middle and right button
|
||||
e.stopPropagation()
|
||||
if (e.ctrlKey || [1, 2].includes(e.button)) {
|
||||
return
|
||||
}
|
||||
startDrag(e)
|
||||
barStore.value[bar.value.axis] = (e.currentTarget[bar.value.offset] - (e[bar.value.client] - e.currentTarget.getBoundingClientRect()[bar.value.direction]))
|
||||
}
|
||||
|
||||
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.value[bar.value.offset])
|
||||
|
||||
wrap.value[bar.value.scroll] = (thumbPositionPercentage * wrap.value[bar.value.scrollSize] / 100)
|
||||
}
|
||||
|
||||
const startDrag = e => {
|
||||
e.stopImmediatePropagation()
|
||||
cursorDown.value = true
|
||||
on(document, 'mousemove', mouseMoveDocumentHandler)
|
||||
on(document, 'mouseup', mouseUpDocumentHandler)
|
||||
onselectstartStore = document.onselectstart
|
||||
document.onselectstart = () => false
|
||||
}
|
||||
|
||||
const mouseMoveDocumentHandler = e => {
|
||||
if (cursorDown.value === false) return
|
||||
const prevPage = barStore.value[bar.value.axis]
|
||||
|
||||
if (!prevPage) return
|
||||
|
||||
const offset = ((instance.value.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]) * -1)
|
||||
const thumbClickPosition = (thumb.value[bar.value.offset] - prevPage)
|
||||
const thumbPositionPercentage = ((offset - thumbClickPosition) * 100 / instance.value[bar.value.offset])
|
||||
wrap.value[bar.value.scroll] = (thumbPositionPercentage * wrap.value[bar.value.scrollSize] / 100)
|
||||
}
|
||||
|
||||
const mouseUpDocumentHandler = () => {
|
||||
cursorDown.value = false
|
||||
barStore.value[bar.value.axis] = 0
|
||||
off(document, 'mousemove', mouseMoveDocumentHandler)
|
||||
document.onselectstart = onselectstartStore
|
||||
}
|
||||
|
||||
const thumbStyle = computed(() => renderThumbStyle({
|
||||
size: props.size,
|
||||
move: props.move,
|
||||
bar: bar.value,
|
||||
}))
|
||||
|
||||
const showBar = () => {
|
||||
visible.value = !!props.size
|
||||
}
|
||||
|
||||
const hideBar = () => {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
on(scrollbar.value, 'mousemove', showBar)
|
||||
on(scrollbar.value, 'mouseleave', hideBar)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
off(document, 'mouseup', mouseUpDocumentHandler)
|
||||
off(scrollbar.value, 'mousemove', showBar)
|
||||
off(scrollbar.value, 'mouseleave', hideBar)
|
||||
})
|
||||
|
||||
return {
|
||||
instance,
|
||||
thumb,
|
||||
bar,
|
||||
clickTrackHandler,
|
||||
clickThumbHandler,
|
||||
thumbStyle,
|
||||
visible,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
</script>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="el-scrollbar">
|
||||
<div ref="scrollbar" class="el-scrollbar">
|
||||
<div
|
||||
ref="wrap"
|
||||
:class="[
|
||||
@ -26,21 +26,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {
|
||||
addResizeListener,
|
||||
removeResizeListener,
|
||||
} from '@element-plus/utils/resize-event'
|
||||
import { addResizeListener, removeResizeListener } from '@element-plus/utils/resize-event'
|
||||
import { toObject } from '@element-plus/utils/util'
|
||||
import {
|
||||
defineComponent,
|
||||
ref,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
nextTick,
|
||||
provide,
|
||||
computed,
|
||||
} from 'vue'
|
||||
import Bar from './bar'
|
||||
import { computed, defineComponent, nextTick, onBeforeUnmount, onMounted, provide, ref } from 'vue'
|
||||
import Bar from './bar.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ElScrollbar',
|
||||
@ -77,10 +66,12 @@ export default defineComponent({
|
||||
const sizeHeight = ref('0')
|
||||
const moveX = ref(0)
|
||||
const moveY = ref(0)
|
||||
const scrollbar = ref(null)
|
||||
const wrap = ref(null)
|
||||
const resize = ref(null)
|
||||
|
||||
provide('scroll-bar-wrap', wrap)
|
||||
provide('scrollbar', scrollbar)
|
||||
provide('scrollbar-wrap', wrap)
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!props.native && wrap.value) {
|
||||
@ -92,15 +83,20 @@ export default defineComponent({
|
||||
const update = () => {
|
||||
if (!wrap.value) return
|
||||
|
||||
const heightPercentage =
|
||||
(wrap.value.clientHeight * 100) / wrap.value.scrollHeight
|
||||
const widthPercentage =
|
||||
(wrap.value.clientWidth * 100) / wrap.value.scrollWidth
|
||||
const heightPercentage = (wrap.value.clientHeight * 100) / wrap.value.scrollHeight
|
||||
const widthPercentage = (wrap.value.clientWidth * 100) / wrap.value.scrollWidth
|
||||
|
||||
sizeHeight.value = heightPercentage < 100 ? heightPercentage + '%' : ''
|
||||
sizeWidth.value = widthPercentage < 100 ? widthPercentage + '%' : ''
|
||||
}
|
||||
|
||||
const style = computed(() => {
|
||||
if (Array.isArray(props.wrapStyle)) {
|
||||
return toObject(props.wrapStyle)
|
||||
}
|
||||
return props.wrapStyle
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (props.native) return
|
||||
nextTick(update)
|
||||
@ -111,19 +107,14 @@ export default defineComponent({
|
||||
if (props.native) return
|
||||
!props.noresize && removeResizeListener(resize.value, update)
|
||||
})
|
||||
const style = computed(() => {
|
||||
let style = props.wrapStyle
|
||||
if (Array.isArray(props.wrapStyle)) {
|
||||
style = toObject(props.wrapStyle)
|
||||
}
|
||||
return style
|
||||
})
|
||||
|
||||
return {
|
||||
moveX,
|
||||
moveY,
|
||||
sizeWidth,
|
||||
sizeHeight,
|
||||
style,
|
||||
scrollbar,
|
||||
wrap,
|
||||
resize,
|
||||
update,
|
||||
|
@ -6,15 +6,6 @@
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
> .el-scrollbar__bar {
|
||||
opacity: 1;
|
||||
transition: opacity 340ms ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(wrap) {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
@ -48,8 +39,6 @@
|
||||
bottom: 2px;
|
||||
z-index: 1;
|
||||
border-radius: 4px;
|
||||
opacity: 0;
|
||||
transition: opacity 120ms ease-out;
|
||||
|
||||
@include when(vertical) {
|
||||
width: 6px;
|
||||
@ -70,3 +59,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-scrollbar-fade-enter-active {
|
||||
transition: opacity 340ms ease-out;
|
||||
}
|
||||
|
||||
.el-scrollbar-fade-leave-active {
|
||||
transition: opacity 120ms ease-out;
|
||||
}
|
||||
|
||||
.el-scrollbar-fade-enter-from,
|
||||
.el-scrollbar-fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user