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: 100vh; Â Â 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:
