The following approach covers how to create an animated shared layout using framer-motion and ReactJS.
Prerequisites:
- Knowledge of JavaScript (ES6)
- Knowledge of HTML/CSS.
- Basic knowledge of ReactJS.
Creating React Application And Installing Module:
-
Step 1: Create a React application using the following command:
npx create-react-app animated-layout
-
Step 2: After creating your project folder i.e. animated-layout, move to it using the following command.
cd animated-layout
-
Step 3: Add the npm packages you will need during the project :
npm install framer-motion
Now open the src folder and delete the following files and create a JavaScript file named Item.js.
- logo.svg
- serviceWorker.js
- setupTests.js
- App.test.js (if any)
- index.css
Project Structure: Your project structure tree should look like this:
Folder structure
Example:
- We are going to create an Item component that is the animated layout using react useState hook and framer-motion components motion and AnimatePresence.
- Content component is used to create Item’s (animated shared layout) content using HTML img tag and div & framer-motion component motion.
- The toggleOpen is a utility function to set the ‘isOpen’ value not (!) of its last value.
- In App.js, itemList is for the number of animated shared layout we want to create, in our case it is 3.
- In App.js, we are going to use framer-motion AnimatedSharedLayout component to wrap the imported Item component and map through ‘itemsList’ array to render animated layouts.
App.js
import React from "react" ; import { AnimateSharedLayout } from "framer-motion" ; import Item from "./Item" ; import "./styles.css" ;   // This is an example of animating shared layouts // using react and framer-motion library. const itemsList = [   {     index: 0,     content: `Motion components are DOM primitives   optimised for 60fps animation and gestures.`   },   {     index: 1,     content: `Motion can animate:     Numbers: 0, 10 etc.     Strings containing numbers: "0vh" , "10px" etc.`   },   {     index: 2,     content: `Transform properties are accelerated by the GPU,      and therefore animate smoothly. `   } ];   const App = () => {   return (     // The framer-motion component to wrap Item component to animate it     <AnimateSharedLayout>       { /* Mapping through itemList array to render layouts*/ }       {itemsList.map((item) => (         <Item key={item.index} content={item.content} />       ))}     </AnimateSharedLayout>   ); };   export default App; |
Item.js
import React, { useState } from "react" ; import { motion, AnimatePresence } from "framer-motion" ;   const Content = ({ content }) => {       "20200817185016/gfg_complete_logo_2x-min.png"     return (     <motion.div       layout       initial={{ opacity: 0 }}       animate={{ opacity: 1 }}       exit={{ opacity: 0 }}     >       <img         src={url}         alt= "neveropen"       />       <div className= "row" >{content}</div>     </motion.div>   ); };   const Item = ({ content }) => {   // React useState hook is used to manage the state of 'isOpen'   // that in turn toggles shared layout, user clicks on   const [isOpen, setIsOpen] = useState( false );     // Utility function to set 'isOpen' '!'(not) of its last value   const toggleOpen = () => setIsOpen(!isOpen);       "27UlohMeBLxyUdhs9hUbc-Agw=s900-c-k-c0x00ffffff-no-rj"     return (     <motion.li       layout       title= "Click to reveal"       onClick={toggleOpen}       initial={{ borderRadius: [25] }}     >       <motion.div className= "avatar" layout>         { " " }         <img           src={url}           alt= "gfg"         />{ " " }       </motion.div>       <br />       <AnimatePresence>{isOpen && <Content content={content} />}       </AnimatePresence>     </motion.li>   ); };   export default Item; |
styles.css
body { Â Â min-height : 100 vh; Â Â margin : 0 ; Â Â display : flex; Â Â justify- content : center ; Â Â align-items: center ; } Â Â * { Â Â box-sizing: border-box; } Â Â ul, li { Â Â list-style : none ; Â Â margin : 0 ; Â Â padding : 0 ; } Â Â ul { Â Â width : 300px ; Â Â display : flex; Â Â flex- direction : column; Â Â background : #fcfcfc ; Â Â padding : 20px ; Â Â border-radius: 25px ; } Â Â li { Â Â background-color : rgba( 214 , 214 , 214 , 0.5 ); Â Â border-radius: 10px ; Â Â padding : 20px ; Â Â margin-bottom : 20px ; Â Â overflow : hidden ; Â Â cursor : pointer ; Â Â width : 300px ; } Â Â li:last-child { Â Â margin-bottom : 0px ; } Â Â .avatar { Â Â width : 40px ; Â Â height : 40px ; Â Â border-radius: 20px ; } Â Â .avatar img { Â Â width : 40px ; Â Â border-radius: 100% ; } Â Â .row { Â Â margin-top : 12px ; } Â Â img { Â Â width : 250px ; Â Â height : 40px ; } |
Step to Run Application: Run the application using the following command from the root directory of the project:
npm start
Output: Now open your browser and go to http://localhost:3000/, you will see the following output: