Browse Source

feat: 添加Marquees组件以实现滚动文本效果并在Billboard中使用

ShangHai_LongXiang
jiannibang 1 year ago
parent
commit
fe9f612455
  1. 115
      src/base/Marquees/Marquees.vue
  2. 12
      src/views/Billboard/Billboard.vue

115
src/base/Marquees/Marquees.vue

@ -0,0 +1,115 @@
<template>
<div ref="wrapElRef" class="relative h-full w-full overflow-hidden p-0">
<div ref="contentElRef" class="absolute h-full whitespace-nowrap" :class="state.animationClass" :style="contentStyle" @animationend="animationend">
<slot></slot>
</div>
</div>
</template>
<script setup>
import { computed, onBeforeUnmount, reactive, ref, watch } from 'vue'
const props = defineProps(['content', 'delay', 'speed'])
const emit = defineEmits(['end'])
const state = reactive({
wrapWidth: 0, //
firstRound: true, //
duration: 0, //
offsetWidth: 0, //
animationClass: '' //animate
})
const wrapElRef = ref(null)
const contentElRef = ref(null)
const contentStyle = computed(() => ({
//,
paddingLeft: (state.firstRound ? 0 : state.wrapWidth) + 'px',
//
animationDelay: (state.firstRound ? props.delay || 0.8 : 0) + 's',
animationDuration: state.duration + 's'
}))
function animationend() {
state.firstRound = false
emit('end')
state.duration = (state.offsetWidth + state.wrapWidth) / (props.speed || 40)
state.animationClass = 'animate-infinite'
}
let timer
watch(
() => props.content,
() => {
state.firstRound = true
state.animationClass = ''
state.duration = 0
clearTimeout(timer)
timer = setTimeout(() => {
clearTimeout(timer)
const wrapWidth = wrapElRef.value.getBoundingClientRect().width
const offsetWidth = contentElRef.value.getBoundingClientRect().width
state.wrapWidth = wrapWidth
state.offsetWidth = offsetWidth
if (state.offsetWidth > state.wrapWidth) {
state.duration = offsetWidth / props.speed
state.animationClass = 'animate'
} else {
emit('end')
state.animationClass = ''
state.duration = 0
}
}, 1000)
},
{
immediate: true
}
)
onBeforeUnmount(() => {
clearTimeout(timer)
timer = null
})
</script>
<style scoped>
.relative {
position: relative;
}
.absolute {
position: absolute;
}
.w-full {
width: 100%;
}
.h-full {
height: 100%;
}
.animate {
animation: paomadeng linear;
}
.overflow-hidden {
overflow: hidden;
}
.p-0 {
padding: 0;
}
.whitespace-nowrap {
white-space: nowrap;
}
.animate-infinite {
animation: paomadeng-infinite linear infinite;
}
@keyframes paomadeng {
to {
transform: translate3d(-100%, 0, 0);
}
}
@keyframes paomadeng-infinite {
to {
transform: translate3d(-100%, 0, 0);
}
}
</style>

12
src/views/Billboard/Billboard.vue

@ -28,7 +28,7 @@
<div class="left">
<img class="dir" :src="data.dir" />
<div class="text">
{{ data.shopName }}
<Marquees :content="data.shopName">{{ data.shopName }}</Marquees>
</div>
</div>
<div class="right">{{ data.distance ? data.distance + '米' : '' }}</div>
@ -76,6 +76,8 @@ import { getBrandListByFormat } from '@/http/brand/api'
import QRCodeFromText from '@/components/QRCodeFromText/QRCodeFromText.vue'
import { useMediaQuery, useWindowSize } from '@vueuse/core'
import { setShopInactive } from '@/composables/useInitMap'
import Marquees from '@/base/Marquees/Marquees.vue'
const vmin = Math.min(window.innerWidth, window.innerHeight)
const scale = vmin / 1080
const isH = useMediaQuery('(min-aspect-ratio: 1/1)')
@ -305,20 +307,20 @@ watch(scrollLefts, _scrollLefts => {
overflow: hidden;
text-overflow: hidden;
.text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
height: 24px;
}
}
.right {
display: inline-flex;
flex: 0 0 100px;
flex: 0 0 60px;
font-weight: 500;
font-size: 16px;
line-height: 19px;
justify-content: flex-end;
padding-right: 16px;
color: rgba(0, 0, 0, 0.6);
white-space: nowrap;
}
.dir {
width: 24px;

Loading…
Cancel
Save