I have a React / Tailwind component with a full screen element. On desktop it works as intended however on mobile the address bars of either chrome of safari cause part of the element to be shown underneath it. Giving the impression that the element isn't correctly centered.
I have researched this issue for a while now and have found no solid answer on how to fix it in React.
I have tried various combinations of height: --webkit-fill-available height: 100vh height: 100% ,with min-height also, on HTML, Root, Body in my main CSS file. Additionally I have tried several different JS functions that calculate the inner height of the document (the height of the document with the address bar excluded.) As well as not using height: 100vh on my targeted element and instead using a combination of height: 100% and fixed positioning. None of these methods have worked in any way at all.
Im hoping for a more React oriented fix to this issue instead of a plain HTML method.
Please note: motion. to anyone unfamiliar is part of Framer Motion and has no effect on the targeted element outside of using Framer properties.
Heres the component:
import * as React from 'react';
import { useEffect } from 'react';
import { motion } from 'framer-motion';
export default function LoadingScreen() {
useEffect(() => {
window.scrollTo(0, 0)
}, [])
return(
<motion.div
className="fixed z-50 w-full bg-tertiary"
initial= {{ opacity: 1 }}
animate={{ opacity: 0 }}
transition={{ duration: 0.3, delay: 2.5, ease: 'easeInOut' }}
>
{/* Image container */}
<motion.div
className="flex justify-center items-center"
initial= {{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, ease: 'easeInOut' }}
>
<motion.img
src="../Images/icon.png"
className="h-[80px] absolute left-0 right-0 top-[50%] -translate-y-[50%] px-5 mx-auto"
initial= {{ opacity: 1 }}
animate={{ opacity: 0 }}
transition={{ duration: 1, delay: 1, ease: 'easeInOut' }}
></motion.img>
<motion.img
src="../Images/icon-primary.png"
className="h-[80px] absolute left-0 right-0 top-[50%] -translate-y-[50%] px-5 mx-auto"
initial= {{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 1.5, ease: 'easeInOut' }}
></motion.img>
</motion.div>
</motion.div>
)
};
Not sure what kind of the result you're looking for?
If you want to make it center both horizontally and vertically, you need to add the device height in the outer layer so it has the space to center.
<motion.div
className="bg-tertiary fixed z-50 h-screen w-full"
initial={{ opacity: 1 }}
animate={{ opacity: 0 }}
transition={{ duration: 0.3, delay: 2.5, ease: "easeInOut" }}
>
{/* Image container */}
<motion.div
className="flex items-center justify-center"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, ease: "easeInOut" }}
>
<motion.img
src="../Images/icon.png"
className="absolute left-0 right-0 top-[50%] mx-auto h-[80px] -translate-y-[50%] px-5"
initial={{ opacity: 1 }}
animate={{ opacity: 0 }}
transition={{ duration: 1, delay: 1, ease: "easeInOut" }}
></motion.img>
<motion.img
src="../Images/icon.png"
className="absolute left-0 right-0 top-[50%] mx-auto h-[80px] -translate-y-[50%] px-5"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 1, delay: 1.5, ease: "easeInOut" }}
></motion.img>
</motion.div>
</motion.div>
Related
I want to use framer-motion to create a transition where element 2 slides in from the bottom as element 1 slides out to the top when I click next.
I'm using AnimatePresence component to achieve this, but the result is a strangely choppy transition where Element 2 appears in the final position briefly before it starts animating from the bottom.
Code:
<AnimatePresence exitBeforeEnter>
{
active === 0 ? (
<motion.div
key='id_1'
initial={{ opacity: 0 }}
animate={{ opacity: 1, backgroundColor: 'green' }}
exit={{ y: '-100%' }}
transition={{ duration: 2 }}>
Element 1
</motion.div>
)
: (
<motion.div
key='id_2'
initial={{ y: '100%' }}
animate={{ y: 0 }}
transition={{ duration: 2 }}
style={{
backgroundColor: 'purple'
}}>
Element 2
</motion.div>
)
}
</AnimatePresence>
Why does the second element appear briefly at first completely ignoring the initial state?
Here is the website link and as you can see, the intro animations look too laggy. Here is the code:
<motion.div className='pic' initial={{x:window.innerWidth < 996?'0%':'100%'}} animate={{x:'0%'}} transition={{delay:'1.6', type:'spring'}}><img src={blob} alt='hero section image'></img></motion.div>
Even the animations after the hero section don't look nice. I have used the react-intersection-observer to activate the animation on scrolling in this particular code.
<motion.div className='detailed' ref={refdetail} initial={{x:'100%', opacity:'0%'}} animate={{x: inViewdetail? '0%': '100%', opacity:inViewdetail? '100%': '0%'}} transition={{ delay:'1.5'}}>
Recently I have worked with framer.motion and it worked well out in my side.
I think that problem is related to the delay sub-prop you used in transition prop.
Here is my tested codebase with motion.framer which works fine in my side.
<motion.div
key={"setuplayout_motion"}
exit={{ opacity: 0 }}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
>
<SetupLayout setCurrentStep={setCurrentStep} />
</motion.div>
<motion.div
animate={{
x: authType === "sign-in" ? "10px" : "70px",
}}
transition={{
x: { type: "spring", stiffness: 100 },
duration: 0.8,
delay: 0.2,
}}
/>
I would like to know how can I use "exit" property with framer motion and InertiaJs / ReactJs please.
Currently I have two pages :
profil.js
const { pathname } = window.location
...
<div>
<motion.div className="flex flex-col" layoutId={`title-3`}
initial={{opacity: 0}}
animate={{
y: [-20, 0],
opacity: [0, 1]
}}
exit={{scale: 2}}
key={{pathname}}
transition={{duration: 0.2, ease: "easeOut", delay: 0.1}}
>
<span>Profil</span>
</motion.div>
</div>
home.js
const { pathname } = window.location
...
<div>
<motion.div className="flex flex-col" layoutId={`title-3`}
initial={{opacity: 0}}
animate={{
y: [-20, 0],
opacity: [0, 1]
}}
exit={{scale: 2}}
key={{pathname}}
transition={{duration: 0.2, ease: "easeOut", delay: 0.1}}
>
<span>Home</span>
</motion.div>
</div>
And I set AnimateSharedLayout on my App (app.js) :
import { AnimateSharedLayout } from 'framer-motion';
return render(
<Provider store={store}>
<AnimateSharedLayout>
<App {...props} />
</AnimateSharedLayout>
</Provider>, el
);
When I click on "home" or "profil" in the sidebar, I see the animate, but I don't see the exit animation.
I use InertiaJs and ReactJs.
Do you have an idea why I don't see the exit ?
Once you click away from "home" or "profile", that element is removed from the DOM right away, so it won't be there long enough to see the animation.
Framer Motion Docs
By wrapping motion components with AnimatePresence, they gain the use of an exit property that can define either a set of values or a variant name to animate to before being removed.
Try wrapping your <motion.div>s with <AnimatePresence> like this:
const { pathname } = window.location
...
<div>
<AnimatePresence>
<motion.div className="flex flex-col" layoutId={`title-3`}
initial={{opacity: 0}}
animate={{
y: [-20, 0],
opacity: [0, 1]
}}
exit={{scale: 2}}
key={{pathname}}
transition={{duration: 0.2, ease: "easeOut", delay: 0.1}}
>
<span>Home</span>
</motion.div>
</AnimatePresence>
</div>
I'm trying to create a React component where when scrolling down into it, its background transitions from a color to an image.
This is the component:
const TeamSection = () => {
return (
<Background>
<TeamSectionContainer
initial={{
opacity: 0,
}}
whileInView={{
opacity: 1,
}}
transition={{
ease: "easeOut",
duration: 2,
}}
>
<Title>Text</Title>
<Team>Text<Team>
</TeamSectionContainer>
</Background>
);
};
And the styles:
const TeamSectionContainer = styled(motion.div)`
${tw`
bg-no-repeat
w-full
min-h-[200vh]
flex
flex-col
`};
background-image: url(${BackgroundImage});
`;
const Background = tw.div`
bg-[#001F33]
`;
The result is that th animation works for the whole TeamSectionContainer component, indluding its background. I'm trying to make it so that none of the content is animated and only the background is.
How could be this achieved?
I don't think you can animate the opacity of a background image like that. You'd need to add a separate div inside the TeamSectionContainer with the background image and apply the animation to that:
const TeamSection = () => {
return (
<Background>
<TeamSectionContainer>
<motion.div className="backgroundImage"
initial={{
opacity: 0,
}}
whileInView={{
opacity: 1,
}}
transition={{
ease: "easeOut",
duration: 2,
}}
/>
<Title>Text</Title>
<Team>Text<Team>
</TeamSectionContainer>
</Background>
);
};
I am to make a step-like animation, hence, when I click a button, another div than the previous is shown, e.g:
import { motion, AnimatePresence } from 'framer-motion'
const MyApp= props => {
const [count, setCount] = useState(0)
return (
<>
<AnimatePresence>
{count == 0 && (
<motion.div
transition={{ duration: 2 }}
initial={{ opacity: 1 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Hello 1
<button onClick={() => { setCount(count + 1) }}
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
{count == 1 && (
<motion.div
transition={{ duration: 2 }}
initial={{ opacity: 1 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Hello 2
<button onClick={() => { setCount(count + 1) }}
</motion.div>
)}
</AnimatePresence>
</>
)
}
export default MyApp
However, the issue is, that when one div is fading out/in, the other is also fading in/out, hence, I have two elements at the same time. Instead I thought it would just animate one, remove it, and animate the other.
Am I doing something wrong here ?
Since you want to animate them together they should be under one <AnimatePresence />
AnimatePresence requires you to add an explicit key on the component you are animating beneath it so that it can track the components as they are added and removed https://www.framer.com/api/motion/animate-presence/#unmount-animations
Since you want to replace one component with another you need to add the prop exitBeforeEnter on the AnimatePrensence. https://www.framer.com/api/motion/animate-presence/#animatepresenceprops.exitbeforeenter
<AnimatePresence exitBeforeEnter> // Note on #3
{isGreenBox ? (// Note on #1
<GreenBox
initial={{ scale: 0 }}
animate={{ scale: 1 }}
exit={{ scale: 0 }}
key="green" // Note on #2
/>
) : (
<PurpleBox
initial={{ scale: 0 }}
animate={{ scale: 1 }}
exit={{ scale: 0 }}
key="purple" // Note on #2
/>
)}
</AnimatePresence>
Here is a working example I created :) https://codesandbox.io/s/framer-motion-animation-presence-exitbeforeenter-0miq0