<script lang="ts"> import { h, onMounted, onUnmounted, ref, watch } from 'vue'; export default { name: 'MarqueeText', props: { duration: { type: Number, default: 15, }, repeat: { type: Number, default: 2, }, paused: { type: Boolean, default: false, }, reverse: { type: Boolean, default: false, }, }, setup(props) { const contentEl = ref(); function calc() { const eachLength = contentEl.value.offsetWidth / props.repeat; const factor = 3000; const duration = props.duration / ((1 / eachLength) * factor); contentEl.value.style.animationDuration = `${duration}s`; } watch(() => props.duration, calc); onMounted(() => { calc(); }); onUnmounted(() => { }); return { contentEl, }; }, render({ $slots, $style, $props: { duration, repeat, paused, reverse, }, }) { return h('div', { class: [$style.wrap] }, [ h('span', { ref: 'contentEl', class: [ paused ? $style.paused : undefined, $style.content, ], }, Array(repeat).fill( h('span', { class: $style.text, style: { animationDirection: reverse ? 'reverse' : undefined, }, }, $slots.default()), )), ]); }, }; </script> <style lang="scss" module> .wrap { overflow: clip; animation-play-state: running; &:hover { animation-play-state: paused; } } .content { display: inline-block; white-space: nowrap; animation-play-state: inherit; } .text { display: inline-block; animation-name: marquee; animation-timing-function: linear; animation-iteration-count: infinite; animation-duration: inherit; animation-play-state: inherit; } .paused .text { animation-play-state: paused; } @keyframes marquee { 0% { transform:translateX(0); } 100% { transform:translateX(-100%); } } </style>