react 組件實(shí)現(xiàn)無縫輪播示例詳解
正文
需求是做一個(gè)無縫輪播圖,我說這不是有很多現(xiàn)成的輪子嗎?后來了解到他有一個(gè)特殊的需求,他要求小圓點(diǎn)需要在輪播圖外面,因?yàn)楝F(xiàn)在大部分插件都是將小圓點(diǎn)寫在輪播圖內(nèi)部的,這對(duì)于不了解插件內(nèi)部結(jié)構(gòu)的小伙伴確實(shí)不知道如何修改。

很久沒有寫插件的我準(zhǔn)備寫一個(gè)插件(react)
無縫輪播
無縫輪播從最后一張到第一張的過程中不會(huì)原路返回,它就像輪子似的,從結(jié)束到開始是無縫連接的,非常自然地循環(huán)下去。

實(shí)現(xiàn)思路
輪播圖的實(shí)現(xiàn)思路有很多,我們這里采用的是最簡(jiǎn)單的輪播圖方案,如上圖,即當(dāng)輪播圖輪播到第x張圖片時(shí),當(dāng)前整個(gè)輪播圖列表中只保留第x張圖片,其余圖片dom全部隱藏掉即可。
那么大家有一個(gè)疑問,這樣不會(huì)導(dǎo)致切換時(shí)不連貫嗎?這個(gè)大家不必?fù)?dān)心,我們可以在上一個(gè)輪播圖小時(shí)和下一個(gè)輪播圖展現(xiàn)時(shí)增加動(dòng)畫效果,來達(dá)到連貫的感覺。
構(gòu)思使用時(shí)代碼結(jié)構(gòu)
參考了大部分輪播圖組件,得出來下面的這種使用結(jié)構(gòu)。
import Carousel,{ Item } from '組件'
render(){
return (
<Carousel>
<Item><img src="xxx" /></Item>
<Item><img src="xxx" /></Item>
<Item><img src="xxx" /></Item>
</Carousel>
)
}
Carousel組件
新建Carousel組件,這個(gè)組件是組件的整體框架文件。
內(nèi)部增加inner div 來充當(dāng)當(dāng)前展示輪播圖的視口
const Carousel=()=>{
return (
<div className={'carousel'}>
<div className={'carousel-inner'}>
{xxx輪播圖xxx}
</div>
</div>
)
}
overflow:hidden是關(guān)鍵
.inner{
width:100%;
height:100%;
position: relative;
overflow: hidden;
}
CarouselItem組件
新建CarouselItem組件,這個(gè)組件是Carousel組件的子組件,是輪播圖列表每一項(xiàng)的容器。
const CarouselItem=(props)=>{
return (
<div className={'carousel-item'}>
{props.children}
</div>
)
}
注意 這里需要使用top0 left0 不然顯示的時(shí)候會(huì)從上往上偏移 導(dǎo)致錯(cuò)誤顯示
.carousel-item{
position: absolute;
left:0;
top:0;
width: 100%;
height:100%;
}
完善組件
- 如何顯示當(dāng)前輪播圖元素
之前我們說過這次我們輪播圖的核心思路是顯示當(dāng)前元素,那么什么情況下顯示當(dāng)前元素呢?
carousel組件內(nèi)有一個(gè)state表示當(dāng)前輪播到第幾張圖 我們這里叫current,而根據(jù)傳入carousel的元素排序,我們可以在item內(nèi)部拿到當(dāng)前是第幾張圖片,然后2者如果是相等的,就顯示當(dāng)圖片。
我們?nèi)绾潍@取元素當(dāng)前的index呢?我們可以利用react提供的解析children的api
// util
import React from "react";
export function injecteIndex(children:React.ElementType,current):any{
let result:React.ReactElement[]=[];
React.Children.forEach(children,(item,index)=>{
result.push(React.cloneElement((item as any),{
//selfIndex即為輪播圖的index
selfIndex:index,
key:index,
current
}))
});
return result;
}
//carousel組件
//initial 為傳入配置 默認(rèn)為0 即默認(rèn)展示第一張圖
const Carousel=()=>{
const {
initial
}=props;
const [current,setCurrent]=useState<number>(initial);
return (
<div className={'carousel'}>
<div className={'carousel-inner'}>
{injecteIndex(children,current)}
</div>
</div>
)
}
//carousel-item組件
const CarouselItem=(props)=>{
const { selfIndex,current }=props;
const visible=selfIndex===current
return (
{visible && <div className={'carousel-item'}>
{props.children}
</div>}
)
}
- 實(shí)現(xiàn)autoPlay
上面我們其實(shí)已經(jīng)實(shí)現(xiàn)了第一張圖的展示,那么我們?nèi)绾巫屗麆?dòng)起來呢?其實(shí)也很簡(jiǎn)單,我們一起來實(shí)現(xiàn)一下
//useLatest
import { useEffect, useRef } from "react"
export default (params:any)=>{
const latest=useRef();
useEffect(()=>{
latest.current=params;
})
return latest;
}
//carousel組件
const Carousel=(props)=>{
const {
//initial 為傳入配置 默認(rèn)為0 即默認(rèn)展示第一張圖
initial=0,
//是否自動(dòng)輪播 默認(rèn)為是
autoplay=true,
//時(shí)間間隔 默認(rèn)為3秒
interval=3000
}=props;
//新建定時(shí)器存儲(chǔ)變量
const timer:any=useRef(null);
const [current,setCurrent]=useState<number>(initial);
const [itemLength]=useState<number>(React.Children.count(children));
//解決閉包問題
const latest:any=useLatest(current);
const autoPlay=()=>{
//設(shè)置定時(shí)器 每隔3s切換到下一張圖片
timer.current=setInterval(()=>{
setStep('next');
},interval);
}
const setStep=(direction:'prev'|'next')=>{
switch(direction){
case 'prev':
let prevStep:number=latest.current-1;
//當(dāng)為第一張時(shí) 跳到第5張
if(prevStep===-1){
prevStep=itemLength-1;
}
setCurrent(prevStep);
break;
case 'next':
let nextStep:number=latest.current+1;
//當(dāng)為最后一張時(shí) 跳到第1張
if(nextStep===itemLength){
nextStep=0;
}
setCurrent(nextStep);
break;
default:
}
}
useEffect(()=>{
if(autoplay){
//自動(dòng)輪播
autoPlay();
}
return ()=>{
//銷毀定時(shí)器
clearInterval(timer.current);
timer.current=null;
}
},[autoplay]);
return (
<div className={'carousel'}>
<div className={'carousel-inner'}>
{injecteIndex(children,current)}
</div>
</div>
)
}
```# 完成動(dòng)畫
其實(shí)上面我們已經(jīng)把無縫輪播業(yè)務(wù)邏輯完成了,那么如何讓他輪播起來呢?我們這里使用react官方推薦的一個(gè)過渡庫(kù),因?yàn)閞eact并沒有像vue為我們提供transition api。
```js
const CarouselItem=(props)=>{
const {
children,
selfIndex
}=props;
const visible=selfIndex===current;
return (
<CSSTransition mountOnEnter unmountOnExit appear in={visible} timeout={500} classNames={'carousel'}>
<div className={'carousel-item'} >
{children}
</div>
</CSSTransition>
)
});
.carousel-enter-active,.carousel-exit-active{
transition: all 500ms linear;
}
.carousel-enter{
transform: translateX(100%);
opacity: 0.8;
}
.carousel-enter-active{
transform: translateX(0);
opacity: 1;
}
.carousel-exit{
transform: translateX(0);
opacity: 1;
}
.carousel-exit-active{
transform: translateX(-100%);
opacity: 0.8;
}
之前我們?cè)O(shè)置的top:0 left:0 可以得出輪播元素的原點(diǎn)都是容器的左上角 在enter過程將translateX從100到0 即可展示從右往左的動(dòng)畫效果 在exit過程前將translateX從0到-100 即可展示從左往右的動(dòng)畫效果
完成小圓點(diǎn)
其實(shí)讓我寫輪播圖的老兄 最大的難點(diǎn)是小圓點(diǎn)的位置 因?yàn)榇蟛糠州啿D 小圓點(diǎn)部分都是寫在inner里面的 而inner元素為overflow:hidden,即不管如何小圓點(diǎn),他都會(huì)溢出隱藏。所以這次我們把小圓點(diǎn)放在inner元素上,并且提供一個(gè)屬性讓小圓點(diǎn)可調(diào)。
//carousel
<div className={classes}>
<div className={'inner'}>
{injecteIndex(children)}
</div>
<Dots length={itemLength} position={dotPosition}/>
</div>
//dots原點(diǎn)
<div className={classnames(
classes,
)} style={{...position}}>
{
newArray.map((item,index)=>(
<div className={classnames(`${prefixCls}-item`,{
'active':current===index
})} key={index} onClick={()=>onClick(index)}/>
))
}
</div>實(shí)現(xiàn)效果

