How to add animations between pages in gartsby with Gsap - reactjs

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.

Related

React - Product Fly to cart animation using framer-motion

i want an animation that takes the product Card and put it on the card like a fly animation and i want to use the framer-motion the problem that i have is when i set the x,y of the animation all of them goes to the same place
i tried the GSAP library but i didn't know how to manage it.
if there's any other libraries that i can use with explaination feel free to share
Here's my code :
const [xValue, setxValue] = useState(0);
const [yValue, setyValue] = useState(0);
const handleClick = async (p) => {
await set_order({
...order,
products: {
...order?.products,
[order?.products?.length]: { ...p, qte: 1 },
},
});
setxValue(45);
setyValue(45);
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1, x : xValue , y : yValue }}
transition={{ duration: 1 }}
exit={{ opacity: 0 }}
>
<Card
// ref={ref_card}
onClick={() => handleClick(product)}
i tried the gsap library and now im trying the framer motion
couldn't find that much resources that have this kinda of animation
i only found one that uses the gsap and i couldn't apply it
thank you for sharing any informations regarding this issue

How can I stop my google map reloading every time I try to do something else off the map?

I have React Page. I splitted the page in 2. In the first half I have a form with some fields, and In the second half I have a Google Map Component. I added a Polyline. The path of the polyline gets updated every time the user left clicks (add point) or right clicks (remove point), or if it's dragged. Everything works well until I leave the map and focus on inputs or press some buttons in the other half. Then if I try to add new points or remove them ( this is not working because it has no points to remove) it starts a new polyline.
My methods are very simple, I took them from the docs and they do the job.
const addLatLng = (event) => {
const path = poly.getPath();
path.push(event.latLng);
}
const removeVertex = (vertex) => {
let path = poly.getPath();
path.removeAt(vertex)
}
// eslint-disable-next-line no-undef
google.maps.event.addListener(poly, 'rightclick', function (event) {
if (event.vertex === undefined) {
return;
} else {
removeVertex(event.vertex)
}
})
<div style={{ height: '100vh' }}>
<GoogleMap
center={center}
zoom={3}
onLoad={(map) => setMap(map)}
mapContainerStyle={{ width: '100%', height: '100%' }}
onClick={(event) => { !showInputForLink && addLatLng(event) }}
>
</GoogleMap>
</div>
First methods do the action and this is how I declared the map.
const { isLoaded } = useJsApiLoader({
googleMapsApiKey: ct.MAPS_API_KEY,
libraries: ['drawing, places']
})
// eslint-disable-next-line no-undef
const [map, setMap] = useState(/** #type google.maps.Map */(null))
const poly = new google.maps.Polyline({
strokeColor: '#160BB9',
strokeOpacity: 1.0,
strokeWeight: 3,
editable: true,
});
poly.setMap(map)
And this is how I declared the poly.
I tried to look up in Docs to see if I missed something, but I couldn't find anything that will not lose the focus on the map when I do something else or to start another polyline.
I am not using different components, everything is in on file.
Should I declare a Poly component inside the map? and not use the traditional JavaScript method?
How can I create this without resetting the map when I do actions in the first half?

React - Framer motion: prevent initial prop from triggering

I have a basic CRUD application where I'm able to add and remove items on a list, which is animated. This works well with framer-motion, but I'm having troubles with the following:
Whenever I delete an item from the list, the following function is called:
const handleDeleteFormation = async (id) => {
await axios({
method: "DELETE",
url: `http://localhost:1337/formations/${id}`
})
getFormations()
}
As you can clearly see, I'm calling the getFormations() function after the deletion to re-render the updated list:
async function getFormations() {
const res = await axios.get('http://localhost:1337/formations')
setFormations(res.data)
console.log(res.data)
}
This will results in a re-render of the list, which is good. But... right after the re-render, the initial prop of the motion.div will be triggered again which results in conflicting visuals.
<AnimatePresence exitBeforeEnter initial={false}>
{formations && formations.map((formation, index) =>
<motion.div
key={formation.id}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -10 }}
transition={{ type: "tween", ease: 'easeOut', duration: 0.2 }}
>
...
My question: is it possible to prevent the initial prop from triggering right after the list-update?
You're looking for <AnimatePresence initial={false}>.
From the docs:
Suppressing initial animations
By setting initial={false} on AnimatePresence, components present when
AnimatePresence first loads will start in their animate state. Only
components that enter after this initial render will animate in.

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

Modal component renders Twice on open

I'm using react-spring to animate a Modal based on #reach/dialog. The Modal can have any children. In the children I'm fetching some data based on some prop.
The problem is that the fetch call is made two times on opening the modal. I think it has probably to do with how I'm managing the state and that is causing re-renders.
I'v tried memoizing the children inside the modal and it didn't work, so I think that the problem is outside of the Modal component.
Here is something close to my code and how it is working https://codesandbox.io/s/loving-liskov-1xouh
EDIT: I already know that if I remove the react-spring animation the double rendering doesn't happen, but I want to try keeping the animation intact.
Do you think you can help me to identify where is the bug? (Also some tips on good practice with hooks are highly appreciated).
it renders three times because your return component has transitions.map since you have three item inside the
from: { opacity: 0 }
enter: { opacity: 1 }
leave: { opacity: 0 }
the {children} was called two times when the isOpen is true
you can fix the issue with just removing the from: { opacity: 0 } and leave: { opacity: 0 }
so change your modal.js => transitions
const transitions = useTransition(isOpen, null, {
enter: { opacity: 1 }
});
I checked and it is rendered twice because of animation in a Modal component when an animation is finished, modal is rendered second time when I commented out fragment responsible for animation, Modal renders only once.
const Modal = ({ children, toggle, isOpen }) => {
// const transitions = useTransition(isOpen, null, {
// from: { opacity: 0 },
// enter: { opacity: 1 },
// leave: { opacity: 0 }
// });
console.log("render");
const AnimatedDialogOverlay = animated(DialogOverlay);
// return transitions.map(
// ({ item, key, props }) =>
// item && (
return (
<AnimatedDialogOverlay isOpen={isOpen}>
<DialogContent>
<div
style={{
display: `flex`,
width: `100%`,
alignItems: `center`,
justifyContent: `space-between`
}}
>
<h2 style={{ margin: `4px 0` }}>Modal Title</h2>
<button onClick={toggle}>Close</button>
</div>
{children}
</DialogContent>
</AnimatedDialogOverlay>
);
// )
// );
};
The problem is, that at the end of the animation AnotherComponent remounts. I read similar problems about react-spring. One way could be, that you lift out the state from AnotherComponent to the index.js. This way the state will not lost at remount and you can prevent refetching the data.
const AnotherComponent = ({ url, todo, setTodo }) => {
useEffect(() => {
if (todo.length === 0) {
axios.get(url).then(res => setTodo(res.data));
}
});
....
}
Here is my version: https://codesandbox.io/s/quiet-pond-idyee

Resources