Galería de páginas deslizantes animadas usando framer-motion y React.js

El siguiente enfoque cubre cómo crear una galería de páginas deslizantes animadas usando framer-motion y ReactJS.

requisitos previos:

  1. Conocimiento de JavaScript (ES6)
  2. Conocimientos de HTML y CSS .
  3. 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:

  1. logotipo.svg
  2. serviceWorker.js
  3. setupTests.js
  4. App.test.js (si corresponde)
  5. índice.css.

Cree un archivo llamado PageSlider.js .

Estructura del proyecto: El árbol de la estructura del proyecto debería verse así:

Estructura del proyecto

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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *