perf: optimize ScrooX component.
This commit is contained in:
parent
a1db8273f5
commit
76c3f0b64c
@ -1,20 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="wrapper" class="tags-wrapper" @mousewheel.prevent="handleMouseWheel">
|
<div ref="wrapper" class="wrapper" @mousewheel.prevent="handleMouseWheel">
|
||||||
<template v-if="showArrow && isOverflow">
|
<template v-if="showArrow && isOverflow">
|
||||||
<div class="left" @click="handleMouseWheel({ wheelDelta: 50 })">
|
<div class="left" @click="handleMouseWheel({ wheelDelta: 120 })">
|
||||||
<icon-ic:baseline-keyboard-arrow-left />
|
<icon-ic:baseline-keyboard-arrow-left />
|
||||||
</div>
|
</div>
|
||||||
<div class="right" @click="handleMouseWheel({ wheelDelta: -50 })">
|
<div class="right" @click="handleMouseWheel({ wheelDelta: -120 })">
|
||||||
<icon-ic:baseline-keyboard-arrow-right />
|
<icon-ic:baseline-keyboard-arrow-right />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
ref="content"
|
ref="content"
|
||||||
class="tags-content"
|
class="content"
|
||||||
:class="{ overflow: isOverflow && showArrow }"
|
:class="{ overflow: isOverflow && showArrow }"
|
||||||
:style="{
|
:style="{
|
||||||
height: height + 'px',
|
|
||||||
transform: `translateX(${translateX}px)`,
|
transform: `translateX(${translateX}px)`,
|
||||||
}">
|
}">
|
||||||
<slot />
|
<slot />
|
||||||
@ -24,37 +23,23 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { debounce } from '@/utils'
|
import { debounce } from '@/utils'
|
||||||
import { isNullOrUndef } from '@/utils/is'
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
default: 50,
|
|
||||||
},
|
|
||||||
showArrow: {
|
showArrow: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
refreshIsOverflow()
|
|
||||||
})
|
|
||||||
|
|
||||||
const translateX = ref(0)
|
const translateX = ref(0)
|
||||||
const content = ref(null)
|
const content = ref(null)
|
||||||
const wrapper = ref(null)
|
const wrapper = ref(null)
|
||||||
const isOverflow = ref(false)
|
const isOverflow = ref(false)
|
||||||
|
|
||||||
function refreshIsOverflow(isIncrease) {
|
const refreshIsOverflow = debounce(() => {
|
||||||
isOverflow.value = content.value.offsetWidth > wrapper.value.offsetWidth
|
isOverflow.value = content.value.offsetWidth > wrapper.value.offsetWidth
|
||||||
if (isNullOrUndef(isIncrease)) return
|
}, 200)
|
||||||
if (isOverflow.value) {
|
|
||||||
handleMouseWheel({ wheelDelta: isIncrease ? -100 : 100 })
|
|
||||||
} else if (!isIncrease && translateX.value < 0) {
|
|
||||||
handleMouseWheel({ wheelDelta: 100 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function handleMouseWheel(e) {
|
function handleMouseWheel(e) {
|
||||||
const { wheelDelta } = e
|
const { wheelDelta } = e
|
||||||
const wrapperWidth = wrapper.value.offsetWidth
|
const wrapperWidth = wrapper.value.offsetWidth
|
||||||
@ -65,15 +50,15 @@ function handleMouseWheel(e) {
|
|||||||
* @wrapperWidth 容器的宽度
|
* @wrapperWidth 容器的宽度
|
||||||
* @contentWidth 内容的宽度
|
* @contentWidth 内容的宽度
|
||||||
*/
|
*/
|
||||||
if (wheelDelta < 0 && -translateX.value > contentWidth - wrapperWidth + 10) {
|
if (wheelDelta < 0) {
|
||||||
return
|
if (wrapperWidth > contentWidth && translateX.value < -10) return
|
||||||
|
if (wrapperWidth <= contentWidth && contentWidth + translateX.value - wrapperWidth < -10) return
|
||||||
}
|
}
|
||||||
if (wheelDelta > 0 && translateX.value > 10) {
|
if (wheelDelta > 0 && translateX.value > 10) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
translateX.value += wheelDelta
|
translateX.value += wheelDelta
|
||||||
|
|
||||||
resetTranslateX(wrapperWidth, contentWidth)
|
resetTranslateX(wrapperWidth, contentWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,20 +72,29 @@ const resetTranslateX = debounce(function (wrapperWidth, contentWidth) {
|
|||||||
}
|
}
|
||||||
}, 200)
|
}, 200)
|
||||||
|
|
||||||
defineExpose({
|
const observer = new MutationObserver(refreshIsOverflow)
|
||||||
refreshIsOverflow,
|
onMounted(() => {
|
||||||
|
refreshIsOverflow()
|
||||||
|
|
||||||
|
window.addEventListener('resize', refreshIsOverflow)
|
||||||
|
// 监听内容宽度刷新是否超出
|
||||||
|
observer.observe(content.value, { childList: true })
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('resize', refreshIsOverflow)
|
||||||
|
observer.disconnect()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.tags-wrapper {
|
.wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.tags-content {
|
position: relative;
|
||||||
|
.content {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<ScrollX ref="scrollX" :height="useTheme.tags.height">
|
<ScrollX :class="`h-${useTheme.tags.height}`">
|
||||||
<n-tag
|
<n-tag
|
||||||
v-for="tag in tagsStore.tags"
|
v-for="tag in tagsStore.tags"
|
||||||
:key="tag.path"
|
:key="tag.path"
|
||||||
@ -47,15 +47,6 @@ watch(
|
|||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
const scrollX = ref(null)
|
|
||||||
watch(
|
|
||||||
() => tagsStore.tags,
|
|
||||||
async (newVal, oldVal) => {
|
|
||||||
await nextTick()
|
|
||||||
scrollX.value?.refreshIsOverflow(newVal.length > oldVal.length)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleTagClick = (path) => {
|
const handleTagClick = (path) => {
|
||||||
tagsStore.setActiveTag(path)
|
tagsStore.setActiveTag(path)
|
||||||
router.push(path)
|
router.push(path)
|
||||||
|
Loading…
Reference in New Issue
Block a user