odd behavior with framer motion animate on presence

1.4k views Asked by At

I'm working ona project management application , and I'm developing this feature where the project header can go into editing state where user can edit project title , this header has two child components HeaderContent and EditProjectForm :
>ProjectHeader
-->HeaderContent
-->EditProjectForm
my problem now is that when I fade out the HeaderContent the faded In EditProjectForm is intially pushed down then it jumps to its place , it seems that this happens because even though HeaderContent was faded out it still was affecting the dom structure

here is a short screen recording I just uploaded to further make things clear https://www.youtube.com/watch?v=UerYDuEcUWQ

Header component

const ProjectHeader=()=>{
const [isEditingState, setisEditingProject] = useState({value:false,triggerFrom:"PANEL_HEADER"})

return <div>
  <HeaderContent {...{isEditingProject, setisEditingProject}}    />
  <EditProjectForm {...{isEditingProject, setisEditingProject}} />
</div>
}

HeaderContent

const HeaderContent =({isEditingProject, setisEditingProject})=>{
 const [render, setrender] = useState(true)
 useEffect(() => {
        let ref=null
        // if(isEditingProject.triggerFrom =="EDIT_PROJECT_FORM") if I have don this whenever I change isEditingProject.value from this component this setTimeout below will be fired and I want to only be fired when this compoent is unmounted 
        if(isEditingProject.triggerFrom =="EDIT_PROJECT_FORM"){
            ref= setTimeout(() => {
               setrender(!isEditingProject.value)
            }, 200);//wait until edit form finishes its fade_out animtion
          }
        return ()=>ref && clearTimeout(ref)
  }, [isEditingProject])


return <AnimatePresence  initial={false} >
     {
        render
        &&(<motion.div 
        animate={{opacity:1 ,y:0}}  
        initial={{opacity:1 ,y:0}}
        exit   ={{opacity:0    ,y:10}}
        transition={{
          duration:.2,
          opacity: { type: "spring", stiffness: 100 },
        }}>

      //.. header links and buttons and the title 
    <button onClick={e=>{
      setrender(false)                    
      setisEditingProject({...isEditingProject,value:true,triggerFrom:"PANEL_HEADER"})
  }} >edit</button>
    }
</AnimatePresence  >
}

EditProjectForm

const EditProjectForm =({isEditingProject, setisEditingProject})=>{
 const [render, setrender] = useState(true)
 useEffect(() => {
        let ref =null
 // if(isEditingProject.triggerFrom =="PANEL_HEADER") if I haven't don this whenever I change isEditingProject.value from this component this setTimeout below will be fired and I want to only be fired when this compoent is unmounted 
       if(isEditingProject.triggerFrom =="PANEL_HEADER"){
         ref=setTimeout(() => {
            setrender(isEditingProject.value)
         }, 200);
       }
       return ()=>ref && clearTimeout(ref)
 }, [isEditingProject.value])

return <AnimatePresence>
    {
      render &&  <motion.form
      animate={{  opacity:1 ,y:0  }}  
      initial={{  opacity:1 ,y:10 }}
      exit   ={{  opacity:0 ,y:-10}}
         transition={{
            duration:.2,
             opacity: { type: "spring", stiffness: 100 },
         }}
       >
        /.. title input 
          <button onClick={e=>{
           setrender(false)                    
           setisEditingProject({...isEditingProject,value:true,triggerFrom:"EDIT_PROJECT_FORM"})
           }} >edit</button>
      </motion.form>
    }
</AnimatePresence>
}
1

There are 1 answers

2
ChilTest On

It's problem with css, not with framer. My advise is to wrap your component with div absolute position, where top:0;

Maybe, you have some flex divs which trigger this "strange" behaviour