109 lines
2.5 KiB
Vue
109 lines
2.5 KiB
Vue
![]() |
<template>
|
||
|
<div
|
||
|
ref="scrollContainer"
|
||
|
class="scroll-container"
|
||
|
@wheel.prevent="handleScroll"
|
||
|
>
|
||
|
<div ref="scrollWrapper" class="scroll-wrapper">
|
||
|
<slot />
|
||
|
</div>
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script setup>
|
||
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||
|
|
||
|
const emit = defineEmits(['scroll'])
|
||
|
|
||
|
const scrollContainer = ref()
|
||
|
const scrollWrapper = ref()
|
||
|
|
||
|
let scrollLeft = 0
|
||
|
let scrollTop = 0
|
||
|
|
||
|
const handleScroll = (e) => {
|
||
|
const eventDelta = e.wheelDelta || -e.deltaY * 40
|
||
|
const $container = scrollContainer.value
|
||
|
const $containerWidth = $container.offsetWidth
|
||
|
const $wrapper = scrollWrapper.value
|
||
|
const $wrapperWidth = $wrapper.offsetWidth
|
||
|
|
||
|
if (eventDelta > 0) {
|
||
|
scrollLeft = Math.max(0, scrollLeft - 50)
|
||
|
} else {
|
||
|
if ($containerWidth - 50 < $wrapperWidth) {
|
||
|
if (scrollLeft < $wrapperWidth - $containerWidth + 50) {
|
||
|
scrollLeft += 50
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$wrapper.style.left = scrollLeft * -1 + 'px'
|
||
|
emit('scroll')
|
||
|
}
|
||
|
|
||
|
const moveToTarget = (currentTag) => {
|
||
|
const $container = scrollContainer.value
|
||
|
const $containerWidth = $container.offsetWidth
|
||
|
const $wrapper = scrollWrapper.value
|
||
|
|
||
|
if (!currentTag) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
const tagList = $wrapper.querySelectorAll('.tags-view-item')
|
||
|
|
||
|
let firstTag = null
|
||
|
let lastTag = null
|
||
|
|
||
|
if (tagList.length > 0) {
|
||
|
firstTag = tagList[0]
|
||
|
lastTag = tagList[tagList.length - 1]
|
||
|
}
|
||
|
|
||
|
if (firstTag === currentTag) {
|
||
|
scrollLeft = 0
|
||
|
} else if (lastTag === currentTag) {
|
||
|
scrollLeft = $wrapper.offsetWidth - $containerWidth
|
||
|
} else {
|
||
|
const tagListArray = [...tagList]
|
||
|
const currentIndex = tagListArray.findIndex(item => item === currentTag)
|
||
|
const prevTag = tagListArray[currentIndex - 1]
|
||
|
const nextTag = tagListArray[currentIndex + 1]
|
||
|
|
||
|
const afterNextTagOffsetLeft = nextTag ? nextTag.offsetLeft + nextTag.offsetWidth + 4 : 0
|
||
|
|
||
|
const beforePrevTagOffsetLeft = prevTag ? prevTag.offsetLeft - 4 : 0
|
||
|
|
||
|
if (afterNextTagOffsetLeft > scrollLeft + $containerWidth) {
|
||
|
scrollLeft = afterNextTagOffsetLeft - $containerWidth
|
||
|
} else if (beforePrevTagOffsetLeft < scrollLeft) {
|
||
|
scrollLeft = beforePrevTagOffsetLeft
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$wrapper.style.left = scrollLeft * -1 + 'px'
|
||
|
}
|
||
|
|
||
|
// 暴露方法
|
||
|
defineExpose({
|
||
|
moveToTarget
|
||
|
})
|
||
|
</script>
|
||
|
|
||
|
<style lang="scss" scoped>
|
||
|
.scroll-container {
|
||
|
white-space: nowrap;
|
||
|
position: relative;
|
||
|
overflow: hidden;
|
||
|
width: 100%;
|
||
|
|
||
|
.scroll-wrapper {
|
||
|
position: absolute;
|
||
|
top: 0px;
|
||
|
height: 100%;
|
||
|
transition: left 0.3s ease-in-out;
|
||
|
}
|
||
|
}
|
||
|
</style>
|