正文

需求是做一个无缝轮播图,我说这不是有很多现成的轮子吗?后来了解到他有一个特殊的需求,他要求小圆点需要在轮播图外面,因为现在大部分插件都是将小圆点写在轮播图内部的,这对于不了解插件内部结构的小伙伴确实不知道如何修改。

react组件实现无缝轮播示例详解

很久没有写插件的我准备写一个插件(react)

无缝轮播

无缝轮播从最后一张到第一张的过程中不会原路返回,它就像轮子似的,从结束到开始是无缝连接的,非常自然地循环下去。

react组件实现无缝轮播示例详解

实现思路

轮播图的实现思路有很多,我们这里采用的是最简单的轮播图方案,如上图,即当轮播图轮播到第x张图片时,当前整个轮播图列表中只保留第x张图片,其余图片dom全部隐藏掉即可。

那么大家有一个疑问,这样不会导致切换时不连贯吗?这个大家不必担心,我们可以在上一个轮播图小时和下一个轮播图展现时增加动画效果,来达到连贯的感觉。

构思使用时代码结构

参考了大部分轮播图组件,得出来下面的这种使用结构。

import Carousel,{ Item } from '组件'
render(){
return (
<Carousel>
<Item><img src="https://www.jb51.net/article/xxx" /></Item>
<Item><img src="https://www.jb51.net/article/xxx" /></Item>
<Item><img src="https://www.jb51.net/article/xxx" /></Item>
</Carousel>
)
}

Carousel组件

新建Carousel组件,这个组件是组件的整体框架文件。

内部增加inner div 来充当当前展示轮播图的视口

const Carousel=()=>{
return (
<div className={'carousel'}>
<div className={'carousel-inner'}>
{xxx轮播图xxx}
</div>
</div>
)
}

overflow:hidden是关键

.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 不然显示的时候会从上往上偏移 导致错误显示

.carousel-item{
position: absolute;
left:0;
top:0;
width: 100%;
height:100%;
}

完善组件

之前我们说过这次我们轮播图的核心思路是显示当前元素,那么什么情况下显示当前元素呢?

carousel组件内有一个state表示当前轮播到第几张图 我们这里叫current,而根据传入carousel的元素排序,我们可以在item内部拿到当前是第几张图片,然后2者如果是相等的,就显示当图片。

我们如何获取元素当前的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 为传入配置 默认为0 即默认展示第一张图
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>}
)
}

上面我们其实已经实现了第一张图的展示,那么我们如何让他动起来呢?其实也很简单,我们一起来实现一下

//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 为传入配置 默认为0 即默认展示第一张图
initial=0,
//是否自动轮播 默认为是
autoplay=true,
//时间间隔 默认为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=()=>{
//设置定时器 每隔3s切换到下一张图片
timer.current=setInterval(()=>{
setStep('next');
},interval);
}
const setStep=(direction:'prev'|'next')=>{
switch(direction){
case 'prev':
let prevStep:number=latest.current-1;
//当为第一张时 跳到第5张
if(prevStep===-1){
prevStep=itemLength-1;
}
setCurrent(prevStep);
break;
case 'next':
let nextStep:number=latest.current+1;
//当为最后一张时 跳到第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>
)
}
```# 完成动画
其实上面我们已经把无缝轮播业务逻辑完成了,那么如何让他轮播起来呢?我们这里使用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;
}

之前我们设置的top:0 left:0 可以得出轮播元素的原点都是容器的左上角 在enter过程将translateX从100到0 即可展示从右往左的动画效果 在exit过程前将translateX从0到-100 即可展示从左往右的动画效果

完成小圆点

其实让我写轮播图的老兄 最大的难点是小圆点的位置 因为大部分轮播图 小圆点部分都是写在inner里面的 而inner元素为overflow:hidden,即不管如何小圆点,他都会溢出隐藏。所以这次我们把小圆点放在inner元素上,并且提供一个属性让小圆点可调。

//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>

实现效果

react组件实现无缝轮播示例详解

源码地址

如果大家有想使用的话 可以放心 使用 源码已发到github和npm上面

npm install @parrotjs/carousel -S

以上就是react 组件实现无缝轮播示例详解的详细内容,更多关于react 组件无缝轮播的资料请关注本站其它相关文章!