how to animate text when it updates using react-spring? - reactjs

in a react.js App,
i have some simple text stored in the App state , and passed to a child component who will display it directly .
i want to animate the transition so that when it changes , the previous text will fade out and then the incoming text will fade in .
how is that possible using react-spring ?

You can put it into a transition and it will handle it for you:
const transitions = useTransition(text, null, {
from: { opacity: 0 },
enter: { opacity: 1 },
leave: { opacity: 0 }
})
return transitions.map(({ item, key, props }) => (
<animated.div style={props}>{item}</animated.div>
))
Make sure the div is positioned absolutely if you want text phrases to overlap one another.

Related

react-custom-scrollbar-2 event on scroll top

I'm trying to implement on click scroll to top in react, but since I'm using custom scrollbar 2 I couldn't able to achieve it. The pageYoffset value is always 0 since I made the parent component max-height as 100vh and child component as scrollable. Here is what I got till now
<Scrollbars onScrollFrame={handleScroll}>
<ChildComponent/>
{
showButton && <button onClick={handleClick}><button/>
}
</Scrollbars>
In handleScroll
const {top} = values;
if(top > 0.02){
setShowButton(true);
}else{
setShowButton(false);
}
Depending on the top value I can show the button now I want to implement the scroll function when the button got clicked and scroll to the child component top
In handleClick I've tried to scroll it to top
window.scrollTo({ top: 0, behavior: "smooth" });
I've tried scrollIntoView with the top element as well it's not working.

Is it impossible to use Framer Motion on instant Search Results? Animating an updating .map() function

https://i.imgur.com/drQmJn5.mp4
The initial results are animated perfectly. Staggered fade-ins.
However, all results AFTER that just appear suddenly and without animation. I'm not understanding why the updated results aren't also animated.
The code currently looks like this:
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.3
}
}
};
const listItem = {
hidden: { opacity: 0},
show: { opacity: 1}
};
<PostList
variants={container}
initial="hidden"
animate="show"
as={motion.ul}>
{results && results.slice(0, 2).map((result, i) => (
<PostListItem
as={motion.li}
variants={listItem}
key={i}
> blah blah
</PostListItem>
))}
You should not use the loop index as the key in your PostListItem. Since the indices (and thus the keys) will always be the same, it doesn't let Framer track when elements have been added or removed in order to animate them.
Instead use a value like a unique id property for each element. This way React (and Framer) will know when it's rendering a different element (vs the same element with different data). This is what triggers the animations.
Here's a more thorough explanation:
react key props and why you shouldn’t be using index

How can I make the native controls on a React Native expo-av Video visible?

I need to know how to assign a function to an expo-av Video component's onPress event, or how to make a useRef reference trigger an onPress event on an expo-av Video component.
I have a lot of requirements for a React Native video component and I am trying to work out how to best achieve them all. The main issue is the user needs to be able to double-tap the video and have an animation fire. We have a Pressable component that renders over the video that handles a double tap. The animation works fine. But the problem is there's also a requirement that the video uses native controls, and the Pressable blocks those controls.
I have two strategies but I haven't been able to achieve either. Plan A, someone on StackOverflow tells me that expo-av Video components have some kind of onPress function not listed in their docs, and all I have to do is trigger the double tap animation using that function. So far I've tried onPress, onTouchEnd, onTouchStart, and onClick, and all are undefined and not listed in the docs. No dice.
Plan B, I can create a Pressable component that renders over the Video component that is short enough not to block the native controls, but tall enough to catch all other taps. Then, when a user double-taps the Pressable, the animation fires and everything's good. This is what I initially submitted as a solution, but our QA folks said they need the native controls to show on the first press. Normally the native controls are not visible and the expo-av Video shows them on any press, but the Pressable blocks those presses, and I cannot find anything in the docs that allows you to referentially trigger the expo-av press event. I have a useRef() working and have tried videoRef.current.press, videoRef.current.click, and videoRef.current.setStatusAsync({ progressUpdateIntervalMillis: 0 }). No dice. I just need the control buttons to show up like they normally do on a tap, nothing else.
So in summary, does anyone A: Know how to assign a function to the expo-av Video component's onPress event? or B: Know how to make a useRef reference trigger an onPress event on an expo-av Video component? or C: Some other solution I haven't thought of?
Here are some code snippets if it helps:
The Video instantiation:
<Video
ref={(video) => videoRef.current = video}
style={[styles.postMedia, { width: videoDimensions.width, height: videoDimensions.height }]}
source={{ uri: videoUrl }}
rate={1.0}
volume={1.0}
isMuted={isMuted}
resizeMode={Video.RESIZE_MODE_CONTAIN}
shouldPlay={shouldPlay || screenIsFocussed && (keyInView === _id)}
isLooping
useNativeControls
onError={(e) => setVideoUrl(`${getS3BaseUrl(video.s3Bucket)}/${video.url}`)}
onReadyForDisplay={(response) => {
const { width, height } = response.naturalSize;
if (width >= height) return setVideoDimensions({ width: screenWidth, height: screenWidth * height / width });
if (height > width) return setVideoDimensions({ height: screenWidth, width: screenWidth * width / height });
}}
/>
The handleDoubleTap function:
let lastTap = null;
const handleDoubleTap = () => {
const now = Date.now();
const DOUBLE_PRESS_DELAY = 300;
if (lastTap && (now - lastTap) < DOUBLE_PRESS_DELAY) {
toggleLike();
} else {
if (videoRef) videoRef.current.setStatusAsync({ progressUpdateIntervalMillis: 0 }); // <-- insert something that actually works here
lastTap = now;
}
};
The Pressable component instantiation:
{(video || image) && (
<Pressable
onPress={handleDoubleTap}
style={video ? {
...styles.videoLikePostFromFeedPressable,
height: IS_IOS ? DEVICE_WIDTH * 0.75 : DEVICE_WIDTH * 0.66,
} : {
...styles.likePostFromFeedPressable,
height: DEVICE_WIDTH,
}}
>
{renderOverlay()} {/* <-- This is just the animation render, works fine */}
</Pressable>
)}

