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)思路有很多,我們這里采用的是最簡單的輪播圖方案,如上圖,即當(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í)也很簡單,我們一起來實(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è)過渡庫,因?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)文章
詳解react、redux、react-redux之間的關(guān)系
這篇文章主要介紹了詳解react、redux、react-redux之間的關(guān)系,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧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ò)請(qǐng)求的方法
axios是一個(gè)基于Promise的Http網(wǎng)絡(luò)庫,可運(yùn)行在瀏覽器端和Node.js中,Vue應(yīng)用的網(wǎng)絡(luò)請(qǐng)求基本都是使用它完成的。這篇文章主要介紹了React Native使用axios進(jìn)行網(wǎng)絡(luò)請(qǐng)求,需要的朋友可以參考下2021-08-08使用react-native-image-viewer實(shí)現(xiàn)大圖預(yù)覽
這篇文章主要介紹了使用react-native-image-viewer實(shí)現(xiàn)大圖預(yù)覽,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09react-router browserHistory刷新頁面404問題解決方法
本篇文章主要介紹了react-router browserHistory刷新頁面404問題解決方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-12-12