效果图
代码
<template><div class="number-scroller"><divclass="viewport":style="{ width: width + 'px', height: height + 'px' }"><div class="number-scroller-box" ref="num"><div v-for="number in numbers" :key="number" class="number-item">{{ number }}</div></div></div></div>
</template><script setup>
import { ref, onMounted, watch } from 'vue';const props = defineProps({width: {type: Number,default: 60},height: {type: Number,default: 60},// 目标数字targetNumber: {type: Number,default: 5},// 动画持续时间animationDuration: {type: Number,default: 10},// 动画缓动函数easing: {type: String,default: 'cubic-bezier(.5,.13,.01,.35)'},// 动画延迟时间delay: {type: Number,default: 1},// 旋转方向direction: {type: String,default: 'vertical', // 默认横向旋转validator: value => ['horizontal', 'vertical'].includes(value)}
});const numbers = ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
const num = ref();/*** rotateToTarget 旋转到目标数字*/
function rotateToTarget (targetNumber, direction) {const angle = 360 / numbers.value.length;const targetIndex = numbers.value.indexOf(targetNumber);if (targetIndex === -1) return; // 如果目标数字不存在,直接返回const randomCircles = Math.floor(Math.random() * 3) + 1; // 随机生成1到3圈const totalAngle = 360 * randomCircles - targetIndex * angle;const rotateAxis = direction === 'horizontal' ? 'Y' : 'X';document.body.getBoundingClientRect();num.value.style.transition = `transform ${props.animationDuration}s ${props.easing} ${props.delay}s`;num.value.style.transform = `rotate${rotateAxis}(${totalAngle}deg)`;
}
/*** layout 页面布局*/
const layout = () => {const angle = 360 / numbers.value.length;const radius = props.width / (2 * Math.sin(Math.PI / numbers.value.length)); // 旋转半径计算numbers.value.forEach((_, index) => {const item = num.value.children[index];const rotateAxis = props.direction === 'horizontal' ? 'Y' : 'X';item.style.transform = `rotate${rotateAxis}(${index * angle}deg) translateZ(${radius}px)`;});
}onMounted(() => {layout()rotateToTarget(props.targetNumber, props.direction);
});
</script><style lang="less" scoped>
.number-scroller {display: flex;align-items: center;position: relative;perspective: 1000px;.viewport {overflow: hidden;position: relative;}.number-scroller-box {position: relative;left: 0;top: 0;width: 100%;height: 100%;transform-style: preserve-3d;box-sizing: border-box;transition: all 3s;.number-item {position: absolute;left: 0;top: 0;box-sizing: border-box;width: 100%;height: 100%;line-height: 100%;padding: 10px;border: 1px solid #ccc;border-radius: 4px;display: flex;align-items: center;justify-content: center;backface-visibility: hidden;}}
}
</style>