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

posted @ 2023-05-10 12:29  anin  阅读(340)  评论(0编辑  收藏  举报