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:
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: