前端实现简单轮播图,js实现一个无缝轮播图
吐槽 公司首页有一个动态显示数据的板块,同事直接用定时器手动修改div里面的数据,后来要求要有一个动态轮播滚动效果。哎,没办法,加入这个项目后就是在优化改写别人的代码,以前没测试到的bug,现在测出来让我修复,以前没实现的功能让我去实现。一堆简单堆砌的代码,重复用到的地方就是再复制一份,改起来都头大,只能自己一个一个重写再封装成组件。
这个轮播图组件只是简单练手,实际最好使用成熟的swiper库。
js文件
/* eslint-disable react/no-array-index-key */
/**
* @file 简易轮播图组件,建议直接使用swiper库(https://github.com/nolimits4web/swiper, https://swiperjs.com/react)
* @author Anin
* @lastEditors
*/
import React, { FC, memo, useEffect, useState } from 'react'
import './index.less';
interface AnSwiperProps {
items?: any[];
speed?: number;
autoplay?: boolean;
paginationRender?: any;
slidesPerView?: number; // 同时显示的slides数量
}
export interface AnSwiperType extends FC<AnSwiperProps> {
AnSwiperSlide: FC<any>
}
let timer: NodeJS.Timer | null = null;
let length = 0;
let activeIndex = 0;
const AnSwiper: AnSwiperType = ({ autoplay = true, speed = 3000, slidesPerView = 1, paginationRender, children }) => {
const [index, setIndex] = useState(activeIndex);
// 无缝切换
const next = () => {
const wrapperDom: HTMLDivElement = document.querySelector('.AnSwiper-wrapper');
activeIndex = activeIndex + 1
wrapperDom.style.transition = 'transform 0.5s ease'
wrapperDom.style.transform = `translateX(-${activeIndex * (100 / slidesPerView)}%)`
if (activeIndex >= length) {
setTimeout(() => {
wrapperDom.style.transition = 'none'
wrapperDom.style.transform = 'translateX(0%)'
activeIndex = 0
setIndex(0)
}, 500);
return;
}
setIndex(activeIndex)
}
const prev = () => {
const wrapperDom: HTMLDivElement = document.querySelector('.AnSwiper-wrapper');
activeIndex = activeIndex - 1
wrapperDom.style.transition = 'transform 0.5s ease'
wrapperDom.style.transform = `translateX(-${activeIndex * (100 / slidesPerView)}%)`
if (activeIndex <= 0) {
setTimeout(() => {
wrapperDom.style.transition = 'none'
wrapperDom.style.transform = `translateX(-${length * (100 / slidesPerView)}%)`
activeIndex = length
setIndex(length)
}, 500);
return;
}
setIndex(activeIndex)
};
const start = () => {
if (timer) {
clearInterval(timer);
timer = null;
}
timer = setInterval(next, speed);
}
// 鼠标划入
const handleMouseOver = () => {
if (timer) {
clearInterval(timer);
timer = null;
}
};
// 鼠标划出
const handleMouseLeave = () => {
if (!timer && length > 1) {
start()
}
};
useEffect(() => {
document.body.style.setProperty('--slidesPerView', `${100 / slidesPerView}%`);
if (children) {
if (Array.isArray(children)) {
length = children.length;
if (autoplay && children?.length > 0) {
start()
}
} else if (typeof children === 'object') {
length = 1
}
}
return () => {
if (timer) {
clearInterval(timer);
timer = null;
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [autoplay, children])
console.log(index)
return (<div className='AnSwiper' onMouseOver={handleMouseOver} onMouseLeave={handleMouseLeave}>
<div className='AnSwiper-wrapper' >
{children?.[children?.length - 1]}
{children}
{children?.[0]}
</div>
{paginationRender && <ul className='pagination-wrap'>
{
([... new Array(length)].map((_, i) => (
<div key={i} onClick={()=> {
activeIndex = i+ 1;
setIndex(i + 1)
document.querySelector('.AnSwiper-wrapper').style.transform = `translateX(-${(i+1) * (100 / slidesPerView)}%)`
}}>
{/* {paginationRender((i +1) === index) || (i === length -1) } */}
{paginationRender(i === index -1 || (index === 0 && i === length -1))}
</div>
)
))
}
</ul>
}
</div>)
}
function ANSwiperSlide({ children }) {
return <div className='AnSwiper-slide'>{children}</div>
}
// AnSwiper.AnSwiperSlide = AnSwiperSlide
export const AnSwiperSlide = memo(ANSwiperSlide);
export default memo(AnSwiper);
less 文件
.AnSwiper {
width: 100%;
height: fit-content;
position: relative;
overflow: hidden;
.AnSwiper-wrapper {
display: flex;
// transition: transform 0.5s ease;
transform: translateX(-100%);
.AnSwiper-slide {
// width: 30%;
flex: 0 0 var(--slidesPerView);
}
}
.pagination-wrap {
position: absolute;
padding-left: 35px;
bottom: 0;
width: 100%;
}
}
使用
import AnSwiper, { AnSwiperSlide } from '@/components/AnSwiper';
const App = () => {
const list = [
{
name: '1',
icon: 'icon'
},
{
name: '2',
icon: 'icon'
},
{
name: '3',
icon: 'icon'
},
{
name: '4',
icon: 'icon'
},
] reutrn (<div> <AnSwiper paginationRender={(isActive)=> { return <li className={`pagination ${isActive ? 'tabs-active': 'tabs-item'}`}></li> }} > {list.map((item)=> ( <AnSwiperSlide key={item?.name}> <div className='banner-box'> <img className="banner-img-cock" src={item.icon} /> </div> </AnSwiperSlide>))} </AnSwiper> ) </div>) }
css
.tabs-item {
width: 12px;
height: 4px;
border-radius: 4px;
float: left;
margin-right: 4px;
background: #d4e5ff;
cursor: pointer;
}
.tabs-active {
width: 12px;
height: 4px;
border-radius: 4px;
float: left;
margin-right: 4px;
background: #2468f2;
cursor: pointer;
}
style.setProperty 方法用来动态改变样式。有时候不能通过动态添加修改类名的方式改变样式的时候,使用style.setProperty方法就能很好的解决这个问题

浙公网安备 33010602011771号