Inconsistent delay time for executing code - reactjs

I'm trying to write some code to play animations with the help of the react package framer-motion:
import React from "react";
import './Line.css';
import { motion, useAnimation } from 'framer-motion';
export const Line = ({speed}) => {
const [goingRight, setGoingRight] = React.useState(false);
const [delay, setDelay] = React.useState(1);
const [oldTime, setOldTime] = React.useState(new Date().getTime());
const controls = useAnimation()
React.useEffect(() => {
controls.start({
x: 280,
transition: { delay: 0, duration: speed, type: 'tween', ease: 'linear' }
})
}, [])
const start = async () => {
setDelay((new Date().getTime()) - oldTime);
setOldTime(new Date().getTime());
await setGoingRight(!goingRight);
if (goingRight) {
await controls.start({
x: 280,
transition: { delay: 0, duration: speed, type: 'tween', ease: 'linear' }
})
} else {
await controls.start({
x: 0,
transition: { delay: 0, duration: speed, type: 'tween', ease: 'linear' }
})
}
}
return (
<>
{delay}
<div className='outer'>
<motion.div className='ball'
animate={controls}
onAnimationComplete={() => start()}
/>
<div className='line'/>
</div>
</>
)
}
This component renders a horizontal line and a circle that sits on that line, the circle starts at the left side of the line and is animated to the right. After that, another animation is made to move the circle back to the left and then the cycle continues.
Each animation takes as long as the speed property (in seconds).
I'm also displaying the delay state variable which does the appropriate math to so determine how many milliseconds the most recent animation took since the last animation.
Here's my problem, let's say I render this component with a speed property of 1. In a perfect world, delay would always = 1000, but of course, there's some time loss for executing the code in the start() function so delay usually = 1017 instead. That would be fine because I could account for the difference, but strangely, delay is sometimes exactly 1000 instead (around 1 in every 4 animations it seems, but it's not consistent).
I need these animation times to be precise and this is too much of a deviation for what I need and I can't seem to figure out how to fix it. I have a suspicion this might be just be the nature of doing all this in real time and that it may be an unsolvable problem, but I figured I'd ask in case anyone has found themselves in a scenario like this before.
Instead of using multiple animation that are run after another, I've tried just using one infinitely repeating animation like this:
controls.start({
x: 280,
transition: { delay: 0, duration: speed, type: 'tween', ease: 'linear' repeat: Infinity, repeatType: 'reverse', }
})
The problem with doing this is that I need code to run after the circle hits the end of the line every time, which before I was using onAnimationComplete to accomplish, but onAnimationComplete seems to only be triggered when the entire animation completes, not also when it repeats

Related

framer-motion custom hover variant is affected by a delay from another variant

I have a set of variants defined as such:
const letterVariants: Variants = {
visible: (custom: number) => ({
opacity: 1,
y: 0,
transition: {
delay: custom * 0.1,
},
}),
hidden: {
opacity: 0,
y: 100,
},
hover: {
y: -30,
transition: {
delay: 0,
},
},
};
which are used for the hero title:
<h1 className="font-peralta">
{letters.map((letter, i) => (
<motion.div
className="inline-block"
variants={letterVariants}
custom={i}
initial="hidden"
animate="visible"
whileHover="hover"
key={nanoid()}
>
{letter}
</motion.div>
))}
</h1>
On initial render, each letter of the hero title fades in from the bottom. I am trying to make it also that when I hover (before or after animation end), the letters slide up a bit. The problem I am facing is that the custom transition delay that allows for the letters to pop up one-by-one is also applied to the hover variant. So when I hover over the first letter, it moves up and down immediately, if I hover over the last letter, it goes up but takes x * 0.1 seconds to return to its normal position.
It's almost exactly the same as in this post except that solution has not worked for me.

How to change colors using React-Spring. I would like to include delay and duration

I am using react-spring. I am changing the background color which is working however I am hoping to do the following. Wait for 10 seconds then after those 10 seconds loop through the colors without the delay. I would also like to have a 1 second duration of each color.
currently my issue is that it waits 10 seconds then it starts to loop but makes me wait 10 seconds between every color change. Not sure what I am doing wrong
import {useSpring, animated} from 'react-spring';
const AnimatedView = animated(View);
const bgStyle = useSpring({
to: {backgroundColor: 'red'},
from: {backgroundColor: 'green'},
delay: 10000,
config: {duration: 1000},
loop: true,
});
return <AnimatedView style={bgStyle}>{props.children}</AnimatedView>;
I think we need to wait for 10s once at first. But the delay prop of useSpring makes us wait for 10s at the start of each animation. Because the value of delay is not updating, it is always 10000.
Here we can take a useRef reference to track our initial delay. By checking this we can return a delay of 10000 at first and then 0 for the rest.
const initialDelay = useRef(true);
const bgStyle = useSpring({
from: { backgroundColor: "green" },
to: { backgroundColor: "red" },
delay: () => {
if (initialDelay.current) {
initialDelay.current = false;
return 10000;
}
return 0;
},
config: { duration: 1000 },
loop: true
});

How to add animations between pages in gartsby with Gsap

