React如何自定義輪播圖Carousel組件
自定義輪播圖Carousel組件
需求:
要求0-1自定義輪播圖組件,默認(rèn)自動翻頁,無限翻頁,允許點(diǎn)擊翻頁,底部有動畫進(jìn)度條,且可配置輪播時間,可以操作Children.
架構(gòu)
React: ^18.0. Hooks. Css. FunctionComponent
- React : ^18.0 //未使用其他第三方庫
- Hooks //進(jìn)行狀態(tài)管理 模塊化設(shè)計
- Css //進(jìn)行樣式分離,xxx.module.css. 局部作用域樣式。防止Css全局污染 函數(shù)式編程.
需求解析
可點(diǎn)擊 + 可定義輪博時間 + 可無限輪播項 + 動畫進(jìn)度條 + 可配置輪播圖單項內(nèi)容 + 業(yè)務(wù)定制化
- 可點(diǎn)擊:允許用戶點(diǎn)擊底部進(jìn)度條進(jìn)行對應(yīng)索引翻頁
- 可定義輪播時間:當(dāng)前默認(rèn)為3秒,可以根據(jù)業(yè)務(wù)來調(diào)節(jié)時間 3000ms = 3s
- 可無限輪播項:useEffect 進(jìn)行監(jiān)聽 并進(jìn)行相應(yīng)的操作 實現(xiàn)無線輪播
- 動畫進(jìn)度條:底色 #0000001a 黑色+10%的透明度 固定寬度,動畫顏色為 #FFFFFF 動態(tài)寬度
.css{
animation-name: progressBar; // 指定要綁定到選擇器的關(guān)鍵幀的名稱 name = progressBar
animation-fill-mode: forwards; // 指定動畫在執(zhí)行時間之外應(yīng)用的值 forwards = 保留最后一個關(guān)鍵幀設(shè)置的樣式值
animation-iteration-count: infinite; // 指定動畫播放的次數(shù) infinite = 播放無限次
animation-duration: 3s // 一個周期所需的時間長度 3s = 3秒
}
可配置輪播圖單項內(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 輪播圖 單項
*/
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 間隔時間 默認(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); // 對應(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();
};
/**
* 重置動畫
*/
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é)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Native?Memory?Tracking追蹤區(qū)域示例分析
這篇文章主要為大家介紹了Native?Memory?Tracking追蹤區(qū)域示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
react-native-webview和h5通信用法及說明
本文詳細(xì)介紹了React?Native和WebView之間的通信方式,包括HTML向RN通信、RN向HTML通信以及使用postMessage進(jìn)行消息通信,通過具體的函數(shù)和代碼示例,展示了如何實現(xiàn)雙向通信,并提供了一個在項目中實際應(yīng)用的調(diào)用示例2026-03-03
Redux thunk中間件及執(zhí)行原理詳細(xì)分析
redux的核心概念其實很簡單:將需要修改的state都存入到store里,發(fā)起一個action用來描述發(fā)生了什么,用reducers描述action如何改變state tree,這篇文章主要介紹了Redux thunk中間件及執(zhí)行原理分析2022-09-09
React報錯Element type is invalid解決案例
這篇文章主要為大家介紹了React報錯Element type is invalid解決案例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
React中的State和Props深入理解及關(guān)鍵區(qū)別
state和props是React中兩個核心概念,它們共同構(gòu)成了組件的數(shù)據(jù)管理機(jī)制,本文將詳細(xì)探討 state 和 props 的定義、用途、特點(diǎn)以及它們之間的關(guān)鍵區(qū)別,并提供一些最佳實踐,感興趣的朋友跟隨小編一起看看吧2026-01-01
React創(chuàng)建組件的三種方式及其區(qū)別是什么
在React中,創(chuàng)建組件的三種主要方式是函數(shù)式組件、類組件和使用React Hooks的函數(shù)式組件,本文就詳細(xì)的介紹一下如何使用,感興趣的可以了解一下2023-08-08
React狀態(tài)更新后視圖不刷新的常見原因及解決方案
如果你是一名React開發(fā)者,那么你一定遇到過這樣的場景:明明已經(jīng)調(diào)用了setState或useState的更新函數(shù),但頁面卻像被施了魔法一樣紋絲不動,本文將深入剖析React狀態(tài)更新后視圖不刷新的常見原因,并提供解決方案,需要的朋友可以參考下2026-05-05
React+Redux實現(xiàn)簡單的待辦事項列表ToDoList
這篇文章主要為大家詳細(xì)介紹了React+Redux實現(xiàn)簡單的待辦事項列表ToDoList,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-09-09

