简单的网页数字变更动画实现

通常情况下,数字在网页中的变化是直接转变为另一个值。而如果在数字转化的过程中,每次改变一点点,最终转变为另一个值,会让干硬的网页变得更加生动一点。


分析拆解

数字变更的整个周期,主要分为三个状态。

  1. 数字的初始状态(即原始值
  2. 数字变化中的状态(原始值到新赋值之间的变动
  3. 数字的结束状态(即新赋值

状态的变化可能性

  1. 数字一次性由少变到多,或者由多变到少
  2. 数字变化状态中,多次改变结束状态的值

这里解释下两种变化可能性,第一种就是说,数字的变化从少到多或者从多到少,都是一次性变化,中间不会有任何干扰。而第二种,打个比方,初始值为1,新赋值为10,数字在从1到10的渐变中,还没有变化完全,仅仅变到了7的时候,就被用户再一次改变了新赋值为5或者20(即可能比第一个新赋值低,也可能高)。这个时候,应该顺应此时已经变化到的数值7,以7为起点,向着5或者20去渐增或者渐减。不然,重新以1为起点开始向5或者20进行变化,就会有违和感。而顺应已经变化到的值进行变化,即便中途进行了再多次数值变更,整体的变化仍是流畅自然的。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>数值变化测试demo</title>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
background-color: cornflowerblue;
}

#app {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
align-content: center;
}

.num {
width: 100%;
font-size: 100px;
margin-bottom: 15px;
text-align: center;
}
</style>
</head>
<body>
<div id="app">
<div class="num">{{ num.a }}</div>
<button class="btn" @click="change(1)">1</button>
<button class="btn" @click="change(5)">5</button>
<button class="btn" @click="change(10)">10</button>
<button class="btn" @click="change(20)">20</button>
<button class="btn" @click="change(50)">50</button>
<button class="btn" @click="change(75)">75</button>
<button class="btn" @click="change(100)">100</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
new Vue({
el: '#app',
data: {
num: {
a: 0
},
aniMap: new Map()
},
methods: {
change(newNum) {
this.numAni(this.num, 'a', newNum, 0);
},
// obj: 数字所在对象, key: 数字属性, newVal: 新变化的值,decimal: 变化期间的小数位数, name: 不同对象出现相同key值时,可自主设定name进行区分
numAni(obj, key, newVal, decimal, name) {
newVal = Number(newVal); // 处理可能是字符串的数字内容
const mapKey = name || key;
if (this.aniMap.has(mapKey)) {
clearInterval(this.aniMap.get(mapKey));
this.aniMap.delete(mapKey);
}
const diff = newVal - obj[key]; // 差值
if (diff === 0) { return; }
let ch = Number((Math.abs(diff) / 11).toFixed(decimal)); // 将变化的差值分作11份,进行10次变化
ch = ch <= 1 && decimal === 0 ? 1 : ch;
let count = 0;
const timer = setInterval(() => {
let tempNum = Number(obj[key]);
if (count < 10) {
count += 1;
tempNum += diff < 0 ? -ch : ch;
obj[key] = Number(tempNum.toFixed(decimal));
if (obj[key] === newVal) {
obj[key] = newVal.toString();
clearInterval(timer);
}
} else {
obj[key] = newVal.toString();
this.aniMap.delete(mapKey);
clearInterval(timer);
}
}, 20); // 每单次变化耗时20ms
this.aniMap.set(mapKey, timer); // 用于临时存储计时器返回值
}
}
})
</script>
</body>
</html>

注意事项

  1. 进行变化的数值变量,本身不能是一个单纯的基本变量,而应该存储在一个对象中。
  2. 此方法需要一个map来进行临时的计时器数据变量保存

展示效果

好饿,想吃两个小饼饼(# ̄y▽ ̄)╭
0%