前端实现简单轮播图,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方法就能很好的解决这个问题