如何使用React和Framer Motion构建图像轮播

发布时间 2023-07-04 10:25:58作者: 晓风晓浪

您可能在许多现代应用程序中遇到过轮播。这些多功能网页元素以各种名称(例如滑块或旋转器)而闻名,它们以视觉上吸引人的滑动或旋转方式展示内容。

轮播可以帮助您节省空间、增强用户界面并提供出色的用户体验。

轮播已成为 UI 设计的主要内容,通常用于显示图像、推荐等。创建引人入胜的动态界面时,它们是不可或缺的。

在本文中,我们将深入探讨使用 React 和 Framer Motion 构建图像轮播的过程,指导您完成每一步,为您的应用程序创建令人惊叹的交互式视觉组件。

(更|多优质内|容:java567 点 c0m)

 

什么是成帧器运动?

这是一个用于 React 应用程序的开源动画库,您可以使用它为我们的 Web 应用程序创建动态和响应式动画。

Framer Motion 有几个有用的功能,包括:

  1. 动画:这允许您为组件进行无缝过渡。

  2. 手势:它支持触摸和鼠标动作,允许您考虑某些事件。

  3. 变体:Framer Motion 使您能够以声明方式声明组件,从而保持代码的组织性和可重用性。

所有这些功能都非常有用,我们很快就会看到它们的实际应用。

要更深入地了解 Framer Motion,您可以浏览其文档和资源。但在本文中,我们将重点关注基础知识。当我指导您了解使用 Framer Motion 的基础知识时,我的主要目标是构建令人印象深刻且引人入胜的图像轮播。

如何设置您的开发环境

我们要做的第一件事是设置您的开发环境。这涉及安装必要的包以成功构建您的应用程序。这包括安装Node.js和npm

如果您已经安装了 Node.js 和 npm,则无需再次下载并安装它们。

创建一个 React 应用程序

此时,我假设您已经安装了 Node 和 npm。要创建 React 应用程序,只需转到终端并访问您希望应用程序所在的目录。然后运行以下命令:

 npx create-react-app react-image-carousel

您可以为您的应用程序命名任何您想要的名称 - 但出于本文的目的,我将其命名为react-image-carousel.

成功创建 React 应用程序后,在代码编辑器中打开您的目录。您应该获得一些默认文件和样式,它应该如下所示:

 

我们不需要此项目中的大部分文件和样式,因此您可以清理以下文件:app.test.js、logo.svg、reportWebVitals.js、setupTest.js。您还可以删除 App.css 表中的所有默认样式。

 

现在您的 React 应用程序已创建并设置完毕,为此项目设置开发环境的最后一步是安装 Framer Motion。

为此,只需转到终端确保您位于项目目录中并运行以下命令:

  npm  install framer-motion

这应该安装最新版本的 Framer Motion。现在你应该可以走了。只需用于npm run start在浏览器上启动开发服务器即可。

如何设计图像轮播组件

为了开始设计,我们首先创建一个Carousel.js组件。在轮播组件中,我们将从useStateReact 导入钩子,然后从 Framer Motion导入motion和属性。AnimatePresence

 import { useState } from "react";
 import { motion, AnimatePresence } from "framer-motion";

然后我们创建 carousel 函数,该函数接受imagesprop,该 prop 是图像 URL 的数组:

 const Carousel = ({ images }) => {};

在我们的 carousel 函数中,我们使用 useState 初始化一个状态变量,以跟踪我们用作setCurrentIndex更新索引的相应函数的当前图像索引。

