React如何自定义轮播图Carousel组件
作者:贺知叶
这篇文章主要介绍了React如何自定义轮播图Carousel组件问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
自定义轮播图Carousel组件
需求:
要求0-1自定义轮播图组件,默认自动翻页,无限翻页,允许点击翻页,底部有动画进度条,且可配置轮播时间,可以操作Children.
架构
React: ^18.0. Hooks. Css. FunctionComponent
- React : ^18.0 //未使用其他第三方库
- Hooks //进行状态管理 模块化设计
- Css //进行样式分离,xxx.module.css. 局部作用域样式。防止Css全局污染 函数式编程.
需求解析
可点击 + 可定义轮博时间 + 可无限轮播项 + 动画进度条 + 可配置轮播图单项内容 + 业务定制化
- 可点击:允许用户点击底部进度条进行对应索引翻页
- 可定义轮播时间:当前默认为3秒,可以根据业务来调节时间 3000ms = 3s
- 可无限轮播项:useEffect 进行监听 并进行相应的操作 实现无线轮播
- 动画进度条:底色 #0000001a 黑色+10%的透明度 固定宽度,动画颜色为 #FFFFFF 动态宽度
.css{ animation-name: progressBar; // 指定要绑定到选择器的关键帧的名称 name = progressBar animation-fill-mode: forwards; // 指定动画在执行时间之外应用的值 forwards = 保留最后一个关键帧设置的样式值 animation-iteration-count: infinite; // 指定动画播放的次数 infinite = 播放无限次 animation-duration: 3s // 一个周期所需的时间长度 3s = 3秒 }
可配置轮播图单项内容:React.Children.map 和 React.cloneElement
需求解决
一、import Carousel, { CarouselItem, CarouselInfo } from “./Carousel”;
import React, { useState, useEffect } from "react"; import style from "./carousel.module.css"; /** * @param {children} children ReactNode * @param {width} width 宽度 * @param {height} height 高度 * @param {styles} styles 样式 * @returns 轮播图 单项 */ export const CarouselItem = ({ children = React.createElement("div"), width = "100%", height = "100%", styles = {}, }) => { return ( <div className={style.carousel_item} style={{ width: width, height: height, ...styles }} > {children} </div> ); }; /** * @param {title} title 标题 * @param {describe} describe 描述 * @param {image} image 图片 * @returns 轮播图 主体 */ export const CarouselInfo = ({ title = "", describe = "", image = "" }) => { return ( <div className="carousel_info_container"> <div className="carousel_info_info"> <h1>{title}</h1> <span>{describe}</span> </div> <div className="carousel_info_image_container"> <img src={image} alt="Jay" className="carousel_info_image" /> </div> </div> ); }; /** * @param {children} children ReactNode * @param {switchingTime} switchingTime 间隔时间 默认3秒 以毫秒为单位 3000ms = 3s * @returns 轮播图 容器 */ const Carousel = ({ children = React.createElement("div"), switchingTime = 3000, }) => { const time = ((switchingTime % 60000) / 1000).toFixed(0); // 将毫秒转换为秒 const [activeIndex, setActiveIndex] = useState(0); // 对应索引 /** * 更新索引 * @param {newIndex} newIndex 更新索引 */ const onUpdateIndex = (newIndex) => { if (newIndex < 0) { newIndex = React.Children.count(children) - 1; } else if (newIndex >= React.Children.count(children)) { newIndex = 0; } setActiveIndex(newIndex); replayAnimations(); }; /** * 重置动画 */ const replayAnimations = () => { document.getAnimations().forEach((anim) => { anim.cancel(); anim.play(); }); }; /** * 底部加载条点击事件 * @param {index} index 跳转索引 */ const onClickCarouselIndex = (index) => { onUpdateIndex(index); replayAnimations(); }; useEffect(() => { const interval = setInterval(() => { onUpdateIndex(activeIndex + 1); }, switchingTime); return () => { if (interval) { clearInterval(interval); } }; }); return ( <div className={style.container}> <div className={style.inner} style={{ transform: `translateX(-${activeIndex * 100}%)` }} > {React.Children.map(children, (child) => { return React.cloneElement(child, { width: "100%", height: "100vh" }); })} </div> <div className={style.loading}> {React.Children.map(children, (child, index) => { return ( <div className={style.indicator_outer} onClick={() => onClickCarouselIndex(index)} > <div className={style.indicator_inside} style={{ animationDuration: index === activeIndex ? `${time}s` : "0s", backgroundColor: index === activeIndex ? "#FFFFFF" : null, }} /> </div> ); })} </div> </div> ); }; export default Carousel;
二、import style from “./carousel.module.css”;
.container { overflow: hidden; } .inner { white-space: nowrap; transition: transform 0.3s; } .carousel_item { display: inline-flex; align-items: center; justify-content: center; height: 200px; color: #fff; background-color: #312520; } .loading { position: absolute; bottom: 0; display: flex; flex-direction: row; align-items: center; justify-content: center; margin-bottom: 10px; width: 100%; } .indicator_outer { width: 90px; height: 7px; background-color: #0000001a; margin-left: 20px; border-radius: 5px; } .indicator_inside { height: 100%; border-radius: 5px; animation-fill-mode: forwards; animation-name: progressBar; animation-iteration-count: infinite; } @keyframes progressBar { 0% { width: 0%; } 100% { width: 100%; } }
三、 App.js
import "./App.css"; import React, { useState } from "react"; import JayOne from "./assets/1.jpeg"; import JayTwo from "./assets/2.jpeg"; import JayThree from "./assets/3.jpeg"; import JayFour from "./assets/4.jpeg"; import Carousel, { CarouselItem, CarouselInfo } from "./Carousel"; // 轮播图数据 const info = [ { id: 1, title: "Jay", describe: "2000—11—07", image: JayOne, backgroundColor: "#425066", }, { id: 2, title: "范特西", describe: "2001—09—20", image: JayTwo, backgroundColor: "#1bd1a5", }, { id: 3, title: "范特西PLUS", describe: "2001—12—28", image: JayThree, backgroundColor: "#a78e44", }, { id: 4, title: "八度空间", describe: "2002—07—18", image: JayFour, backgroundColor: "#493131", }, ]; const App = () => { return ( <Carousel> {info?.map((item) => { return ( <CarouselItem key={item.id} styles={{ backgroundColor: item.backgroundColor }} > <CarouselInfo title={item.title} describe={item.describe} image={item.image} /> </CarouselItem> ); })} </Carousel> ); }; export default App;
效果图
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。