在可视化展示界面时有一种场景,就是页面在初始化的时候,有些数字展示想要从某个值开始动态递增到实际值,形成一种动画效果。例如:
第一种情况:固定最大递增过程时间
import { useEffect, useState } from 'react';
import { timer } from 'rxjs';
import { takeWhile } from 'rxjs/operators';
export const Counter = ({
counts,
time = 1000, // 默认1秒之内整个动画完成
}: {
counts: number;
time?: number;
}) => {
const [count, setCount] = useState(0);
useEffect(() => {
const step = counts <= time ? 1 : Math.ceil(counts / time); // 两种情况:1.总数小于time时,就以每毫秒递增1的形式增加;2.总数达于1000,计算出每毫秒至少要递增多少。
const sub = timer(0, 1) // 设置一个定时器,每一毫秒执行一次
.pipe(
takeWhile((val) => {
setCount((pre) => (pre + step > counts ? counts : pre + step));
return val < Math.min(counts, time); // 当counts值小于time时,就只执行行counts毫秒,达到counts就停止;如果counts值大于time,那就执行time毫秒
})
)
.subscribe();
return () => sub.unsubscribe(); // 组件关闭时删除掉timer
}, [counts]);
return <div>{count}</div>;
};
<Counter counts={452} />
这种处理方法的好处是,整个递增动画的时间可控,并且人性化的处理了有一些比较小的数字,没必要通过变缓整个动画效果来达到固定的动画时间,如果页面需要这个动画效果的地方较多的时候,整个递增的频率是一致的,界面看起来也不会混乱。这种方法比较适合单纯只是给页面做一种动态特效,增加页面丰富性。
第二种情况:固定步进
import { useEffect, useState } from 'react';
import { timer } from 'rxjs';
import { takeWhile } from 'rxjs/operators';
export const Counter = ({
counts,
step = 1,
}: {
counts: number;
step?: number;
}) => {
const [count, setCount] = useState(0);
useEffect(() => {
const t =
counts % step
? (counts + (step - (counts % step))) / step
: counts / step; // 求出具体需要递增多少次
const sub = timer(0, 1)
.pipe(
takeWhile((val) => {
setCount((pre) => (pre + step > counts ? counts : pre + step)); // 递增值大于counts时,设为counts
return val < t // 只执行t毫秒
})
)
.subscribe();
return () => sub.unsubscribe();
}, []);
return <div>{count}</div>;
};
<Counter counts={4555} step={2} />
这种做法是把每次递增的步进给固定了,每次必须增加这么多,在这种情况下,像我们上面的这种处理方式就是不固定具体整个动画要进行多长时间,只需要看我们计算出来的递增次数有多少次,那么就按照每一毫秒递增的频率递增多少毫秒。
当然,你还可以在这个基础上继续延伸,比如递增步进固定的同时,把递增最大时间也给固定了。。