-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Animated Path d attribute #951
Comments
If you're fine with using Animated without useNativeDriver, then it should work as is. If you want the native driven animation, you'll need to use a PR I made but didn't get merged yet/hasn't been rebased to the latest version of react-native: facebook/react-native#18187 |
I just tried rebasing to the latest 0.59 release candidate and it seems to work fine in both iOS and Android. You can try it using: Commit: Diff to react-native 0.59-stable (if you want to rebase/apply it yourself to e.g. 0.58, or adapt it to the master branch): And e.g. this: import React, { Component } from 'react';
import { StyleSheet, View, Dimensions, Animated } from 'react-native';
import { Svg, Rect, Path } from 'react-native-svg';
const { width, height } = Dimensions.get('window');
const AnimatedRect = Animated.createAnimatedComponent(Rect);
const AnimatedPath = Animated.createAnimatedComponent(Path);
function getInitialState() {
const anim = new Animated.Value(0);
const fillOpacity = anim.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
});
const offset = fillOpacity.interpolate({
inputRange: [0, 1],
outputRange: [0, 10],
});
const strokeOpacity = offset.interpolate({
inputRange: [0, 10],
outputRange: [0, 1],
extrapolateRight: 'clamp',
});
const path = anim.interpolate({
inputRange: [0, 1],
outputRange: ['M20,20L20,80L80,80L80,20Z', 'M40,40L33,60L60,60L65,40Z'],
});
const fill = strokeOpacity.interpolate({
inputRange: [0, 1],
outputRange: ['rgba(255, 0, 0, 0.5)', 'rgba(0, 255, 0, 0.99)'],
});
const oneToFivePx = offset.interpolate({
inputRange: [0, 10],
outputRange: ['1px', '5px'],
});
return { anim, fillOpacity, offset, strokeOpacity, path, fill, oneToFivePx };
}
export default class App extends Component {
state = getInitialState();
componentDidMount() {
const { anim } = this.state;
Animated.timing(anim, {
toValue: 1,
duration: 10000,
useNativeDriver: true,
}).start();
}
render() {
const { fillOpacity, offset, strokeOpacity, path, fill, oneToFivePx } = this.state;
return (
<View style={styles.container}>
<Svg width={width} height={height} viewBox="0 0 100 100">
<AnimatedRect
x="5"
y="5"
width="90"
height="90"
stroke="blue"
fill={fill}
strokeDasharray="1 1"
strokeWidth={oneToFivePx}
strokeDashoffset={offset}
strokeOpacity={strokeOpacity}
fillOpacity={fillOpacity}
/>
<AnimatedPath
d={path}
stroke="blue"
/>
</Svg>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#ecf0f1',
},
}); |
Will passing an Animated value to an 'arc' from 'd3-shape' and using that as a d property for Path, work with nativeDriver: true? |
You can use arc from d3-shape to create path data strings, and use Animated.Value to interpolate between them. But, animated doesn't support scientific notation of numbers represented as strings, so you'll need to convert to fixed notation using something like this, or make a pr to react-native import React, { Component } from 'react';
import { StyleSheet, View, Animated } from 'react-native';
import { Svg, Circle, Path, G } from 'react-native-svg';
import { arc } from 'd3-shape';
const AnimatedPath = Animated.createAnimatedComponent(Path);
function convertToFixed(m, integer, decimal = '', sign, power) {
const fixed = integer + decimal;
return sign === '+'
? fixed + '0'.repeat(power - decimal.length)
: '0.' + '0'.repeat(power - 1) + fixed;
}
function exponentialToFixedNotation(num) {
return num.replace(/(\d)(?:\.(\d+))?e([+-])(\d+)/, convertToFixed);
}
function getInitialState() {
const anim = new Animated.Value(0);
const arcGenerator = arc();
const range = [
arcGenerator({
innerRadius: 0,
outerRadius: 10,
startAngle: 0,
endAngle: Math.PI / 2,
}), // "M6.123233995736766e-16,-10A10,10,0,0,1,10,0L0,0Z"
arcGenerator({
innerRadius: 0,
outerRadius: 100,
startAngle: 0,
endAngle: Math.PI / 2,
}), // "M6.123233995736766e-15,-100A100,100,0,0,1,100,0L0,0Z"
];
const outputRange = range.map(exponentialToFixedNotation);
// ["M0.0000000000000006123233995736766,-10A10,10,0,0,1,10,0L0,0Z", "M0.000000000000006123233995736766,-100A100,100,0,0,1,100,0L0,0Z"]
const path = anim.interpolate({
inputRange: [0, 1],
outputRange,
});
return { anim, path };
}
export default class App extends Component {
state = getInitialState();
componentDidMount() {
const { anim } = this.state;
Animated.timing(anim, {
toValue: 1,
duration: 3000,
}).start();
}
render() {
const { path } = this.state;
return (
<View style={styles.container}>
<Svg
width="50%"
height="50%"
viewBox="0 0 200 200"
style={{ borderWidth: 1, borderColor: 'black', borderStyle: 'solid' }}
>
<G transform="translate(100, 100)">
<Circle r="100" fill="none" stroke="black" />
<AnimatedPath d={path} stroke="blue" />
</G>
</Svg>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#ecf0f1',
justifyContent: 'center',
alignItems: 'center',
flex: 1,
},
}); |
Thanks for the prompt reply.
I'm hoping that it works with your PR for string interpolation for d attribute is merged. I will test it out once I upgrade to RN 0.59 as currently I'm using 0.57. P.S. I'm a fan of your work and this library. Keep it up! |
@msand just check your example with https://github.com/facebook/react-native/pull/24177/files |
@msand i found issue (check only ios impletementation)
we get something like: fix:
and it's work! =) |
@matpaul Thanks for the fix! There was also an issue on android. I've pushed a fix to my fork/pr: facebook/react-native@86052af |
@yodaheis I've added support for scientific notation to the string interpolation now, so the exponentialToFixedNotation function isn't needed anymore. facebook/react-native@19bba43 |
@msand testing some animation in my project - there is an issue when two path is not consistent between, for example end path: Error occure: invalid pattern I think about function that take two path and create 0 for all missing points What do you think? |
@msand found another issue when animate d using native driver, and then delete it - fatal issue |
Can you post a minimal reproduction? |
import { arc } from 'd3-shape';
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { Animated, Button, StyleSheet, View } from 'react-native';
import { Circle, G, Path, Svg } from 'react-native-svg';
const AnimatedPath = Animated.createAnimatedComponent(Path);
const arcGenerator = arc();
function ReproduceIssue() {
const [isShown, setShown] = useState(true);
const animatedValue = useMemo(() => new Animated.Value(0), []);
const [startPath, endPath] = useMemo(
() => [
arcGenerator({
innerRadius: 0,
outerRadius: 10,
startAngle: 0,
endAngle: Math.PI / 2,
}),
arcGenerator({
innerRadius: 0,
outerRadius: 100,
startAngle: 0,
endAngle: Math.PI / 2,
}),
],
[]
);
useEffect(() => {
const loop = Animated.loop(
Animated.sequence([
Animated.timing(animatedValue, {
toValue: 1,
duration: 3000,
useNativeDriver: true,
}),
Animated.timing(animatedValue, {
toValue: 0,
duration: 3000,
useNativeDriver: true,
}),
])
);
loop.start();
return () => loop.stop();
}, []);
return (
<View style={styles.container}>
<Button
title={'Hide/Show'}
onPress={useCallback(() => setShown(!isShown), [isShown])}
/>
{isShown && (
<Svg
width="50%"
height="50%"
viewBox="0 0 200 200"
style={styles.svgContainer}
>
<G transform="translate(100, 100)">
<Circle r="100" fill="none" stroke="black" />
<AnimatedPath
d={animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [startPath, endPath],
})}
stroke="blue"
/>
</G>
</Svg>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#ecf0f1',
justifyContent: 'center',
alignItems: 'center',
width: 500,
height: 500,
},
svgContainer: { borderWidth: 1, borderColor: 'black', borderStyle: 'solid' },
});
export default memo(ReproduceIssue); |
@msand forgot to say, this is only on Android! - on iOS everything is ok |
@msand Hi!, I try to research this issue, so i found that restoreDefaultValues method make d: null PropsAnimatedNode.java
I don't know how to properly fix it but simple if check in PathView works )) PathView.java
|
Always get
|
@headfire94 hi, do you patch react-native with facebook/react-native#24177 ? invalid pattern issue: |
No, i will try. Thanks for response |
@headfire94 use patch-package, also note that ios patch is simple, android is not =) |
color animations with this package work great with Animated from RN but it doesn't seem to support |
RN 0.61 includes my patches to Animated, allowing useNativeDriver animations of svg paths using only react-native and react-native-svg, no extra dependencies needed. It also includes some fixes for edge-cases and to make it easier to animate several properties at once per element, and share animations among several elements and styles. Closing this now. |
@wcandillon Could you please open a new issue with a repro for the color compatibility with reanimated? |
Hi, I stumbled onto this issue regarding my problem: #908
In my case, I would prefer to use the react native animated library and not react-native-reanimated.
I woule like to interpolate on multiple points from an animated.Value() and forge my d attribute from it.
Is it supported ?
Maybe is there a better way, I want to have a 'rising' animation.
so a path starting at y 0 and rising progressively.
The text was updated successfully, but these errors were encountered: