【react】基于umi框架编写轮播图组件

引言

基于该文仿写:web 完整轮播图——带只拖鞋去流浪 https://zhuanlan.zhihu.com/p/138232728

组件源码:https://gitee.com/leftstan/rotation.git

组件效果:https://www.jianguoyun.com/p/Dd81zscQzLDdCRiKuo4E

 

 

 

创建项目

创建一个umi2.x的项目

选择项目类型为app,不使用ts

npm create umi

 

安装依赖

yarn

 

运行项目

yarn start

 

组件调用

data为需要展示图片的数据

size轮播图组件尺寸

delay为播放时延(可选)

const data = [{   key: "1",   src: "https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/a532e33470d046b3f044d5ea49fc5e9e.png?thumb=1&w=1226&h=460&f=webp&q=90",   action: "https://www.mi.com" }, ... ]   <div>   {/* 轮播图数据 */}   {/* 轮播图尺寸, 单位px*/}   {/* 轮播时延 */}   <Rotation      data={data}      size={{ width: '1200', height: '480' }}      delay={2000}>    </Rotation> </div>

  

 

编写组件

 html布局为三部分内容

图片,导航按钮,上/下一页

        <div
            className={styles.content}
            style={{ width: size.width + 'px', height: size.height + 'px' }}
            onm ouseOver={() => stopPlay()}
            onm ouseLeave={() => autoPlay()}
        >
            {/* 图片展示轮播 */}
            <ul className={styles.picContent} id="picContent">
                {data.map((item) =>
                    <li key={`rotate${item.key}`} style={{ width: size.width + 'px' }}>
                        <a href={item.action}>
                            <img style={{ width: size.width + 'px', height: size.height + 'px' }} src={item.src} id={`rotate${item.key}`} />
                        </a>
                    </li>
                )}
            </ul>

            {/* 底部导航按钮跳转 */}
            <ul className={styles.dotButton}>
                {data.map((item, index) =>
                    <li
                        className={`${styles.dot} ${Math.abs(btn) === index ? styles.current : ''}`}
                        key={`btn${index}`} id={`btn${index}`}
                        onClick={() => jump(index)}
                    >
                    </li>
                )}
            </ul>

            {/* 上下页按钮 */}
            <div className={`${styles.btn} ${styles.left}`} onClick={() => {
                prev();
            }}><</div>
            <div className={`${styles.btn} ${styles.right}`} onClick={() => {
                next();
            }}>></div>
        </div>

  

无缝切换

需要在图片【盒子尾部】插入一份第一张图片;

当播放到【最后一张图】(数据最后一张),要跳转到第一张图时,执行动画操作跳转到我们插入到【盒子尾部】的【第一张图片的副本】

此时再播放下一张时,先无动画跳转到【第一张图片】,再执行动画操作跳转到【第二张图片】

 

使用react hooks定义需要用到的参数

useState进行定义

//图片数据
    const [data, setData] = useState(props.data);
    //图片尺寸
    const [size, setSize] = useState(props.size);
    //图片宽度
    const [width, setWidth] = useState(size.width > 0 ? size.width : 1200);
    //右下角导航按钮当前选项
    const [current, setCurrent] = useState(0);
    const [btn, setBtn] = useState(0);
    //自动播放计时器
    const [timer, setTimer] = useState();

 

useEffect中进行初始化

useEffect(() => {
        //获取单张图片宽度
        const wid = size.width > 0 ? size.width : 1200;
        setWidth(wid);
        //设置图片盒子宽度
        let pics = document.getElementById('picContent');
        pics.style.width = (data.length + 1) * width + 'px';
        // 将第一张图片 clone 到最后
        let firstLi = pics.firstChild.cloneNode(true);
        pics.appendChild(firstLi);
        //设置自动播放
        autoPlay();
    }, [])

 

下一页

    const next = () => {
        let pics = document.getElementById('picContent');
        let len = pics.children.length - 1;
        let ind = current;
        //无动画,从尾部跳转到第一张图片
        if (ind >= len) {
            ind = 0;
            pics.style.left = 0;
        }
        ind++;
        //跳转动画
        animate(pics, -width * ind);
        //更新导航按钮
        setCurrent(ind);
        ind >= len ? setBtn(0) : setBtn(ind);
        // console.log("next is: ", ind)
    }

 

底部导航按钮跳转

   const jump = (ind) => {
        let pics = document.getElementById('picContent');
        animate(pics, -width * ind);
        setCurrent(ind);
        setBtn(ind);
    }

 

动画效果

//动画对象,结束帧位置(目标位置)
    const animate = (obj, target) => {
        clearInterval(obj.timer);
        obj.timer = setInterval(() => {
            var leader = obj.offsetLeft;
            var step = 30;
            //设置不同动画方向
            step = leader < target ? step : -step;
            if (Math.abs(leader - target) >= Math.abs(step)) {
                leader += step;
                obj.style.left = leader + 'px';
            } else {
                obj.style.left = target + 'px';
                clearInterval(obj.timer);
            }
        }, 10)
    }

 

自动播放

react hooks与setInterval

在react hooks中直接使用setInterval无法达到预期的效果,需要使用useReducer

(具体缘由参考该文:https://www.cnblogs.com/qcloud1001/p/10408634.html)

//设置自动播放
    const autoPlay = () => {
        setTimer(setInterval(() => {
            dispatch('inc');
        }, props.delay));
    }
    //取消自动播放
    const stopPlay = () => {
        clearInterval(timer);
        setTimer(null);
    }
    const [count, dispatch] = useReducer((state, action) => {
        if (action === 'inc') {
            next();
        }
    }, 0);

  

  

参考资料:

https://v2.umijs.org/zh/guide/create-umi-app.html#%E5%88%9B%E5%BB%BA-umi-%E9%A1%B9%E7%9B%AE

https://zhuanlan.zhihu.com/p/138232728

https://www.cnblogs.com/qcloud1001/p/10408634.html

 

原文地址:https://www.cnblogs.com/leftstan/p/15208856.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom