效果动画图:
完整代码如下
<template> <div class="number-flip"> <div class="box-item" v-for="(digit, index) in digits" :key="index"> <span class="item-num" ref="spanRefs">0123456789</span> </div> </div> </template> <script setup lang="ts"> import { reactive, ref, watch, onMounted } from 'vue' const props = defineProps<{ number: number, enableInitialAnimation?: boolean // 是否启用初始动画 }>() const spanRefs = ref<HTMLElement[]>([]) const digits = ref<number[]>([]) // 将数字转换为数字数组 const updateDigits = (num: number) => { digits.value = String(num).split('').map(Number) } // 更新数字位置 const updateDigitPositions = (withTransition = true) => { requestAnimationFrame(() => { spanRefs.value.forEach((span, index) => { if (span) { if (!withTransition) { // 临时移除过渡效果 span.style.transition = 'none' } span.style.transform = `translate(-50%, -${digits.value[index] * 10}%)` if (!withTransition) { // 强制浏览器重绘 span.offsetHeight // 恢复过渡效果 span.style.transition = '' } } }) }) } // 初始化 onMounted(() => { updateDigits(props.number) updateDigitPositions(props.enableInitialAnimation) }) // 监听数字变化 watch(() => props.number, (newVal) => { updateDigits(newVal) updateDigitPositions(true) // 数字变化时总是启用动画 }) </script> <style lang="less" scoped> .number-flip { display: inline-flex; align-items: center; .box-item { width: 0.8em; height: 1.4em; border-radius: 2px; position: relative; margin: 0 1px; overflow: hidden; .item-num { position: absolute; top: 1%; left: 40%; font-weight: bold; transform: translateX(-50%); writing-mode: vertical-lr; text-orientation: upright; transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1); line-height: 1.6; letter-spacing: 0.2em; font-size: 1.2em; } } } </style>
使用方式如下:
<NumberTransition :number="savedCnt" :enable-initial-animation="false" style="color:blue;font-weight:bold;vertical-align: middle;"/>
文章评论