react制作的轮播图组件

预览效果

代码
逻辑部分,命名为panelPPT.tsx

import { Key, memo, useEffect, useRef, useState } from "react"
import sty from './panelPPT.module.scss'

export declare type PPTItemType = {
    key: Key
    imgsrc: string
    title?: string
}

declare interface PanelPPTProps {
    items: PPTItemType[]
    /**切换间隔,毫秒,默认3000 */
    duration?: number
    onItemClick?: (key: Key) => void
    className?:string
    style?: React.CSSProperties
}
/**面板-轮播图 */
const PanelPPT = memo((props: PanelPPTProps) => {
    const logic = PanelPPTLogic(props)
    return (
        <div className={`${sty.root} ${props.className || ''}`}
            style={props.style}
            ref={logic.setDomContainer}
            onMouseOver={logic.stop}
            onMouseLeave={logic.start}
        >
            <div className={sty.scrollbox}>
                <ul className={sty['list-bg']} style={{ left: `${logic.listOffsetX}%` }}>
                    {
                        props.items.map(d => (
                            <li className={sty.listitem} key={d.key} style={{ backgroundImage: `url(${d.imgsrc})` }} />
                        ))
                    }
                </ul>
            </div>
            <ul className={sty['list-small']}>
                {
                    props.items.map((d, i) => (
                        <li className={`${sty.listitem} ${i === logic.index ? sty.focus : ''}`}
                            key={d.key}
                            title={d.title}
                            onMouseOver={() => logic.handleMouseover(d)}
                            onMouseLeave={logic.start}
                            onClick={() => logic.handleItemClick(d)}
                            style={{ backgroundImage: `url(${d.imgsrc})` }} />
                    ))
                }
            </ul>
        </div>
    )
})

export default PanelPPT

const PanelPPTLogic = (props: PanelPPTProps) => {
    const ctrlState = useRef({
        timer: undefined as undefined | NodeJS.Timer,
        index: 0
    })
    const [domContainer, setDomContainer] = useState<HTMLDivElement | null>()
    const [listOffsetX, setListOffsetX] = useState(0)
    const [index, setIndex] = useState(0)
    const start = () => {
        stop()
        ctrlState.current.timer = setInterval(() => {
            if (ctrlState.current.index < props.items.length - 1) {
                ctrlState.current.index++
            } else {
                ctrlState.current.index = 0
            }
            setListOffsetX(-ctrlState.current.index * 100)
            setIndex(ctrlState.current.index)
        }, props.duration || 3000)
    }
    const stop = () => {
        clearInterval(ctrlState.current.timer)
    }
    const handleMouseover = (item: PPTItemType) => {
        stop()
        ctrlState.current.index = props.items.indexOf(item)
        setListOffsetX(-ctrlState.current.index * 100)
        setIndex(ctrlState.current.index)
    }
    const handleItemClick = (item: PPTItemType) => {
        if (props.onItemClick) {
            props.onItemClick(item.key)
        }
    }
    useEffect(() => {
        start()
        return () => {
            stop()
        }
    }, [props.items])
    return {
        domContainer, setDomContainer,
        listOffsetX,
        ctrlState,
        index,
        start,
        stop,
        handleMouseover,
        handleItemClick
    }
}

样式部分,命名为panelPPT.module.scss

.root {
    height  : 400px;
    width   : 100%;
    position: relative;

    .scrollbox {
        width   : 100%;
        height  : 100%;
        overflow: hidden;
        position: relative;

        .list-bg {
            width      : 100%;
            height     : 100%;
            white-space: nowrap;
            transition : all .5s ease-in-out;
            position   : absolute;

            .listitem {
                width          : 100%;
                height         : 100%;
                display        : inline-block;
                background-size: cover;
            }
        }
    }



    .list-small {
        position       : absolute;
        bottom         : -5px;
        display        : flex;
        justify-content: center;
        gap            : 1.6rem;
        width          : 100%;

        .listitem {
            width          : 176px;
            height         : 99px;
            background     : rgba(255, 255, 255, 0.3);
            border         : 1px solid rgba(255, 255, 255, 0.45);
            backdrop-filter: blur(16px);
            border-radius  : 6px;
            flex-shrink    : 0;
            overflow       : hidden;
            transition     : all .3s ease-in-out;
            cursor         : pointer;
            background-size: cover;
            filter         : drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.08));

            &:hover,
            &.focus {
                transform : scale(1.1);
                border    : 2px solid #fff;
                box-shadow: 0px 6px 30px 5px rgba(0, 0, 0, 0.05), 0px 16px 24px 2px rgba(0, 0, 0, 0.04), 0px 8px 10px -5px rgba(0, 0, 0, 0.08);
            }
        }
    }
}

@media only screen and (min-width: 1440px) {
    .root {
        height: 50rem;
    }
}

@media only screen and (max-width: 1440px) {
    .root {
        height: 40rem;
    }
}

你可能感兴趣的:(源码分享,web前端,react.js,javascript,前端)