react 組件實現(xiàn)無縫輪播示例詳解
正文
需求是做一個無縫輪播圖,我說這不是有很多現(xiàn)成的輪子嗎?后來了解到他有一個特殊的需求,他要求小圓點需要在輪播圖外面,因為現(xiàn)在大部分插件都是將小圓點寫在輪播圖內(nèi)部的,這對于不了解插件內(nèi)部結(jié)構(gòu)的小伙伴確實不知道如何修改。
很久沒有寫插件的我準(zhǔn)備寫一個插件(react)
無縫輪播
無縫輪播從最后一張到第一張的過程中不會原路返回,它就像輪子似的,從結(jié)束到開始是無縫連接的,非常自然地循環(huán)下去。
實現(xiàn)思路
輪播圖的實現(xiàn)思路有很多,我們這里采用的是最簡單的輪播圖方案,如上圖,即當(dāng)輪播圖輪播到第x張圖片時,當(dāng)前整個輪播圖列表中只保留第x張圖片,其余圖片dom全部隱藏掉即可。
那么大家有一個疑問,這樣不會導(dǎo)致切換時不連貫嗎?這個大家不必?fù)?dān)心,我們可以在上一個輪播圖小時和下一個輪播圖展現(xiàn)時增加動畫效果,來達(dá)到連貫的感覺。
構(gòu)思使用時代碼結(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組件,這個組件是組件的整體框架文件。
內(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組件,這個組件是Carousel組件的子組件,是輪播圖列表每一項的容器。
const CarouselItem=(props)=>{ return ( <div className={'carousel-item'}> {props.children} </div> ) }
注意 這里需要使用top0 left0 不然顯示的時候會從上往上偏移 導(dǎo)致錯誤顯示
.carousel-item{ position: absolute; left:0; top:0; width: 100%; height:100%; }
完善組件
- 如何顯示當(dāng)前輪播圖元素
之前我們說過這次我們輪播圖的核心思路是顯示當(dāng)前元素,那么什么情況下顯示當(dāng)前元素呢?
carousel組件內(nèi)有一個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>} ) }
- 實現(xiàn)autoPlay
上面我們其實已經(jīng)實現(xiàn)了第一張圖的展示,那么我們?nèi)绾巫屗麆悠饋砟兀科鋵嵰埠芎唵?,我們一起來實現(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, //是否自動輪播 默認(rèn)為是 autoplay=true, //時間間隔 默認(rèn)為3秒 interval=3000 }=props; //新建定時器存儲變量 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è)置定時器 每隔3s切換到下一張圖片 timer.current=setInterval(()=>{ setStep('next'); },interval); } const setStep=(direction:'prev'|'next')=>{ switch(direction){ case 'prev': let prevStep:number=latest.current-1; //當(dāng)為第一張時 跳到第5張 if(prevStep===-1){ prevStep=itemLength-1; } setCurrent(prevStep); break; case 'next': let nextStep:number=latest.current+1; //當(dāng)為最后一張時 跳到第1張 if(nextStep===itemLength){ nextStep=0; } setCurrent(nextStep); break; default: } } useEffect(()=>{ if(autoplay){ //自動輪播 autoPlay(); } return ()=>{ //銷毀定時器 clearInterval(timer.current); timer.current=null; } },[autoplay]); return ( <div className={'carousel'}> <div className={'carousel-inner'}> {injecteIndex(children,current)} </div> </div> ) } ```# 完成動畫 其實上面我們已經(jīng)把無縫輪播業(yè)務(wù)邏輯完成了,那么如何讓他輪播起來呢?我們這里使用react官方推薦的一個過渡庫,因為react并沒有像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; }
之前我們設(shè)置的top:0 left:0 可以得出輪播元素的原點都是容器的左上角 在enter過程將translateX從100到0 即可展示從右往左的動畫效果 在exit過程前將translateX從0到-100 即可展示從左往右的動畫效果
完成小圓點
其實讓我寫輪播圖的老兄 最大的難點是小圓點的位置 因為大部分輪播圖 小圓點部分都是寫在inner里面的 而inner元素為overflow:hidden,即不管如何小圓點,他都會溢出隱藏。所以這次我們把小圓點放在inner元素上,并且提供一個屬性讓小圓點可調(diào)。
//carousel <div className={classes}> <div className={'inner'}> {injecteIndex(children)} </div> <Dots length={itemLength} position={dotPosition}/> </div> //dots原點 <div className={classnames( classes, )} style={{...position}}> { newArray.map((item,index)=>( <div className={classnames(`${prefixCls}-item`,{ 'active':current===index })} key={index} onClick={()=>onClick(index)}/> )) } </div>
實現(xiàn)效果
源碼地址
如果大家有想使用的話 可以放心 使用 源碼已發(fā)到github和npm上面
npm install @parrotjs/carousel -S
以上就是react 組件實現(xiàn)無縫輪播示例詳解的詳細(xì)內(nèi)容,更多關(guān)于react 組件無縫輪播的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解react、redux、react-redux之間的關(guān)系
這篇文章主要介紹了詳解react、redux、react-redux之間的關(guān)系,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04React Native可復(fù)用 UI分離布局組件和狀態(tài)組件技巧
這篇文章主要為大家介紹了React Native可復(fù)用 UI分離布局組件和狀態(tài)組件使用技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09關(guān)于React Native使用axios進(jìn)行網(wǎng)絡(luò)請求的方法
axios是一個基于Promise的Http網(wǎng)絡(luò)庫,可運行在瀏覽器端和Node.js中,Vue應(yīng)用的網(wǎng)絡(luò)請求基本都是使用它完成的。這篇文章主要介紹了React Native使用axios進(jìn)行網(wǎng)絡(luò)請求,需要的朋友可以參考下2021-08-08使用react-native-image-viewer實現(xiàn)大圖預(yù)覽
這篇文章主要介紹了使用react-native-image-viewer實現(xiàn)大圖預(yù)覽,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09react-router browserHistory刷新頁面404問題解決方法
本篇文章主要介紹了react-router browserHistory刷新頁面404問題解決方法,非常具有實用價值,需要的朋友可以參考下2017-12-12