106 lines
2.2 KiB
Vue
106 lines
2.2 KiB
Vue
<script setup lang="ts">
|
|
import { ref, toRefs, useTemplateRef } from 'vue';
|
|
import { useResizeObserver } from '@vueuse/core';
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
text: string;
|
|
maxWidth: number;
|
|
mode?: 'swing' | 'continuous';
|
|
}>(),
|
|
{
|
|
mode: 'continuous',
|
|
},
|
|
);
|
|
|
|
const { text, maxWidth, mode } = toRefs(props);
|
|
|
|
const TEMPLATE_REF = 'marquee-text-ref';
|
|
const marqueeTextRef = useTemplateRef<HTMLDivElement>(TEMPLATE_REF);
|
|
const overflowing = ref<boolean>(false);
|
|
const scrollDistance = ref<number>(0);
|
|
const overflowDistance = ref<number>(0);
|
|
|
|
useResizeObserver(marqueeTextRef, () => {
|
|
const element = marqueeTextRef.value;
|
|
if (!element) return;
|
|
const { scrollWidth, clientWidth } = element;
|
|
overflowing.value = scrollWidth > clientWidth;
|
|
scrollDistance.value = scrollWidth;
|
|
overflowDistance.value = scrollWidth - clientWidth;
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
:ref="TEMPLATE_REF"
|
|
class="marquee-text"
|
|
:style="{
|
|
maxWidth: `${maxWidth}px`,
|
|
'--scroll-distance': `${mode === 'swing' ? overflowDistance : scrollDistance}px`,
|
|
}"
|
|
>
|
|
<!-- 不溢出 -->
|
|
<div v-if="!overflowing" class="marquee-text__text">{{ text }}</div>
|
|
|
|
<!-- 来回滚动 -->
|
|
<div v-else-if="mode === 'swing'" class="marquee-text__text overflowing">{{ text }}</div>
|
|
|
|
<!-- 单向连续 -->
|
|
<div v-else class="marquee-text__scroll">
|
|
<span>{{ text }}</span>
|
|
<span class="marquee-text__gap" />
|
|
<span>{{ text }}</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.marquee-text {
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
font-size: 12px;
|
|
|
|
&__text {
|
|
&.overflowing {
|
|
display: inline-block;
|
|
animation: marquee-swing 4s ease-in-out infinite;
|
|
}
|
|
}
|
|
|
|
&__scroll {
|
|
display: inline-block;
|
|
animation: marquee-continuous 3s linear infinite;
|
|
}
|
|
|
|
&__gap {
|
|
display: inline-block;
|
|
width: 24px;
|
|
}
|
|
}
|
|
|
|
@keyframes marquee-swing {
|
|
0%,
|
|
12.5% {
|
|
transform: translateX(0);
|
|
}
|
|
40%,
|
|
60% {
|
|
transform: translateX(calc(-1 * var(--scroll-distance)));
|
|
}
|
|
87.5%,
|
|
100% {
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
@keyframes marquee-continuous {
|
|
0% {
|
|
transform: translateX(0);
|
|
}
|
|
100% {
|
|
transform: translateX(calc(-1 * var(--scroll-distance) - 24px));
|
|
}
|
|
}
|
|
</style>
|