数据可视化大屏 项目开发中,需要实现数字滚动效果,这里我们使用 gsap 来封装成一个vue组件实现我们想要的效果。
动画效果
使用组件
<numcard :number="item.value" :delay="index*0.15+'s'"></numcard>
组件代码
首先接收一个数字传值,然后接收数字滚动执行时间。
<template>
<div v-if="show" class="numcard numcardItem1">
<span class="real-time-num" ref="number">{{ currentNumber }}</span>
</div>
</template>
<script>
import {gsap} from 'gsap';
export default {
data() {
return {
currentNumber: 0,
show: true,
};
},
props: {
number: {
type: [Number, String],
default() {
return 0;
},
},
// :delay="0.15+'s'"
delay: {
type: [Number, String],
default() {
return 0;
},
},
},
watch: {
number(newVal) {
if (this.show) {
this.$nextTick(() => {
this.currentNumber = 0
this.animateNumber(this.currentNumber, newVal);
});
}
},
show() {
this.$nextTick(() => {
this.currentNumber = 0
this.animateNumber(this.currentNumber, this.number);
});
},
},
methods: {
animateNumber(start, end) {
gsap.to(this, {
currentNumber: end,
duration: 2,
delay: parseFloat(this.delay), // 添加这一行
roundProps: 'currentNumber',
ease: 'power1.out',
onUpdate: () => {
this.currentNumber = Math.round(this.currentNumber);
},
});
},
},
mounted() {
if (this.show) {
this.currentNumber = 0
this.animateNumber(this.currentNumber, this.number);
}
},
};
</script>
<style lang="scss">
.numcardItem1 {
.real-time-num {
width: auto !important;
font-size: 22px;
font-family: DIN-Bold;
font-weight: bold;
font-style: normal;
color: #11FFFE;
animation: slideUp 1s forwards;
}
}
@keyframes slideUp {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
</style>
增加千分位逗号
数字滚动效果有了,不过在实际大屏项目中,我们还需要在千分位上显示逗号,如下图:
代码调整
<template>
<div v-if="show" class="numcard numcardItem1">
<span class="real-time-num" ref="number">{{ formattedNumber }}</span>
</div>
</template>
<script>
import { gsap } from 'gsap';
export default {
data() {
return {
rawNumber: 0, // 更改为rawNumber,存储实际数字值
show: true,
showComma: false, // 是否显示千分位逗号
};
},
props: {
number: {
type: [Number, String],
default() {
return 0;
},
},
comma: { // 通过props控制是否显示千分位
type: Boolean,
default: false
},
delay: {
type: [Number, String],
default() {
return 0;
},
},
},
computed: {
formattedNumber() {
if (this.showComma) {
return this.rawNumber.toLocaleString(); // 格式化数字为千分位格式
}
return this.rawNumber;
},
currentNumber: {
get() {
return this.rawNumber;
},
set(value) {
this.rawNumber = value;
}
}
},
watch: {
number(newVal) {
if (this.show) {
this.$nextTick(() => {
this.currentNumber = 0
this.animateNumber(this.currentNumber, newVal);
});
}
},
show() {
this.$nextTick(() => {
this.currentNumber = 0
this.animateNumber(this.currentNumber, this.number);
});
},
comma(newVal) {
this.showComma = newVal; // 设置是否显示千分位
}
},
methods: {
animateNumber(start, end) {
gsap.to(this, {
currentNumber: end,
duration: 2,
delay: parseFloat(this.delay),
roundProps: 'currentNumber',
ease: 'power1.out',
onUpdate: () => {
this.currentNumber = Math.round(this.currentNumber);
},
});
},
},
mounted() {
this.showComma = this.comma; // 初始化时根据props设置是否显示千分位
if (this.show) {
this.currentNumber = 0
this.animateNumber(this.currentNumber, this.number);
}
},
};
</script>
<gsapNum :number="12345" :comma="true"/>
传入什么数字就渲染什么数字
如果有小数就渲染小数,没有小数就不渲染
<template>
<div v-if="show" class="numcard numcardItem1">
<span class="real-time-num gsapNum" ref="number">{{ formattedNumber }}{{unit}}</span>
</div>
</template>
<script>
import gsap from 'gsap';
export default {
data() {
return {
rawNumber: 0, // 更改为rawNumber,存储实际数字值
show: true,
showComma: false, // 是否显示千分位逗号
};
},
props: {
number: {
type: [Number, String],
default() {
return 0;
},
},
unit: { // 控制单位显示
type: String,
default: ''
},
comma: { // 通过props控制是否显示千分位
type: Boolean,
default: false
},
delay: {
type: [Number, String],
default() {
return 0;
},
},
},
computed: {
formattedNumber() {
return this.rawNumber;
},
currentNumber: {
get() {
return this.rawNumber;
},
set(value) {
this.rawNumber = value;
}
}
},
watch: {
number(newVal) {
if (this.show) {
this.$nextTick(() => {
this.currentNumber = 0
this.animateNumber(this.currentNumber, newVal);
});
}
},
show() {
this.$nextTick(() => {
this.currentNumber = 0
this.animateNumber(this.currentNumber, this.number);
});
},
comma(newVal) {
this.showComma = newVal; // 设置是否显示千分位
}
},
methods: {
animateNumber(start, end) {
// 计算原始数字的小数位数
let decimalPlaces = (end.toString().split('.')[1] || '').length;
gsap.to(this, {
currentNumber: end,
duration: 2,
delay: parseFloat(this.delay),
ease: 'power1.out',
onUpdate: () => {
// 保持相同的小数位数
this.currentNumber = parseFloat(this.currentNumber.toFixed(decimalPlaces));
},
});
},
},
mounted() {
this.showComma = this.comma;
this.showDecimal = this.decimal; // 初始化显示小数的设置
if (this.show) {
this.currentNumber = 0
this.animateNumber(this.currentNumber, this.number);
}
},
};
</script>
<style lang="less">
.numcardItem1 {
.real-time-num {
width: auto !important;
font-size: 36px;
font-family: DIN-Bold, DIN;
font-weight: bold;
color: #FDBA51;
font-style: normal;
}
}
@keyframes slideUp {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
</style>