接下来,我们创建 3 个辅助函数来处理用户交互,其中包括:

  • handleNext:这会将 currentIndex 更新为下一个索引,以便更改图像,如果到达数组末尾,则循环返回。

  • handlePrevious:这与handleNext 函数的作用相同,但这次顺序相反。这使我们能够回到图像。

  • handleDotClick:这将索引作为参数并更新 currentIndex。这样,我们只需单击这些点就可以向前和向后跳转到图像。

 const Carousel = ({ images }) => {
   const [currentIndex, setCurrentIndex] = useState(0);
 
   const handleNext = () => {
     setCurrentIndex((prevIndex) =>
       prevIndex + 1 === images.length ? 0 : prevIndex + 1
    );
  };
   const handlePrevious = () => {
     setCurrentIndex((prevIndex) =>
       prevIndex - 1 < 0 ? images.length - 1 : prevIndex - 1
    );
  };
   const handleDotClick = (index) => {
     setCurrentIndex(index);
  };

这些是我们的组件所需的辅助函数

如何创建我们的模板

我们的模板非常简单,由图像、滑块方向和点(指示器)组成。

   return (
     <div className="carousel">
         <img
           key={currentIndex}
           src={images[currentIndex]}
         /><div className="slide_direction">
         <div className="left" onClick={handlePrevious}>
           <svg
             xmlns="/2000/svg"
             height="20"
             viewBox="0 96 960 960"
             width="20"
           >
             <path d="M400 976 0 576l400-400 56 57-343 343 343 343-56 57Z" />
           </svg>
         </div>
         <div className="right" onClick={handleNext}>
           <svg
             xmlns="/2000/svg"
             height="20"
             viewBox="0 96 960 960"
             width="20"
           >
             <path d="m304 974-56-57 343-343-343-343 56-57 400 400-400 400Z" />
           </svg>
         </div>
       </div>
       <div className="indicator">
        {images.map((_, index) => (
           <div
             key={index}
             className={`dot ${currentIndex === index ? "active" : ""}`}
             onClick={() => handleDotClick(index)}
           ></div>
        ))}
       </div>
     </div>
  );

正如您在模板中看到的,我们在当前索引处显示图像。然后我们有一个 div,其中包含两个带有类名和 的slider_directiondiv 。我们将它们创建为轮播的导航按钮。它们使用内联 SVG 来显示箭头图标,并且它们的 onClick 处理程序分别设置为和。leftrighthandlePrevious``handleNext

我们还有一个指示器 div,我们创建它来显示代表轮播中每个图像的一系列点。它映射图像数组并为每个图像创建一个点,为与currentIndex.

然后,我们为每个点附加一个 onClick 处理程序,该处理程序设置handleDotClick为使用点的索引进行调用。

现在这应该是我们的模板。剩下的就是导出 carousel 组件,将其导入到App.js组件中,并添加一些 CSS。然后我们就准备开始制作动画了。

因此,我们只需从组件中导出轮播功能即可Carousel.js。

 export default Carousel;

如何使用轮播组件

我们已经创建了轮播组件。但要使用它,我们必须将其导入到我们的 App.js 组件中:

 import Carousel from "./Carousel";

之后,我们可以创建图像数组,它将保存我们的图像 URL。

 const images = [
   "/photos/169647/pexels-photo-169647.jpeg?auto=compress&cs=tinysrgb&w=600",
   "/photos/313782/pexels-photo-313782.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
   "/photos/773471/pexels-photo-773471.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
   "/photos/672532/pexels-photo-672532.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
   "/photos/632522/pexels-photo-632522.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
   "/photos/777059/pexels-photo-777059.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
 ];

这些只是我从pexels获得的图像- 这就是我们将在这个项目中使用的图像。

接下来,我们添加 App 函数,它将保存我们的应用程序模板。

 function App() {
   return (
     <div className="App">
       <header className="App-header">
         <h1>Image Carousel using React and Framer Motion</h1>
       </header>
       <main>
         <Carousel images={images} />
       </main>
     </div>
  );
 }
 export default App;

正如您所看到的,我们的标题仅显示一个标题,显示我们的应用程序的内容。

然后我们有主要部分,其中添加了轮播组件并接受图像数组的道具。如果您还记得,这是我们在轮播组件中用于显示图像的道具。

最后,我们导出 App 组件,以便我们可以在 index.js 文件中使用它。

要在没有样式的情况下查看所有这些,请运行命令npm run start。该应用程序应如下所示:

 

丑陋吧?是的,我同意你的看法。但只需几行 CSS,这一切就会发生转变。那么让我们深入了解一下吧。

如何添加 CSS

我不想为轮播组件创建单独的样式表,因此我们将在 App.css 文件中完成所有 CSS 操作。不要忘记导入样式表。

 import "./App.css"

这是我们的 CSS:

 @import url("/css2?family=Oswald:wght@600&display=swap");
 .App-header {
   font-size: 1rem;
   text-align: center;
   font-family: "Oswald", sans-serif;
   padding-bottom: 2rem;
 }
 .carousel-images {
   position: relative;
   border-radius: 10px;
   height: 400px;
   max-width: 650px;
   margin: auto;
   overflow: hidden;
 }
 .carousel-images img {
   width: 99%;
   height: 99%;
   border-radius: 8px;
   border: #ff00008e solid 2px;
 }
 .slide_direction {
   display: flex;
   justify-content: space-between;
 }
 .left,
 .right {
   background-color: #fb666675;
   color: #fff;
   padding: 10px 8px 8px 13px;
   margin: 0 20px;
   border-radius: 50%;
   position: absolute;
   top: 0;
   bottom: 0;
   margin: auto 10px;
   height: 25px;
   width: 25px;
 }
 .left {
   left: 0;
 }
 .right {
   right: 0;
 }
 .carousel-indicator {
   margin-top: 20px;
   display: flex;
   justify-content: center;
   gap: 20px;
 }
 .dot {
   background-color: #333;
   width: 15px;
   height: 15px;
   border-radius: 50%;
 }
 .active {
   background-color: #fa2020;
 }

这是我们的 CSS 的结果:

 

您可能会同意,这看起来好多了,而且功能已经齐全。

现在让我们继续使用 Framer Motion 添加动画,使其具有漂亮的滑动外观。

如何向轮播组件添加动画

要开始使用 Framer Motion 制作动画,您必须熟悉一些概念,因为我们将在本节中经常使用它们。这些概念包括:

  • 变体:将变体视为一组命名的属性。它的工作是定义元素应该如何显示或动画。您可以创建不同的变体来表示元素的不同视觉状态或动画,例如open、closed、hover等。

  • 初始状态:这就是动画开始之前对象所具有的状态。

  • 动画:这只是你的对象将要动画的状态,就这么简单。

回到项目,我们将动画添加到轮播组件中。我们已经导入了我们需要的两个属性 -motion和AnimatePresence属性。

我将把本节分为三个部分,因为我们将向代码的三个部分添加动画,包括图像、滑块方向和指示点。

图像动画

为了对图像的退出和进入进行动画处理,我们需要用一个 组件来包装我们的img元素。AnimationPresence这使我们能够在图像离开或进入时添加动画。然后我们motion.像这样将 a 附加到我们的标签上。

  <AnimatePresence>
   <motion.img key={currentIndex} src={images[currentIndex]} />
 </AnimatePresence>;

接下来,我们走出模板并声明我们的变体。

   const slideVariants = {
     hiddenRight: {
       x: "100%",
       opacity: 0,
    },
     hiddenLeft: {
       x: "-100%",
       opacity: 0,
    },
     visible: {
       x: "0",
       opacity: 1,
       transition: {
         duration: 1,
      },
    },
     exit: {
       opacity: 0,
       scale: 0.8,
       transition: {
         duration: 0.5,
      },
    },
  };

如您所见,sliderVariants具有四个属性:

  • hiddenRight:这会将图像的不透明度设置为 0 并将其放置在容器的右侧。

  • hiddenLeft:这与hiddenRight 的作用相同,但这次它设置在左侧。

  • 可见:无论图像位于容器中心的哪个位置,都会调用此属性来实现幻灯片动画。

  • exit:此动画控制当另一个图像滑入时从屏幕上删除图像。

现在我们的变体已经设置好了。我们如何知道图像应该从哪里滑入?我们需要设置一个方向状态并根据slide_direction单击的 s 来更新状态。

   const [direction, setDirection] = useState('left');

所以我们将方向设置为从左侧开始。这只是合乎逻辑的,因为要显示的第一幅图像将是第一幅图像。然后我们转到辅助函数并根据单击的方向设置方向。

   const handleNext = () => {
     setDirection("right");
     setCurrentIndex((prevIndex) =>
       prevIndex + 1 === images.length ? 0 : prevIndex + 1
    );
  };
 
   const handlePrevious = () => {
     setDirection("left");
 
     setCurrentIndex((prevIndex) =>
       prevIndex - 1 < 0 ? images.length - 1 : prevIndex - 1
    );
  };
 
   const handleDotClick = (index) => {
     setDirection(index > currentIndex ? "right" : "left");
     setCurrentIndex(index);
  };

您可能已经注意到,我们不仅仅设置了handleNext和 的状态handlePrevious。我们也为handleDotClick. 因此,每当单击上一个或下一个点时,都会相应地设置方向。

只是提醒一下 - 方向的目的是设置图像的初始状态,以便滑块可以正常工作。

现在我们的方向已经确定,让我们在img元素中使用我们的变体。

 <AnimatePresence>
           <motion.img
             key={currentIndex}
             src={images[currentIndex]}
             variants={slideVariants}
             initial={direction === "right" ? "hiddenRight" : "hiddenLeft"}
             animate="visible"
             exit="exit"
           />
         </AnimatePresence>

所以我们添加variantsprop 并将其设置为等于slideVariants我们创建的。然后我们添加初始属性并将其设置为等于三元运算符。这会将图像的初始状态设置为 或 ,hiddenRight具体hiddenLeft取决于单击的slider_direction是 或dot。

接下来,我们添加 animate 属性,该属性将图像从初始位置动画到我们在属性中设置的位置visible。

最后,我们添加退出属性并将其设置为exit。当新图像进入时,这会将图像动画移出屏幕。

使用 Framer Motion 时可以使用许多道具。您可以查看文档以了解有关它们的更多信息。

完成后,我们的图像轮播应该可以完美运行。

 

滑块和点动画

我们可以到此为止,但我只想向我的幻灯片方向和点添加一些动画。

   const slidersVariants = {
     hover: {
       scale: 1.2,
       backgroundColor: "#ff00008e",
    },
  };
 const dotsVariants = {
     initial: {
       y: 0,
    },
     animate: {
       y: -10,
       scale: 1.3,
       transition: { type: "spring", stiffness: 1000, damping: "10" },
    },
     hover: {
       scale: 1.1,
       transition: { duration: 0.2 },
    },
  };

像往常一样,首先我们创建我们的变体。对于slidersVariants我们只需添加一个悬停属性。对于 ,dotsVariants我们有三个属性:initial、animate 和hover。

就像我们对img元素所做的那样,我们将添加motion.元素名称作为前缀,以便使用 Framer Motion。

 <div className="slide_direction">
   <motion.div
     variants={slidersVariants}
     whileHover="hover"
     className="left"
     onClick={handlePrevious}
   >
     <svg
       xmlns="/2000/svg"
       height="20"
       viewBox="0 96 960 960"
       width="20"
     >
       <path d="M400 976 0 576l400-400 56 57-343 343 343 343-56 57Z" />
     </svg>
   </motion.div>
   <motion.div
     variants={slidersVariants}
     whileHover="hover"
     className="right"
     onClick={handleNext}
   >
     <svg
       xmlns="/2000/svg"
       height="20"
       viewBox="0 96 960 960"
       width="20"
     >
       <path d="m304 974-56-57 343-343-343-343 56-57 400 400-400 400Z" />
     </svg>
   </motion.div>
 </div>;

如您所见,我们添加了变体并将其设置为等于slidersVariants。然后我们使用一个新属性whileHover并将其设置为等于我们在对象中指定的 over 属性slidersVariants。

 <motion.div
   key={index}
   className={`dot ${currentIndex === index ? "active" : ""}`}
   onClick={() => handleDotClick(index)}
   initial="initial"
   animate={currentIndex === index ? "animate" : ""}
   whileHover="hover"
   variants={dotsVariants}
 ></motion.div>;

这里我们不只是添加 whileHover 属性。我们还添加了一个initial道具和一个animate对当前图像点进行动画处理的道具,使其脱颖而出。

在我们的slidersVariants对象中,我们指定了弹簧的过渡类型,它在动画过渡发生时赋予其弹性性质。

将所有这些加在一起,我们时尚的图像轮播就准备好了。这是最终结果:

 

仅供参考,这是轮播组件的完整代码:

 import { useState } from "react";
 import { motion, AnimatePresence } from "framer-motion";
 
 const Carousel = ({ images }) => {
   const [currentIndex, setCurrentIndex] = useState(0);
   const [direction, setDirection] = useState(null);
 
   const slideVariants = {
     hiddenRight: {
       x: "100%",
       opacity: 0,
    },
     hiddenLeft: {
       x: "-100%",
       opacity: 0,
    },
     visible: {
       x: "0",
       opacity: 1,
       transition: {
         duration: 1,
      },
    },
     exit: {
       opacity: 0,
       scale: 0.8,
       transition: {
         duration: 0.5,
      },
    },
  };
   const slidersVariants = {
     hover: {
       scale: 1.2,
       backgroundColor: "#ff00008e",
    },
  };
   const dotsVariants = {
     initial: {
       y: 0,
    },
     animate: {
       y: -10,
       scale: 1.2,
       transition: { type: "spring", stiffness: 1000, damping: "10" },
    },
     hover: {
       scale: 1.1,
       transition: { duration: 0.2 },
    },
  };
 
   const handleNext = () => {
     setDirection("right");
     setCurrentIndex((prevIndex) =>
       prevIndex + 1 === images.length ? 0 : prevIndex + 1
    );
  };
 
   const handlePrevious = () => {
     setDirection("left");
 
     setCurrentIndex((prevIndex) =>
       prevIndex - 1 < 0 ? images.length - 1 : prevIndex - 1
    );
  };
 
   const handleDotClick = (index) => {
     setDirection(index > currentIndex ? "right" : "left");
     setCurrentIndex(index);
  };
 
   return (
     <div className="carousel">
         <div className="carousel-images">
         <AnimatePresence>
           <motion.img
             key={currentIndex}
             src={images[currentIndex]}
             initial={direction === "right" ? "hiddenRight" : "hiddenLeft"}
             animate="visible"
             exit="exit"
             variants={slideVariants}
           />
         </AnimatePresence>
         <div className="slide_direction">
           <motion.div
             variants={slidersVariants}
             whileHover="hover"
             className="left"
             onClick={handlePrevious}
           >
             <svg
               xmlns="/2000/svg"
               height="20"
               viewBox="0 96 960 960"
               width="20"
             >
               <path d="M400 976 0 576l400-400 56 57-343 343 343 343-56 57Z" />
             </svg>
           </motion.div>
           <motion.div
             variants={slidersVariants}
             whileHover="hover"
             className="right"
             onClick={handleNext}
           >
             <svg
               xmlns="/2000/svg"
               height="20"
               viewBox="0 96 960 960"
               width="20"
             >
               <path d="m304 974-56-57 343-343-343-343 56-57 400 400-400 400Z" />
             </svg>
           </motion.div>
         </div>
       </div>
       <div className="carousel-indicator">
        {images.map((_, index) => (
           <motion.div
             key={index}
             className={`dot ${currentIndex === index ? "active" : ""}`}
             onClick={() => handleDotClick(index)}
             initial="initial"
             animate={currentIndex === index ? "animate" : ""}
             whileHover="hover"
             variants={dotsVariants}
           ></motion.div>
        ))}
       </div>
     </div>
  );
 };
 export default Carousel;

查看GitHub上的 Git 存储库。

这是Netlify上的网站。

资源

我知道可能有些术语或语法不清楚,特别是如果您不熟悉 React 或不熟悉使用 Framer Motion。如果您想了解更多信息,我会推荐以下一些资源:

  • 反应文档

  • Framer 运动文档

  • 成帧运动课程

结论

在本文中,我们探索了使用 React 和 Framer Motion(动画和手势库)的强大组合来设计引人入胜且响应式图像轮播的过程。

通过合并诸如motion和 之类的组件AnimationPresence,我们能够穿过步骤来构建一个视觉上吸引人的旋转木马。该轮播展示了我们的图像,并通过迷人的动画提供图像之间的平滑过渡,以增强用户体验。

(更|多优质内|容:java567 点 c0m)