正文
需求是做一个无缝轮播图 ,我说这不是有很多现成的轮子吗?后来了解到他有一个特殊的需求 ,他要求小圆点需要在轮播图外面 ,因为现在大部分插件都是将小圆点写在轮播图内部的 ,这对于不了解插件内部结构的小伙伴确实不知道如何修改 。
很久没有写插件的我准备写一个插件(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>}
)
}
实现autoPlay
上面我们其实已经实现了第一张图的展示 ,那么我们如何让他动起来呢?其实也很简单 ,我们一起来实现一下
//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>
实现效果
源码地址
如果大家有想使用的话 可以放心 使用 源码已发到github和npm上面
npm install @parrotjs/carousel -S
以上就是react 组件实现无缝轮播示例详解的详细内容 ,更多关于react 组件无缝轮播的资料请关注本站其它相关文章!
声明:本站所有文章,如无特殊说明或标注 ,均为本站原创发布 。任何个人或组织 ,在未征得本站同意时 ,禁止复制 、盗用 、采集 、发布本站内容到任何网站 、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益 ,可联系我们进行处理 。