El siguiente enfoque cubre cómo crear una galería de páginas deslizantes animadas usando framer-motion y ReactJS.
requisitos previos:
- Conocimiento de JavaScript (ES6)
- Conocimientos de HTML y CSS .
- Conocimientos básicos de ReactJS .
Creación de la aplicación React e instalación del módulo:
Paso 1: Cree una aplicación React usando el siguiente comando:
$ npx create-react-app page-gallery
Paso 2: después de crear la carpeta de su proyecto, es decir , la galería de páginas , muévase a ella con el siguiente comando.
$ cd page-gallery
Paso 3: agregue los paquetes npm que necesitará durante el proyecto.
$ npm install framer-motion @popmotion/popcorn
Abra la carpeta src y elimine los siguientes archivos:
- logotipo.svg
- serviceWorker.js
- setupTests.js
- App.test.js (si corresponde)
- índice.css.
Cree un archivo llamado PageSlider.js .
Estructura del proyecto: El árbol de la estructura del proyecto debería verse así:
Nombre de archivo: App.js
Javascript
import React from "react"; import { useState } from "react"; import { motion, AnimateSharedLayout } from "framer-motion"; import PageSlider from "./PageSlider"; import "./styles.css"; const Pagination = ({ currentPage, setPage }) => { // Wrap all the pagination Indicators // with AnimateSharedPresence // so we can detect when Indicators // with a layoutId are removed/added return ( <AnimateSharedLayout> <div className="Indicators"> {pages.map((page) => ( <Indicator key={page} onClick={() => setPage(page)} isSelected={page === currentPage} /> ))} </div> </AnimateSharedLayout> ); }; const Indicator = ({ isSelected, onClick }) => { return ( <div className="Indicator-container" onClick={onClick}> <div className="Indicator"> {isSelected && ( // By setting layoutId, when this component // is removed and a new one is added elsewhere, // the new component will animate out from the old one. <motion.div className="Indicator-highlight" layoutId="highlight" /> )} </div> </div> ); }; const pages = [0, 1, 2, 3, 4]; const App = () => { /* We keep track of the pagination direction as well as * current page, this way we can dynamically generate different * animations depending on the direction of travel */ const [[currentPage, direction], setCurrentPage] = useState([0, 0]); function setPage(newPage, newDirection) { if (!newDirection) newDirection = newPage - currentPage; setCurrentPage([newPage, newDirection]); } return ( <> <PageSlider currentPage={currentPage} direction={direction} setPage={setPage} /> <Pagination currentPage={currentPage} setPage={setPage} /> </> ); }; export default App;
Nombre de archivo: PageSlider.js
Javascript
import React from "react"; import { useRef } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { wrap } from "@popmotion/popcorn"; // Variants in framer-motion define visual states // that a rendered motion component can be in at // any given time. const xOffset = 100; const variants = { enter: (direction) => ({ x: direction > 0 ? xOffset : -xOffset, opacity: 0 }), active: { x: 0, opacity: 1, transition: { delay: 0.2 } }, exit: (direction) => ({ x: direction > 0 ? -xOffset : xOffset, opacity: 0 }) }; const pages = [0, 1, 2, 3, 4]; const PageSlider = ({ currentPage, setPage, direction }) => { /* Add and remove pages from the array to checkout how the gestures and pagination animations are fully data and layout-driven. */ const hasPaginated = useRef(false); function detectPaginationGesture(e, { offset }) { if (hasPaginated.current) return; let newPage = currentPage; const threshold = xOffset / 2; if (offset.x < -threshold) { // If user is dragging left, go forward a page newPage = currentPage + 1; } else if (offset.x > threshold) { // Else if the user is dragging right, // go backwards a page newPage = currentPage - 1; } if (newPage !== currentPage) { hasPaginated.current = true; // Wrap the page index to within the // permitted page range newPage = wrap(0, pages.length, newPage); setPage(newPage, offset.x < 0 ? 1 : -1); } } return ( <div className="slider-container"> <AnimatePresence // This will be used for components to resolve // exit variants. It's necessary as removed // components won't rerender with // the latest state (as they've been removed) custom={direction}> <motion.div key={currentPage} className="slide" data-page={currentPage} variants={variants} initial="enter" animate="active" exit="exit" drag="x" onDrag={detectPaginationGesture} onDragStart={() => (hasPaginated.current = false)} onDragEnd={() => (hasPaginated.current = true)} // Snap the component back to the center // if it hasn't paginated dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }} // This will be used for components to resolve all // other variants, in this case initial and animate. custom={direction} /> </AnimatePresence> </div> ); }; export default PageSlider;
Nombre de archivo: App.css
CSS
body { display: flex; justify-content: center; align-items: center; min-height: 100vh; overflow: hidden; background: #09a960; } * { box-sizing: border-box; } .App { font-family: sans-serif; text-align: center; } .slider-container { position: relative; width: 600px; height: 600px; } .slide { border-radius: 5px; background: white; position: absolute; top: 0; left: 0; bottom: 0; right: 0; } /* position of indicator container */ .Indicators { display: flex; justify-content: center; margin-top: 30px; } .Indicator-container { padding: 20px; cursor: pointer; } .Indicator { width: 10px; height: 10px; background: #fcfcfc; border-radius: 50%; position: relative; } .Indicator-highlight { top: -2px; left: -2px; background: #09f; border-radius: 50%; width: 14px; height: 14px; position: absolute; }
Paso para ejecutar la aplicación: ejecute la aplicación utilizando el siguiente comando desde el directorio raíz del proyecto:
$ npm start
Salida: Ahora abra su navegador y vaya a http://localhost:3000/ , verá la siguiente salida:
Publicación traducida automáticamente
Artículo escrito por jt9999709701 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA