-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Shared style #1470
Shared style #1470
Conversation
Awesome! I've been having huge performance problems with reanimated v2 since I am animating multiple views with a sort of parallax effect, I'm sure this is a big step towards solving those performance issues. 👍 |
hey @mrousavy can you share us these performance problems in an issue? perhaps it's the same problem I had recently |
@terrysahaidak Yeah, here's a video demo (imgur.com). The structure of this view is the following: So the whole horizontal view which contains each individual story screen is translated along the X axis depending on the swipe gesture. Then, I've tried to implement some sort of cool parallax effect with the headers, by making them go into the next screen by a tiny bit, to reveal to the user what the next page has to offer. The problem here is, that I can only achieve that by translating each header individually, depending on the carousel's I tried to play around with virtualization and eventually came up with a way to unmount every header that's not in viewport (and used |
Can you resolve the hooks file conflict? |
Do we really need native set support for this one? Why can't we make an array of descriptors and just assign it in one go instead of making changes with every object being added. I haven't checked the full source but it seem problematic that we use set.add and never use set.remove in this PR. |
Finally, works and ready for review, I would be grateful for any suggestions. |
When this is ready, will it also work for useAnimatedProps? I didn't see issues reporting that the same need for sharing is present with animated props, so I wasn't sure if that had also been considered. |
Hate to "bump" this, but do you guys think this will make it into 2.3.0? @piaskowyk @jakub-gonet let me know if you need any help testing this change 🙏 |
The good news, this PR will be release in 2.3 🎉 |
Yes, in Reanimated 2
|
@piaskowyk Any sight on a alpha.2 release for 2.3? I would love to get started with this. |
This is not working for me in 2.3.0-beta.3 |
## Description We can't use just initial style as the default style in every render because these styles can be outdated. We can't change the default style after the first render, because after the second render we don't run mapper that's why the component can change the style to the initial value. Related: - #2580 - #2431 - #2406 - #1470 ### code Before https://user-images.githubusercontent.com/36106620/158142874-a11191e7-c0d9-4c3f-8f18-e5b540a6f17c.mov https://user-images.githubusercontent.com/36106620/158167177-81dfa334-db01-4e04-a234-e1069e8d715b.mov After https://user-images.githubusercontent.com/36106620/149799832-b0c0748d-2d9d-42b9-b9ba-f6492cc1fbf0.mov <details> <summary>code</summary> ```js import Animated, { useSharedValue, withTiming, useAnimatedStyle, Easing, } from 'react-native-reanimated'; import { View, Button } from 'react-native'; import React, { useState } from 'react'; export default function AnimatedStyleUpdateExample(props:any) { const randomWidth = useSharedValue(10); const [counter, setCounter] = useState(0); const [counter2, setCounter2] = useState(0); const [itemList, setItemList] = useState([]); const [toggleState, setToggleState] = useState(false); const config = { duration: 500, easing: Easing.bezier(0.5, 0.01, 0, 1), }; const style = useAnimatedStyle(() => { return { width: withTiming(randomWidth.value, config), }; }); const staticObject = <Animated.View style={[ { width: 100, height: 3, backgroundColor: 'black', margin: 1 }, style, ]} /> const renderItems = () => { let output = [] for(let i = 0; i < counter; i++) { output.push( <Animated.View key={i + 'a'} style={[ { width: 100, height: 3, backgroundColor: 'blue', margin: 1 }, style, ]} /> ) } return output } return ( <View style={{ flex: 1, flexDirection: 'column', marginTop: 30 }}> <Button title="animate" onPress={() => { randomWidth.value = Math.random() * 350; }} /> <Button title="increment counter" onPress={() => { setCounter(counter + 1) }} /> <Button title="add item to static lists" onPress={() => { setCounter2(counter2 + 1) setItemList([...itemList, <Animated.View key={counter2 + 'b'} style={[ { width: 100, height: 3, backgroundColor: 'green', margin: 1 }, style, ]} />]) }} /> <Button title="toggle state" onPress={() => { setToggleState(!toggleState) }} /> <Animated.View style={[ { width: 100, height: 3, backgroundColor: 'orange', margin: 1 }, style, ]} /> {toggleState && <Animated.View style={[ { width: 100, height: 3, backgroundColor: 'black', margin: 1 }, style, ]} />} {toggleState && staticObject} {renderItems()} {itemList} </View> ); } ``` </details> ### code2 Still works https://user-images.githubusercontent.com/36106620/149800303-4c4316aa-7765-4c66-a81a-74489d9f0215.mov <details> <summary>code2</summary> ```js import React from 'react'; import { View } from 'react-native'; import Animated, { useSharedValue, withSpring, useAnimatedStyle, useAnimatedGestureHandler, interpolate, Extrapolate, runOnJS, } from 'react-native-reanimated'; import { PanGestureHandler, PanGestureHandlerGestureEvent, } from 'react-native-gesture-handler'; import { useEffect, useState } from 'react'; function DragAndSnap(): React.ReactElement { const translation = { x: useSharedValue(0), y: useSharedValue(0), }; type AnimatedGHContext = { startX: number; startY: number; }; // run a couple of updates when gesture starts const [counter, setCounter] = useState(0); const makeFewUpdates = () => { let countdown = 100; const doStuff = () => { setCounter(countdown); countdown--; if (countdown > 0) { requestAnimationFrame(doStuff); } }; doStuff(); }; const gestureHandler = useAnimatedGestureHandler< PanGestureHandlerGestureEvent, AnimatedGHContext >({ onStart: (_, ctx) => { ctx.startX = translation.x.value; ctx.startY = translation.y.value; runOnJS(makeFewUpdates)(); }, onActive: (event, ctx) => { translation.x.value = ctx.startX + event.translationX; translation.y.value = ctx.startY + event.translationY; }, onEnd: (_) => { translation.x.value = withSpring(0); translation.y.value = withSpring(0); }, }); const stylez = useAnimatedStyle(() => { const H = Math.round( interpolate(translation.x.value, [0, 300], [0, 360], Extrapolate.CLAMP) ); const S = Math.round( interpolate(translation.y.value, [0, 500], [100, 50], Extrapolate.CLAMP) ); const backgroundColor = `hsl(${H},${S}%,50%)`; return { transform: [ { translateX: translation.x.value, }, { translateY: translation.y.value, }, ], backgroundColor, }; }); // make render slower let f = 0; for (var i = 0; i < 1e8; i++) { f++; } return ( <View style={{ flex: 1, margin: 50 }}> <PanGestureHandler onGestureEvent={gestureHandler}> <Animated.View style={[ { width: 40, height: 40, }, stylez, ]} /> </PanGestureHandler> </View> ); } export default DragAndSnap; ``` </details>
Description
I added the possibility to use one instance of
useAnimationStyle()
for many components.AnimationStyle
is connected with view by ViewDescriptor object. I changed this to a set ofViewDescriptors
and nowAnimationStyle
can be shared between many components.Fixes #1263
Example of code
code
code2
Change
before changes:
final result: