使用react&hooks复刻网易云音乐轮播图 | 青训营笔记

215 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第二天

基于react&hooks的轮播图制作

在月影大佬教我们写js的时候提到了组件制作,并以轮播图为例进行讲解,而作者最近刚好在临摹网易云音乐 的页面,而其中偌大的一个组件就是轮播图,

尽管作者之前已有实现,但实现的并不优雅:毕竟组件需要一定的解耦,而之前作者是采用了redux做状态管理,与页面是一个强耦合的关系。于是在学习了那节课之后,笔者重新制作轮播图组件

不过与月影大大教的不一样的是我并没有采用原生的js,而是使用了react与hooks制作轮播图

以下就是制作思路:

  • HTML与CSS构思结构与样式:其实这一部分可以直接去参考官网样式,但主要是为了数值与样式,结构上没有必要完全照搬。它的轮播图大概可以划出以下几个层级:

    1. 大容器: 装下这个组件,100%的宽度
    2. 背景图: 随着轮播图的变化而变化,具体样式直接F12
    3. 轮播图: 会有fade out的效果
    4. 左右的两个箭头:可以用<>,也可以把它的png下载下来用
    5. 下方的一排点:本以为是div做的,没想到也是png
    6. 右侧的广告,也是最烦人的,因为它的存在没法很好的把组件抽象出来(当然可以考虑加一个props的属性,从外部把这一坨给传入)
  • js逻辑:

    1. 定时切换
    2. 左右箭头:上一张与下一张
    3. 通过点击点来切换图片
    4. hover时暂停播放
  • 传入组件的参数:

    • 图片列表
    • 播放周期
    • 是否需要左右箭头
    • 是否需要下方的点
  • 然后可以采用useState存储图片索引

  • 然后考虑useEffect这个hooks,而useEffect中放入计时器时需要一个依赖数组的引入,而这个数组中首先需要监听图片索引的变化,其次监听图片列表的变化(图片列表有可能是从后端取到的,在加载组件的时候很可能是空的,加载完成之后才会引入,为了避免出现bug)

  • 然后就发现悬停后移开鼠标轮播图依然不动,这是因为清除掉了定时器,而此时索引没有变,列表也没有变,useEffect自然也就不会触发了,那么需要考虑再次引入一个值,当悬停时会改变状态

叨叨叨的文字扯完了,上代码,我的代码逻辑实现大致如下:

const Swiper = (props) => {
    const {
        picList,
        cycle,
        ifArrow,
        ifDot,
    } = props
    const numPic = picList.length;
    const list = Array.from(Array(numPic), (item, index) => index);
​
    const [indexPic, setIndexPic] = useState(0);
    const idTimer = useRef(0);
    const [play, setPlay] = useState(true);
​
    const nextImage = () => {
        clearInterval(idTimer.current);
        setIndexPic(indexPic => (indexPic + 1) % parseInt(numPic));
    };
​
    const lastImage = () => {
        clearInterval(idTimer.current);
        setIndexPic(indexPic => (indexPic + numPic - 1) % parseInt(numPic));
    };
​
    const pickPoint = (index) => {
        clearInterval(idTimer.current);
        setIndexPic(index);
    };
​
    const startSwiper = () => {
        stopSwiper();
        idTimer.current = setInterval(() => setPlay(!play), cycle);
    };
​
    const stopSwiper = () => {
        clearInterval(idTimer.current);
    };
​
    useEffect(() => {
        idTimer.current = setInterval(() => {
            if (numPic > 0) {
                nextImage();
            }
        }, cycle);
        return () => {
            clearInterval(idTimer.current);
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [indexPic, numPic, play]);
​
    return (
        ...
    )
}
​
export default Swiper;

更详细的代码可以参考我的github

本文仅为兴起时随手而写的一点文字,如有纰漏不足,还请多多批评斧正