import React, { CSSProperties, FunctionComponent, useEffect, useMemo, useRef, } from 'react' import bem from '@/utils/bem' export interface CountUpProps { maxLen: number endNumber: string delaySpeed?: number easeSpeed: number thousands: boolean className: string style: React.CSSProperties } const defaultProps = { maxLen: 0, endNumber: '', delaySpeed: 300, easeSpeed: 1, thousands: false, className: '', } as CountUpProps export const CountUp: FunctionComponent> = (props) => { const { maxLen, endNumber, delaySpeed, easeSpeed, className, thousands, ...reset } = { ...defaultProps, ...props, } const b = bem('countup') const countupRef = useRef(null) const timerRef = useRef(0) const numbers = Array.from({ length: 10 }, (v, i) => i) const getShowNumber = () => { const splitArr = endNumber.split('.') const intNumber = maxLen && splitArr[0].length < maxLen ? (Array(maxLen).join('0') + splitArr[0]).slice(-maxLen) : splitArr[0] const currNumber = `${ thousands ? intNumber.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') : intNumber }${splitArr[1] ? '.' : ''}${splitArr[1] || ''}` return currNumber.split('') } const numerArr = useMemo(getShowNumber, [endNumber, maxLen, thousands]) const setNumberTransform = () => { if (countupRef.current) { const numberItems = countupRef.current.querySelectorAll( '.nut-countup__number' ) const numberFilterArr: Array = numerArr.filter( (item: string) => !Number.isNaN(Number(item)) ) Object.keys(numberItems).forEach((key) => { const elem = numberItems[Number(key)] as HTMLElement const idx = Number(numberFilterArr[Number(key)]) if ((idx || idx === 0) && elem) { // 计算规则:父元素和实际列表高度的百分比,分割成20等份 const transform = `translate(0, -${(idx === 0 ? 10 : idx) * 5}%)` elem.style.transform = transform elem.style.webkitTransform = transform } }) } } const numberEaseStyle: CSSProperties = { transition: `transform ${easeSpeed}s ease-in-out`, } useEffect(() => { timerRef.current = window.setTimeout(() => { setNumberTransform() }, delaySpeed) return () => { window.clearTimeout(timerRef.current) } }, [numerArr]) return (
    {numerArr.map((item: string, idx: number) => { return (
  • {!Number.isNaN(Number(item)) ? ( {[...numbers, ...numbers].map((number, subidx) => { return {number} })} ) : ( {item} )}
  • ) })}
) } CountUp.defaultProps = defaultProps CountUp.displayName = 'NutCountUp'