React如何自定義輪播圖Carousel組件
自定義輪播圖Carousel組件
需求:
要求0-1自定義輪播圖組件,默認(rèn)自動(dòng)翻頁(yè),無(wú)限翻頁(yè),允許點(diǎn)擊翻頁(yè),底部有動(dòng)畫進(jìn)度條,且可配置輪播時(shí)間,可以操作Children.
架構(gòu)
React: ^18.0. Hooks. Css. FunctionComponent
- React : ^18.0 //未使用其他第三方庫(kù)
- Hooks //進(jìn)行狀態(tài)管理 模塊化設(shè)計(jì)
- Css //進(jìn)行樣式分離,xxx.module.css. 局部作用域樣式。防止Css全局污染 函數(shù)式編程.
需求解析
可點(diǎn)擊 + 可定義輪博時(shí)間 + 可無(wú)限輪播項(xiàng) + 動(dòng)畫進(jìn)度條 + 可配置輪播圖單項(xiàng)內(nèi)容 + 業(yè)務(wù)定制化
- 可點(diǎn)擊:允許用戶點(diǎn)擊底部進(jìn)度條進(jìn)行對(duì)應(yīng)索引翻頁(yè)
- 可定義輪播時(shí)間:當(dāng)前默認(rèn)為3秒,可以根據(jù)業(yè)務(wù)來(lái)調(diào)節(jié)時(shí)間 3000ms = 3s
- 可無(wú)限輪播項(xiàng):useEffect 進(jìn)行監(jiān)聽(tīng) 并進(jìn)行相應(yīng)的操作 實(shí)現(xiàn)無(wú)線輪播
- 動(dòng)畫進(jìn)度條:底色 #0000001a 黑色+10%的透明度 固定寬度,動(dòng)畫顏色為 #FFFFFF 動(dòng)態(tài)寬度
.css{ animation-name: progressBar; // 指定要綁定到選擇器的關(guān)鍵幀的名稱 name = progressBar animation-fill-mode: forwards; // 指定動(dòng)畫在執(zhí)行時(shí)間之外應(yīng)用的值 forwards = 保留最后一個(gè)關(guān)鍵幀設(shè)置的樣式值 animation-iteration-count: infinite; // 指定動(dòng)畫播放的次數(shù) infinite = 播放無(wú)限次 animation-duration: 3s // 一個(gè)周期所需的時(shí)間長(zhǎng)度 3s = 3秒 }
可配置輪播圖單項(xiàng)內(nèi)容: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 輪播圖 單項(xiàng) */ 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 標(biāo)題 * @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 間隔時(shí)間 默認(rèn)3秒 以毫秒為單位 3000ms = 3s * @returns 輪播圖 容器 */ const Carousel = ({ children = React.createElement("div"), switchingTime = 3000, }) => { const time = ((switchingTime % 60000) / 1000).toFixed(0); // 將毫秒轉(zhuǎn)換為秒 const [activeIndex, setActiveIndex] = useState(0); // 對(duì)應(yīng)索引 /** * 更新索引 * @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(); }; /** * 重置動(dòng)畫 */ const replayAnimations = () => { document.getAnimations().forEach((anim) => { anim.cancel(); anim.play(); }); }; /** * 底部加載條點(diǎn)擊事件 * @param {index} index 跳轉(zhuǎn)索引 */ 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"; // 輪播圖數(shù)據(jù) 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;
效果圖
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解React??App.js?文件的結(jié)構(gòu)和作用
在React應(yīng)用中,App.js文件通常是項(xiàng)目的根組件文件,它負(fù)責(zé)組織和渲染其他組件,是應(yīng)用的核心部分,本文將詳細(xì)介紹App.js文件的結(jié)構(gòu)、作用和最佳實(shí)踐,感興趣的朋友跟隨小編一起看看吧2024-08-08React-Native TextInput組件詳解及實(shí)例代碼
這篇文章主要介紹了React-Native TextInput組件詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-10-10react 權(quán)限樹(shù)形結(jié)構(gòu)實(shí)現(xiàn)代碼
這篇文章主要介紹了react 權(quán)限樹(shù)形結(jié)構(gòu)實(shí)現(xiàn)代碼,項(xiàng)目背景react + ant design,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2024-05-05React-intl 實(shí)現(xiàn)多語(yǔ)言的示例代碼
本篇文章主要介紹了React-intl 實(shí)現(xiàn)多語(yǔ)言的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11基于visual studio code + react 開(kāi)發(fā)環(huán)境搭建過(guò)程
今天通過(guò)本文給大家分享基于visual studio code + react 開(kāi)發(fā)環(huán)境搭建過(guò)程,本文給大家介紹的非常詳細(xì),包括react安裝問(wèn)題及安裝 Debugger for Chrome的方法,需要的朋友跟隨小編一起看看吧2021-07-07React中的權(quán)限組件設(shè)計(jì)問(wèn)題小結(jié)
這篇文章主要介紹了React中的權(quán)限組件設(shè)計(jì),整個(gè)過(guò)程也是遇到了很多問(wèn)題,本文主要來(lái)做一下此次改造工作的總結(jié),對(duì)React權(quán)限組件相關(guān)知識(shí)感興趣的朋友一起看看吧2022-07-07