vue3跑马灯

<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>

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值