Files
td_official/src/views/gisHome/component/autoScroller.vue

126 lines
2.9 KiB
Vue
Raw Normal View History

2025-05-09 18:41:08 +08:00
<template>
<div class="auto-scroll-container" @mouseenter="pauseScroll" @mouseleave="resumeScroll" ref="container">
<div class="auto-scroll-content" ref="content">
<div class="auto-scroll-item" v-for="(item, index) in duplicatedList" :key="index">
<slot :item="item">{{ item }}</slot>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
// Props
const props = defineProps({
items: {
type: Array,
required: true
},
speed: {
type: Number,
default: 0.5 // px/frame
},
height: {
type: Number,
default: 100 // px
},
minItems: {
type: Number,
default: 2 // 小于这个数量不滚动
},
autoScroll: {
type: Boolean,
default: true // 控制是否自动滚动
}
});
// Refs and Computed
const container = ref(null);
const content = ref(null);
const duplicatedList = computed(() => [...props.items, ...props.items]);
const shouldScroll = computed(() => props.items.length >= props.minItems);
let scrollY = 0;
let animationFrameId = null;
let manualPaused = false; // 记录是否因为滚轮手动停止
// 滚动核心逻辑
function normalizeScrollY(contentHeight) {
if (scrollY <= -contentHeight) scrollY += contentHeight;
if (scrollY >= 0) scrollY -= contentHeight;
}
function step() {
const contentHeight = content.value.offsetHeight / 2;
scrollY -= props.speed;
normalizeScrollY(contentHeight);
content.value.style.transform = `translateY(${Math.round(scrollY)}px)`;
animationFrameId = requestAnimationFrame(step);
}
function startScroll() {
if (!animationFrameId) {
animationFrameId = requestAnimationFrame(step);
}
}
function pauseScroll() {
cancelAnimationFrame(animationFrameId);
animationFrameId = null;
}
// 鼠标滚轮事件:手动滚动并停止自动滚动
function onWheel(e) {
pauseScroll();
manualPaused = true; // 标记为手动停止
const contentHeight = content.value.offsetHeight / 2;
scrollY -= e.deltaY;
normalizeScrollY(contentHeight);
content.value.style.transform = `translateY(${Math.round(scrollY)}px)`;
}
// 鼠标移出时恢复自动滚动(如果不是手动暂停)
function resumeScroll() {
if (props.autoScroll && shouldScroll.value) {
manualPaused = false; // 重置手动暂停标志
startScroll();
}
}
// 生命周期
onMounted(() => {
if (shouldScroll.value && props.autoScroll) {
startScroll();
}
container.value.addEventListener('wheel', onWheel);
});
onUnmounted(() => {
pauseScroll();
container.value.removeEventListener('wheel', onWheel);
});
// 响应 items 数量变化
watch(shouldScroll, (newVal) => {
if (!newVal) {
pauseScroll();
} else if (props.autoScroll && !manualPaused) {
startScroll();
}
});
</script>
<style scoped>
.auto-scroll-container {
overflow: hidden;
position: relative;
}
.auto-scroll-content {
display: flex;
flex-direction: column;
will-change: transform;
}
</style>