手机微信网站怎么做,怎么选择网站开发,网站的空间是,网站建设制作合同这个CountTo组件npmjs里当然有大把的依赖存在#xff0c;不过今天我们不需要借助任何三方依赖#xff0c;造个轮子来手动实现这个组件。
通过研究其他count to插件我们可以发现#xff0c;数字滚动效果主要依赖于requestAnimationFrame 通过js帧来让数字动起来#xff0c;…这个CountTo组件npmjs里当然有大把的依赖存在不过今天我们不需要借助任何三方依赖造个轮子来手动实现这个组件。
通过研究其他count to插件我们可以发现数字滚动效果主要依赖于requestAnimationFrame 通过js帧来让数字动起来数字变化则是依赖于内部的easingFn函数来每次计算。
首先声明组件props类型
interface Props {/*** 动画开始的值*/start?: number;/*** 目标值*/end: number;/*** 持续时间*/duration?: number;/*** 是否自动播放*/autoPlay?: boolean;/*** 精度*/decimals?: number;/*** 小数点*/decimal?: string;/*** 千分位分隔符*/separator?: string;/*** 数字前 额外信息*/prefix?: string;/*** 数字后 额外信息*/suffix?: string;/*** 是否使用变速函数*/useEasing?: boolean;/*** 计算函数*/easingFn?: (t: number, b: number, c: number, d: number) number;/*** 动画开始后传给父组件的回调*/started?: () void;/*** 动画结束传递给父组件的回调*/ended?: () void;
}除了end 是必要的其他都是可选参数。 所以我们需要给组件默认值防止没有参数时会报错。 同时写几个工具函数便于后面使用
export default function Index({end,start 0,duration 3000,autoPlay true,decimals 0,decimal .,separator ,,prefix ,suffix ,useEasing true,easingFn (t, b, c, d) (c * (-Math.pow(2, (-10 * t) / d) 1) * 1024) / 1023 b,started () {},ended () {},
}: Props) {const isNumber (val: string) {return !isNaN(parseFloat(val));};// 格式化数据返回想要展示的数据格式const formatNumber (n: number) {let val ;if (n % 1 ! 0) val n.toFixed(decimals);const x val.split(.);let x1 x[0];const x2 x.length 1 ? decimal x[1] : ;const rgx /(\d)(\d{3})/;if (separator !isNumber(separator)) {while (rgx.test(x1)) {x1 x1.replace(rgx, $1 separator $2);}}return prefix x1 x2 suffix;};...
}初始化数据 const [state, setState] useStateState({start: 0,paused: false,duration,});const startTime useRef(0);const _timestamp useRef(0);const remaining useRef(0);const printVal useRef(0);const rAf useRef(0);const endRef useRef(end);const endedCallback useRef(ended);const [displayValue, setValue] useState(formatNumber(start));// 定义一个计算属性当开始数字大于结束数字时返回trueconst stopCount useMemo(() start end, [start, end]);动画的关键函数 const count (timestamp: number) {if (!startTime.current) startTime.current timestamp;_timestamp.current timestamp;const progress timestamp - startTime.current;remaining.current state.duration - progress;// 是否使用速度变化曲线if (useEasing) {if (stopCount) {printVal.current state.start - easingFn(progress, 0, state.start - end, state.duration);} else {printVal.current easingFn(progress, state.start, end - state.start, state.duration);}} else {if (stopCount) {printVal.current state.start - (state.start - endRef.current) * (progress / state.duration);} else {printVal.current state.start (endRef.current - state.start) * (progress / state.duration);}}if (stopCount) {printVal.current printVal.current endRef.current ? endRef.current : printVal.current;} else {printVal.current printVal.current endRef.current ? endRef.current : printVal.current;}setValue(formatNumber(printVal.current));if (progress state.duration) {rAf.current requestAnimationFrame(count);} else {endedCallback.current?.();}};执行动画的函数 const startCount () {setState({ ...state, start, duration, paused: false });rAf.current requestAnimationFrame(count);startTime.current 0;};挂载时监听是否有autoPlay 来选择是否开始动画同时组件销毁后清除requestAnimationFrame动画 useEffect(() {if (autoPlay) {startCount();started?.();}return () {cancelAnimationFrame(rAf.current);};}, []);一些相关依赖的监听及处理
useEffect(() {if (!autoPlay) {cancelAnimationFrame(rAf.current);setState({ ...state, paused: true });}}, [autoPlay]);useEffect(() {if (!state.paused) {cancelAnimationFrame(rAf.current);startCount();}}, [start]);最后返回displayValue就可以了
好了 我要开启五一假期了 最后附上完整代码 –
use client;import { useEffect, useMemo, useRef, useState } from react;interface Props {/*** 动画开始的值*/start?: number;/*** 目标值*/end: number;/*** 持续时间*/duration?: number;/*** 是否自动播放*/autoPlay?: boolean;/*** 精度*/decimals?: number;/*** 小数点*/decimal?: string;/*** 千分位分隔符*/separator?: string;/*** 数字前 额外信息*/prefix?: string;/*** 数字后 额外信息*/suffix?: string;/*** 是否使用变速函数*/useEasing?: boolean;/*** 计算函数*/easingFn?: (t: number, b: number, c: number, d: number) number;/*** 动画开始后传给父组件的回调*/started?: () void;/*** 动画结束传递给父组件的回调*/ended?: () void;
}
interface State {start: number;paused: boolean;duration: number;
}
export default function Index({end,start 0,duration 3000,autoPlay true,decimals 0,decimal .,separator ,,prefix ,suffix ,useEasing true,easingFn (t, b, c, d) (c * (-Math.pow(2, (-10 * t) / d) 1) * 1024) / 1023 b,started () {},ended () {},
}: Props) {const isNumber (val: string) {return !isNaN(parseFloat(val));};// 格式化数据返回想要展示的数据格式const formatNumber (n: number) {let val ;if (n % 1 ! 0) val n.toFixed(decimals);const x val.split(.);let x1 x[0];const x2 x.length 1 ? decimal x[1] : ;const rgx /(\d)(\d{3})/;if (separator !isNumber(separator)) {while (rgx.test(x1)) {x1 x1.replace(rgx, $1 separator $2);}}return prefix x1 x2 suffix;};const [state, setState] useStateState({start: 0,paused: false,duration,});const startTime useRef(0);const _timestamp useRef(0);const remaining useRef(0);const printVal useRef(0);const rAf useRef(0);const endRef useRef(end);const endedCallback useRef(ended);const [displayValue, setValue] useState(formatNumber(start));// 定义一个计算属性当开始数字大于结束数字时返回trueconst stopCount useMemo(() start end, [start, end]);const count (timestamp: number) {if (!startTime.current) startTime.current timestamp;_timestamp.current timestamp;const progress timestamp - startTime.current;remaining.current state.duration - progress;// 是否使用速度变化曲线if (useEasing) {if (stopCount) {printVal.current state.start - easingFn(progress, 0, state.start - end, state.duration);} else {printVal.current easingFn(progress, state.start, end - state.start, state.duration);}} else {if (stopCount) {printVal.current state.start - (state.start - endRef.current) * (progress / state.duration);} else {printVal.current state.start (endRef.current - state.start) * (progress / state.duration);}}if (stopCount) {printVal.current printVal.current endRef.current ? endRef.current : printVal.current;} else {printVal.current printVal.current endRef.current ? endRef.current : printVal.current;}setValue(formatNumber(printVal.current));if (progress state.duration) {rAf.current requestAnimationFrame(count);} else {endedCallback.current?.();}};const startCount () {setState({ ...state, start, duration, paused: false });rAf.current requestAnimationFrame(count);startTime.current 0;};useEffect(() {if (!autoPlay) {cancelAnimationFrame(rAf.current);setState({ ...state, paused: true });}}, [autoPlay]);useEffect(() {if (!state.paused) {cancelAnimationFrame(rAf.current);startCount();}}, [start]);useEffect(() {if (autoPlay) {startCount();started?.();}return () {cancelAnimationFrame(rAf.current);};}, []);return displayValue;
}