源碼地址
如果大家有想使用的話 可以放心 使用 源碼已發(fā)到github和npm上面
npm install @parrotjs/carousel -S
以上就是react 組件實(shí)現(xiàn)無縫輪播示例詳解的詳細(xì)內(nèi)容,更多關(guān)于react 組件無縫輪播的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
useEvent顯著降低Hooks負(fù)擔(dān)的原生Hook
這篇文章主要為大家介紹了useEvent顯著降低Hooks負(fù)擔(dān)的原生Hook示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
TypeScript在React中的應(yīng)用技術(shù)實(shí)例解析
這篇文章主要為大家介紹了TypeScript在React中的應(yīng)用技術(shù)實(shí)例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
一文搞懂?React?18?中的?useTransition()?與?useDeferredValue()
這篇文章主要介紹了一文搞懂?React?18?中的?useTransition()與useDeferredValue(),文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
解讀useState第二個(gè)參數(shù)的"第二個(gè)參數(shù)"
這篇文章主要介紹了useState第二個(gè)參數(shù)的"第二個(gè)參數(shù)",具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
React+EggJs實(shí)現(xiàn)斷點(diǎn)續(xù)傳的示例代碼
這篇文章主要介紹了React+EggJs實(shí)現(xiàn)斷點(diǎn)續(xù)傳的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07

