wcandillon / react-native-redash

The React Native Reanimated and Gesture Handler Toolbelt
https://wcandillon.gitbook.io/redash/
MIT License
1.96k stars 117 forks source link

Parsing paths always results in error: undefined is not an object (evaluating 'p.curves[index].c1') #462

Open haibert opened 3 years ago

haibert commented 3 years ago

Hello all,

Im trying to interpolate between two different SVG Paths. Im using the example code from the documentation but when I replace the SVG path string provided in the examples with any other SVG path I get the following error. The parser seems to have some sort of limitation i'm not aware of ? Its just a simple path of a home icon.

import React from 'react'
import { View } from 'react-native'
import Animated, {
    useSharedValue,
    useAnimatedProps,
    withTiming,
    Easing,
} from 'react-native-reanimated'
import Svg, { Path } from 'react-native-svg'

import { interpolatePath, parse } from 'react-native-redash'

const AnimatedPath = Animated.createAnimatedComponent(Path)

Animated.addWhitelistedNativeProps({
    stroke: true,
})

// THIS PATH GIVE CAUSES THE ERROR
const PATH1 = parse(
    'M 12 2.0996094 L 1 12 L 4 12 L 4 21 L 11 21 L 11 15 L 13 15 L 13 21 L 20 21 L 20 12 L 23 12 L 12 2.0996094 z M 12 4.7910156 L 18 10.191406 L 18 11 L 18 19 L 15 19 L 15 13 L 9 13 L 9 19 L 6 19 L 6 10.191406 L 12 4.7910156 z'
)

// THIS PATH PROVIDED BY THE DOCUMENTATION WORKS
const PATH2 = parse(
    'M150,0 C175,0 0,100 100,75 M150,200 200,225 200,225 C225,200 200,150 0,150  '
)
const HomeSVG = ({ color, size }) => {
    const progress = useSharedValue(0)

    const animatedProps = useAnimatedProps(() => {
        const d = interpolatePath(progress.value, [0, 1], [PATH1, PATH2])
        return { d }
    })

    return (
        <View
            onTouchStart={() => {
                progress.value = withTiming(progress.value ? 0 : 1, {
                    duration: 1000,
                    easing: Easing.inOut(Easing.cubic),
                })
            }}
        >
            <Svg width={size} height={size} viewBox="0 0 24 24">
                <AnimatedPath
                    stroke="black"
                    strokeWidth={1}
                    fill="blue"
                    fillRule="evenodd"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                    animatedProps={animatedProps}
                />
            </Svg>
        </View>
    )
}

export default HomeSVG
wcandillon commented 3 years ago

I looks like this is because the path is closed more than once, I'm looking to see if something can be done there.

wcandillon commented 3 years ago

I added a test case for $PATH1 and it seems to be parsed properly. Could the issue be from the interpolatePath? Paths needs to have the same number of commands to be interpolated.

haibert commented 3 years ago

Ty for looking into the issue William!

I can confirm that the difference between the number of commands is the reason the interpolatePath fails, but this is strange. I have prepared a snack that show you how this limitation doesn't exist when using the interpolator from flubberimport { interpolate } from 'flubber'. I just don't think this library works with reanimated 👎🏼

If you run the Snack you will see that you can interpolate from one SVG to another that has different number of commands using the original react native Animated API, along with flubbers interpolator. Im tying to achieve the same results.. only with reanimated.

https://snack.expo.dev/@haibert/svg-path-interpolation

wcandillon commented 3 years ago

redash is not nearly as sophisticated than flubber to do path interpolations but thanks for pointing me to flubber, I will definitely take inspiration from it to make the interpolation more flexible.

haibert commented 3 years ago

Thank you William! Also you could check out interpolatePath from d3-interpolate-path I think that was is a good one as well.