import React, { useEffect, useRef } from 'react'
import { Animated, StyleSheet, Text, TextStyle, View } from 'react-native'
import { typeScale } from 'src/styles/fonts'
interface Props {
value: string | number
typeScaleName?: keyof typeof typeScale
animationDuration?: number
disableAnimation?: boolean
testID?: string
}
interface TickTextProps {
textStyle: TextStyle
value: string
}
interface TickProps {
startValue: number
endValue: number
animationDuration: number
textHeight: number
textStyle: TextStyle
}
const numberRange = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
function TickText({ value, textStyle }: TickTextProps) {
return (
{value}
)
}
function Tick({ startValue, endValue, textStyle, textHeight, animationDuration }: TickProps) {
const animatedValue = useRef(new Animated.Value(startValue * textHeight * -1))
const transformStyle = { transform: [{ translateY: animatedValue.current }] }
const duration = animationDuration ?? 1300
useEffect(() => {
if (animatedValue.current) {
Animated.timing(animatedValue.current, {
toValue: endValue * textHeight * -1,
duration,
useNativeDriver: true,
}).start()
}
}, [endValue])
return (
{numberRange.map((number, index) => {
return
})}
)
}
export default function NumberTicker({
value,
typeScaleName = 'displaySmall',
animationDuration = 1300,
disableAnimation = false,
testID,
}: Props) {
const textStyle = typeScale[typeScaleName]
const textHeight = textStyle.lineHeight
const finalValueArray = value.toString().split('')
// For the startValueArray, map over each character in the finalValueArray to
// replace digits with random digits, do not change non-digit characters (e.g.
// decimal separator)
const startValueArray = finalValueArray.map((char) => {
return char.match(/\d/) ? Math.floor(Math.random() * 10).toString() : char
})
return (
{finalValueArray.map((value, index) => {
// If the character is not a digit, render it as a static text element
if (!value.match(/\d/)) {
return
}
const endValue = parseInt(value, 10)
const startValue = parseInt(startValueArray[index], 10)
return (
)
})}
)
}
const styles = StyleSheet.create({
container: {
overflow: 'hidden',
flexDirection: 'row',
// This negative gap is a hack to bring the numbers closer together,
// otherwise they feel unnatural and far apart
gap: -2,
},
tickerText: {
textAlign: 'center',
},
})