oblador / react-native-vector-icons

Customizable Icons for React Native with support for image source and full styling.
https://oblador.github.io/react-native-vector-icons/
MIT License
17.45k stars 2.12k forks source link

How to change icon name with Reanimated #1647

Closed AuroPick closed 2 months ago

AuroPick commented 2 months ago

Environment

Description

I want to use shared value as name prop but it is not working.

I created an Animated component with

const AnimatedFontAwesome6Icon = Animated.createAnimatedComponent(FontAwesome6Icon)

and used it like this

<AnimatedFontAwesome6Icon animatedProps={iconAnimatedProps} size={20)} />

animatedProps is calculated like this

const iconAnimatedProps = useAnimatedProps<FontAwesome6IconProps>(() => ({
    name: iconName.value,
    color: fontColor.value
  }))

color is working fine but name prop is not changing even through iconName.value is changing

this is how iconName is calculated

const iconName = useDerivedValue(() => {
    if (isActive) {
      const foundPointIndex = points.findIndex((point) => point.date === chartState.x.value.value)

      if (foundPointIndex === -1) return 'minus'

      const previousPoint = points[foundPointIndex - 1]

      if (!previousPoint) return 'minus'

      if (chartState.y.value.value.value > previousPoint.value) return 'caret-up'

      if (chartState.y.value.value.value < previousPoint.value) return 'caret-down'

      return 'minus'
    }

    const lastPoint = points[points.length - 1]
    const previousPoint = points[points.length - 2]

    if (!lastPoint || !previousPoint) return 'minus'

    if (lastPoint.value > previousPoint.value) return 'caret-up'

    if (lastPoint.value < previousPoint.value) return 'caret-down'

    return 'minus'
  })

Demo

You can use https://snack.expo.io/ to create a demo that can help users to better understand your problem.

AuroPick commented 2 months ago

Okay I found a way. Instead of using font component I created a text input like suggested in reanimated website

https://docs.swmansion.com/react-native-reanimated/examples/slider. You can found at the bottom of the page

const iconName = useDerivedValue(() => {
    if (isActive) {
      const foundPointIndex = points.findIndex((point) => point.date === chartState.x.value.value)

      if (foundPointIndex === -1) return String.fromCodePoint(61544)

      const previousPoint = points[foundPointIndex - 1]

      if (!previousPoint) return String.fromCodePoint(61544)

      if (chartState.y.value.value.value > previousPoint.value) return String.fromCodePoint(61656)

      if (chartState.y.value.value.value < previousPoint.value) return String.fromCodePoint(61655)

      return String.fromCodePoint(61544)
    }

    const lastPoint = points[points.length - 1]
    const previousPoint = points[points.length - 2]

    if (!lastPoint || !previousPoint) return String.fromCodePoint(61544)

    if (lastPoint.value > previousPoint.value) return String.fromCodePoint(61656)

    if (lastPoint.value < previousPoint.value) return String.fromCodePoint(61655)

    return String.fromCodePoint(61544)
  })

I got the numbers from glyphmaps folder https://github.com/oblador/react-native-vector-icons/tree/e3e7ba7cf27ca38152e8994eeffd9afec7a1b97b/glyphmaps

got the font family like this

const fontAwesome6FontFamily = (FontAwesome6Icon as any).getFontFamily('solid') as Font

used it like this

<AnimatedDescription
              fontFamily={fontAwesome6FontFamily}
              styleOverride={styles.icon}
              fontColor={changeFontColor}
              text={iconName}
              fontSize={wp(5)}
            />

this is the AnimatedDescription component

import React, { memo } from 'react'

import { ColorValue, StyleSheet, TextInput, TextStyle } from 'react-native'
import Animated, {
  useAnimatedProps,
  useAnimatedStyle,
  type SharedValue
} from 'react-native-reanimated'

import { Font } from '../../core/font'
import { useDynamicColors } from '../../hooks/useDynamicColors'

const AnimText = Animated.createAnimatedComponent(TextInput)

Animated.addWhitelistedNativeProps({ text: true })

type AnimatedDescriptionProps = {
  text: SharedValue<string>
  styleOverride?: TextStyle
  fontSize?: number
  fontColor?: SharedValue<ColorValue>
  numberOfLines?: number
  textCenter?: boolean
  fontFamily?: Font
}

function AnimatedDescriptionComponent({
  text,
  styleOverride,
  fontSize,
  fontColor,
  numberOfLines,
  textCenter,
  fontFamily
}: AnimatedDescriptionProps) {
  const dynamicColors = useDynamicColors()

  const animProps = useAnimatedProps(() => ({
    text: text.value,
    defaultValue: text.value
  }))

  const textStyle = useAnimatedStyle(
    () => ({
      ...styles.text,
      color: dynamicColors.text,
      ...(fontSize && { fontSize }),
      ...(fontColor && { color: fontColor.value }),
      ...(textCenter && styles.center),
      ...(fontFamily && { fontFamily }),
      ...(styleOverride && styleOverride)
    }),
    [styleOverride, fontSize, fontColor, textCenter, fontFamily, dynamicColors.text]
  )

  return (
    <AnimText
      style={textStyle}
      // @ts-ignore
      animatedProps={animProps}
      editable={false}
      numberOfLines={numberOfLines}
    />
  )
}

export const AnimatedDescription = memo(AnimatedDescriptionComponent)

const styles = StyleSheet.create({
  text: {
    fontFamily: 'Inter-Regular',
    fontSize: 12,
    flexShrink: 1
  },
  center: {
    textAlign: 'center'
  }
})