在前端开发中,动画效果总能为用户界面增添一抹生动与趣味。今天,我们将深入剖析一个基于Vue 3实现的数字翻滚动画组件,从代码结构到功能实现,再到性能优化,一步步揭开它的神秘面纱。
组件概述
这个Vue组件名为NumberFlipAnimation
,旨在通过数字翻滚的效果来展示一个变化的数值。无论是整数还是小数,都能以翻滚动画的形式呈现,为数据展示增添一抹动感。
代码结构
- html和js部分
<template>
<div ref="numberAnimationRef" class="count-flop" :key="compKey">
<!-- -->
<div :class="item !== '.' ? 'count-flop-box' : 'count-flop-point'" v-for="(item, index) in value" :key="index">
<div v-if="item !== '.'" class="count-flop-content" :class="['rolling_' + item]">
<div v-for="(item2, index2) in numberList" :key="index2" class="count-flop-num">
{{ item2 }}
</div>
</div>
<div v-else class="count-flop-content">.</div>
</div>
<!-- -->
<div v-if="suffix" class="count-flop-unit">{{ suffix }}</div>
</div>
</template>
<script setup>
import { ref, defineProps, onMounted, onBeforeUnmount } from 'vue';
const props = defineProps({
val: {
type: [Number, String],
default: 0,
},
suffix: {
type: String,
default: '',
},
});
// Data
const value = ref([]);
const compKey = ref(0);
=const numberList = ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
const numberAnimationRef = ref(null);
// Watcher
const updateValue = () => {
value.value = props.val.toString().split('');
compKey.value += 1;
};
// Intersection Observer
const observer = ref(null);
onMounted(() => {
observer.value = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
updateValue();
observer.value.unobserve(entry.target);
}
});
});
if (numberAnimationRef.value) {
observer.value.observe(numberAnimationRef.value);
}
});
onBeforeUnmount(() => {
if (observer.value && numberAnimationRef.value) {
console.log('Unobserving element');
compKey.value = 0;
observer.value.unobserve(numberAnimationRef.value);
}
});
</script>
2. css动画部分
<style lang="stylus" scoped>
.count-flop {
display: inline-block;
/* 可更改 */
height: 4.3vw;
font-size: 3.5vw;
font-weight: 500;
line-height: 4.3vw;
color: #fff;
}
.count-flop > div {
position: relative;
display: inline-block;
overflow: hidden;
height: 100%;
}
.count-flop-box {
/* 可更改 */
//margin-right: 5px;
width: 2vw;
//border: 1px solid rgba(72, 152, 241, 0.3);
/*
这里的高度要比上面height的少2px
*/
line-height: 4.3vw;
//border-radius: 6px;
}
.count-flop-point {
/* 可更改 */
margin-right: 5px;
width: 10px;
}
.count-flop-content {
text-align: center;
position: absolute;
left: 0;
top: 0;
width: 100%;
animation-fill-mode: forwards !important;
}
.rolling_0 {
animation: rolling_0 0.7s ease;
}
@keyframes rolling_0 {
from {
transform: translateY(-90%);
}
to {
transform: translateY(0);
}
}
.rolling_1 {
animation: rolling_1 0.7s ease;
}
@keyframes rolling_1 {
from {
transform: translateY(0);
}
to {
transform: translateY(-10%);
}
}
.rolling_2 {
animation: rolling_2 0.9s ease;
}
@keyframes rolling_2 {
from {
transform: translateY(0);
}
to {
transform: translateY(-20%);
}
}
.rolling_3 {
animation: rolling_3 1s ease;
}
@keyframes rolling_3 {
from {
transform: translateY(0);
}
to {
transform: translateY(-30%);
}
}
.rolling_4 {
animation: rolling_4 1.2s ease;
}
@keyframes rolling_4 {
from {
transform: translateY(0);
}
to {
transform: translateY(-40%);
}
}
.rolling_5 {
animation: rolling_5 1s ease;
}
@keyframes rolling_5 {
from {
transform: translateY(0);
}
to {
transform: translateY(-50%);
}
}
.rolling_6 {
animation: rolling_6 1.5s ease;
}
@keyframes rolling_6 {
from {
transform: translateY(0);
}
to {
transform: translateY(-60%);
}
}
.rolling_7 {
animation: rolling_7 2.1s ease;
}
@keyframes rolling_7 {
from {
transform: translateY(0);
}
to {
transform: translateY(-70%);
}
}
.rolling_8 {
animation: rolling_8 0.8s ease;
}
@keyframes rolling_8 {
from {
transform: translateY(0);
}
to {
transform: translateY(-80%);
}
}
.rolling_9 {
animation: rolling_9 1.6s ease;
}
@keyframes rolling_9 {
from {
transform: translateY(0);
}
to {
transform: translateY(-90%);
}
}
/* 其他 rolling_X 动画略 */
</style>
代码结构解析
-
模板部分 (
<template>
):- 组件的HTML结构由外层的
<div>
容器包裹,内部通过v-for
指令遍历value
数组,为每个数字或小数点创建一个独立的<div>
。 - 使用
:key
属性确保列表渲染的高效性。 - 通过动态类名
rolling_
加上数字值,为每个数字应用特定的翻滚动画。 - 若存在后缀(如单位),则在数字后显示。
- 组件的HTML结构由外层的
-
脚本部分 (
<script setup>
):- 使用Vue 3的
setup
语法,定义组件的props
、ref
和响应式数据。 props
包括val
(要展示的数字或字符串)、numberList
(数字列表,用于翻滚动画)和suffix
(后缀,如单位)。updateValue
函数用于将传入的val
转换为字符串数组,并更新compKey
以触发重新渲染。- 利用
IntersectionObserver
实现懒加载动画,当组件进入视口时才执行动画,提高性能。 - 在组件卸载时,通过
onBeforeUnmount
钩子清除观察器,避免内存泄漏。
- 使用Vue 3的
-
样式部分 (
<style lang="stylus" scoped>
):- 使用Stylus预处理器编写组件的样式,确保样式的作用域仅限于当前组件。
- 定义了
.count-flop
及其子元素的样式,包括大小、布局和动画效果。
功能实现细节
- 数字翻滚动画:通过动态类名和CSS动画实现。每个数字都有一套对应的翻滚动画,当
value
更新时,通过更改类名触发动画。 - 懒加载优化:利用
IntersectionObserver
监听组件是否进入视口,只有在用户即将看到组件时才触发动画,减少不必要的计算和渲染。 - 响应式更新:通过
ref
和watch
机制,确保当props
变化时,组件能够响应并更新显示。