<template>
<div class="marquee-wrap">
<div class="icon">
<img src="./guangbo.png" />
</div>
<div
class="marquee-container"
ref="marqueeContainer"
@mouseenter="pauseMarquee"
@mouseleave="resumeMarquee"
>
<div class="marquee-content" ref="marqueeContent" :style="{ left: position + 'px' }">
<template v-for="(item, index) in items" :key="index">
<span class="marquee-item">{{ item }}</span>
<span class="marquee-separator" v-if="showSeparator && index < items.length - 1">
{{ separator }}
</span>
</template>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
const props = defineProps({
items: {
type: Array,
required: true,
default: () => []
},
speed: {
type: Number,
default: 10
},
separator: {
type: String,
default: ''
},
showSeparator: {
type: Boolean,
default: true
}
})
const marqueeContainer = ref(null)
const marqueeContent = ref(null)
const position = ref(0)
const animationId = ref(null)
const isPaused = ref(false)
const containerWidth = ref(0)
const contentWidth = ref(0)
function startMarquee() {
if (isPaused.value) return
position.value -= props.speed
// 当内容完全离开左侧边界时,重置到右侧
if (position.value < -contentWidth.value) {
position.value = containerWidth.value
}
marqueeContent.value.style.left = position.value + 'px'
animationId.value = requestAnimationFrame(startMarquee)
}
function pauseMarquee() {
isPaused.value = true
if (animationId.value) {
cancelAnimationFrame(animationId.value)
}
}
function resumeMarquee() {
if (!isPaused.value) return
isPaused.value = false
startMarquee()
}
function resetMarquee() {
if (marqueeContainer.value && marqueeContent.value) {
containerWidth.value = marqueeContainer.value.offsetWidth
contentWidth.value = marqueeContent.value.offsetWidth
position.value = containerWidth.value
marqueeContent.value.style.left = position.value + 'px'
}
}
// 监听items变化,重置跑马灯
watch(
() => props.items,
() => {
// 等待DOM更新后重置
setTimeout(resetMarquee, 0)
}
)
onMounted(() => {
resetMarquee()
startMarquee()
window.addEventListener('resize', resetMarquee)
})
onBeforeUnmount(() => {
if (animationId.value) {
cancelAnimationFrame(animationId.value)
}
window.removeEventListener('resize', resetMarquee)
})
</script>
<style scoped lang="less">
.marquee-wrap {
display: flex;
padding: 10px 0;
height: 68px;
background: #fff;
padding: 22px 30px;
border-radius: 16px;
margin-bottom: 20px;
.icon {
margin-right: 20px;
padding-top: 2px;
img {
width: 26px;
height: 26px;
}
svg {
width: 20px;
height: 20px;
color: #c39a6b;
font-weight: bold;
margin-right: 10px;
}
}
}
.marquee-container {
width: 100%;
overflow: hidden;
white-space: nowrap;
position: relative;
/* border: 1px solid #ccc; */
}
.marquee-content {
display: inline-block;
position: relative;
}
.marquee-item {
display: inline-block;
font-size: 18px;
color: #000000;
}
.marquee-separator {
display: inline-block;
/* margin: 0 10px; */
width: 60px;
}
</style>
vue3跑马灯
最新推荐文章于 2025-11-10 09:36:03 发布
1万+

被折叠的 条评论
为什么被折叠?