I want to add animation between pages in gatsby using Gsap.
Im trying to follow their docs: https://www.gatsbyjs.com/docs/how-to/adding-common-features/adding-page-transitions-with-plugin-transition-link/
At some point they say: Finally, import the TransitionLink component wherever you want to use it...
https://www.gatsbyjs.com/docs/how-to/adding-common-features/adding-page-transitions-with-plugin-transition-link/#getting-started
But I dont know where to use, should it be used in the actual pages components or in the Layout component?(I want to have disfferent animations for each page though)
I want to use the trigger function:
https://www.gatsbyjs.com/docs/how-to/adding-common-features/adding-page-transitions-with-plugin-transition-link/#using-the-trigger-function
Because I want to use Gsap.
But then they just write in the code "someCustomDefinedAnimation" but I dont know where is that coming from, how do i create one and how do I pass it through props.
<TransitionLink
exit={{
length: length,
trigger: ({ exit, node }) =>
this.someCustomDefinedAnimation({ exit, node, direction: "out" }),
}}
entry={{
length: 0,
trigger: ({ exit, node }) =>
this.someCustomDefinedAnimation({ exit, node, direction: "in" }),
}}
{...props}
>
{props.children}
</TransitionLink>
So my questions are:
Where should I add the TransitionLink component?
How do I create a custom Gsap animation?
Where and how do i pass animation this as props so that TransitionLink can use it?
But then they just write in the code someCustomDefinedAnimation but
I dont know where is that coming from, how do i create one and how do
I pass it through props.
Well, someCustomDefinedAnimation is just a defined function that contains the animation. For example:
verticalAnimation = ({ length, direction}) => {
const directionTo = direction === 'up' ? '-100%' : '100%'
const directionFrom = direction === 'up' ? '100%' : '-100%'
// convert ms to s for gsap
const seconds = length
return gsap.timeline()
.set(this.transitionCover, { y: directionFrom })
.to(this.transitionCover, {
y: '0%',
ease: "power1.easeInOut",
duration: seconds / 2,
})
.set(this.layoutContents, { opacity: 0 })
.to(this.transitionCover, {
y: directionTo,
ease: "power1.easeIn",
duration: seconds / 2,
})
}
Then you'll need to use it as:
<TransitionLink
exit={{
length: length,
trigger: ({ exit, node }) =>
this.verticalAnimation({length: 100, direction: 'up'}),
}}
entry={{
length: 0,
trigger: ({ exit, node }) =>
this.verticalAnimation({length: 100, direction: "down" }),
}}
{...props}
>
{props.children}
</TransitionLink>
Basically, it allows you to fully customize the function triggered in each page transition
You can find a detailed explanation and walkthrough in TransitionLink docs and an implementation example at their GitHub repository.

React Router + React Pose slide in/out with "POP" or "PUSH" direction

I've been having this issue for over a month, getting back to it with new ideas over and over, but I can't wrap my head around a solution. There must be one out there, this is trivial!
My goal is to normally slide pages in from left and slide out to right. When it reverses (user click back button or history notices a route has been popped) the page should exit to the right and next route should enter from left (opposite).
Everything works, but when I PUSH a route and then POP a route, the next page will both exit to right and enter from right. The entering page uses an old pose before it has time to update. If I POP another time, it then works correctly, and the new page slides from left while the old page exits to right. It's only the first time I change with a POP. I'm very dependent on the const reverse value. The component only renders once when I change route and reverse = true the first time I click POP.
I have an AnimatedSwitch component like this
export const AnimatedSwitch = ({ location, children, ...rest }) => {
const clickedBack = useSelector(state => state.interfaceReducer.clickedBack);
const routePopped = useSelector(state => state.routeReducer.routePopped);
const reverse = clickedBack || routePopped;
const onRest = () => {
clickedBack && dispatch(toggleClickedBackButton(false));
}
return (
<PoseGroup
animateOnMount={true}
preEnterPose={reverse ? "leftSide" : "rightSide"}
exitPose={reverse ? "rightSide" : "leftSide"}
onRest={onRest}
reverse={reverse}
>
<ContextRouteAnimation poseKey={`${reverse}-${location.pathname}`} key={location.pathname} reverse={reverse}>
<Switch location={location} {...rest}>
{children}
</Switch>
</ContextRouteAnimation>
</PoseGroup>
)
}
And this is the ContextRouteAnimation pose component:
const ContextRouteAnimation = posed.div({
enter: {
opacity: 1,
x: "0%",
transition: {
type: "tween",
ease: "easeInOut",
duration: TIMING.SLOW,
},
},
leftSide: {
x: "-100%",
opacity: 0,
transition: {
type: "tween",
ease: "easeInOut",
duration: TIMING.SLOW,
},
},
rightSide: {
x: "100%",
opacity: 0,
transition: {
type: "tween",
ease: "easeInOut",
duration: TIMING.SLOW,
},
},
});
I've tried multiple things such as
Pass reverse down to ContextRouteAnimation and use preEnter + exit poses.
Set pose directly on ContextRouteAnimation
Store component as state and only update it after new pose is updated, and then trigger another render

React Native flexDirection animation

I am trying to to an animation which basically changes flexDirection of a view. Check the code
state = {
fadeAnim: new Animated.Value(0),
flex_direction_anim: new Animated.Value("column")
}
componentDidMount() {
Animated.timing(
this.state.fadeAnim,
{
toValue: 1,
duration: 2000
}
).start();
setTimeout(() => {
Animated.timing(
this.state.flex_direction_anim, {
toValue: "row",
duration: 2000
}
)
}, 5000)
}
In above code i have two animations set, fade animation is working fine, but the other animation just throws following error.
I am not sure what i am doing wrong here, may be this kind of animation is not supported in React-Native.
I have also used react-native-animatable library, and also opened an issue here
Any documentation or lead will be very helpful,
Thanks

Resources