Set value immediately after another one have been set in Reanimated 2

I am using Reanimated 2 to build a game with React Native its performance is incredibly good but I have a problem.
I am using a shared value to animate a View as we all know setting the value of the shared value will automatically change the style of the View, my problem is that let's say it will be a Button that the user presses to give the View an elevation simply by changing a shared value used in the animated style of the View, the elevation is simply translation in the y axis.
The elevation value is 0 at first. The user clicks the button the value changes to for example 500 immediately with no transition and no animation, the View will immediately show at 500 above its starting position. And from 500 the View will drop back to 0 with animation.
I tried the code below but no help.
const elevation = useSharedValue(0);
const handleClick = () => {
elevation.value = 500;
elevation.value = withTiming(0, { duration: 1000 });
}
const viewAnimatedStyle = useAnimatedStyle(() => ({
transform: [
{
translateY: elevation.value,
}
]
}))
when pressing the button the view doesn't move, it seems that Reanimated skips the first elevation.value assignment, and since the second assignment is to 0 (the same old value) the View doesn't move.
[Edit] Animated.View is imported from Reanimated 2 and used. I left it out for simplicity.
You need to use <Animated.View> for the useAnimatedStyle, If you are now using It will not be working correctly.
function App() {
const width = useSharedValue(50);
const animatedStyle = useAnimatedStyle(() => {
return {
width: width.value,
};
});
// attach animated style to a View using style property
return <Animated.View style={[styles.box, animatedStyle]} />;
}

How to start animation when props change (via redux) in React Native?

I have an overlayed view in my React Native app which I need to animate on and off screen when the user pushes a button. I know how to position the view and animate it but I can't work out how to trigger the animation.
My redux store has a very simple state with an isOpen flag saying whether the panel is open or closed. I map the state to the panel component's props and when the isOpen prop changes I want to trigger the open or close animation. Obviously if the user presses the toggle button mid animation the currently running animation needs to be cancelled.
This should be simple but I can't find any examples. Any help would be much appreciated.
React Native
To begin an animation on a change of props you can simply start your animation in componentDidUpdate. Here's an example:
componentDidUpdate(prevProps) {
if (this.props.isOpen !== prevProps.isOpen) {
this.state.animation.start();
}
}
Assuming your animation is defined in the component's state.
React (Browser):
[Not relevant to this question but potentially useful.]
A simple way to do this is using CSS transitions. What you can do is give the panel component a CSS class for closed (or open but I find using the closed/collapsed as a style easier because then the default is open).
Then in your panel's render:
render() {
const { isOpen } = this.props;
return <div className={ 'panel' + (isOpen ? '' : ' closed') }></div>
}
And in your CSS:
.panel {
/* some other styles */
transition: .5s ease-in;
}
.closed {
height: 0;
}
This way CSS can handle the animation logic and your concern of clicking the open/close button before the current animation has finished is addressed.
Here are the CSS transition docs: https://developer.mozilla.org/en-US/docs/Web/CSS/transition
Edit: A disadvantage of this method is that height must be explicitly set when the panel is open.
Here's a little example snippet:
function togglePanel() {
const panel = document.querySelector('div.panel');
if (panel.classList.contains('closed')) {
panel.classList.remove('closed');
} else {
panel.classList.add('closed');
}
}
.panel {
background-color: #00c0de;
height: 4rem;
overflow: hidden;
transition: .5s ease-in;
}
.closed {
height: 0;
}
<button onclick='togglePanel()'>Toggle Panel</button>
<div class='panel closed'>
<span>Hello, I'm the panel</span>
</div>

Resources