• 已经2023年了,你还不会手撕轮播图?


    jd.gif


    一、前言

    目前,在移动端或是网页端对于轮播图的需求并不可少,我们有许多代替好的框架或者组件实现这个功能,但不如我们今天自己着手定制一个原生js轮播图,顺便总结提高一下相关知识点。

    轮播图目前出现在各大购物网站的首页用来展示商品信息,现在也出了很多插件帮助我们更加便捷的实现多种多样的轮播图

    京东

    jd.gif

    天猫

    tm.webp

    淘宝

    tb.webp

    纯手写轮播图对于初学者可能很难,也会有公司面试出轮播图来考察面试者的基础能力了。其实轮播图只要细分成几个小的模块逐步实现起来还是比较简单的。

    下面,带大家来实现一下简易的轮播图

    二、动画基础

    我们都知道轮播图是有一个动画过程的,那如何封装实现这一个动画函数呢?

    1. 定时器

    前端的定时器有两种,一种是一次性定时器setTimeout,一种是重复性定时器setInterval

    1-定时器.gif

    如上图所示,setTimeout你只有点击一下按钮物体才会向前跑过了15ms就向前跑10px。而对于setInterval只需要点击一次便会每间隔15ms执行一次,页面中的倒计时效果也是这样做的。

    所以,我们的轮播图肯定要选择setInterval第二种方案了。

    2. left与offsetLeft

    left就是我们加了定位的物体距离左侧的位置,这里可以参考一些常见的定位属性。

    offsetLeft是一个只读属性(不能修改值),返回当前元素相对于 offsetParent 节点左边界的偏移像素值。当前父亲节点是整个页面,所以只需要把offsetLeft赋值给objectleft偏移量就行了。

    3. 封装函数

    有了定时器之后,我们就要考虑把这段代码封装成动画函数,想要的时候调用就行了。

    封装函数要注意参数问题,那么我们定时器要传进来什么参数呢?

    物体 目标点 回调函数

    3.1 物体

    物体为我们要移动的dom元素,就是上面哪个在屏幕行动的粉色盒子。

    3.2 目标点

    上面的盒子运动方向是有了,但是它一旦执行起来一个劲的向前冲,这也不行呀!

    所以,我们试着修改一下上面的代码,比如让它到达800px就清楚定时器让它停下来,否则继续向前运动。
    这时候我们只需要在定时器加了一个if else判断就行了。

    if(object.offsetLeft==500){
        clearInterval(timer);
    }
    else{
        object.style.left=object.offsetLeft+10+'px';
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2-target.gif

    目标点满足了,但是还有两个疑问?

    • 到达800px后,如何后退?
    • 如何改变物体的运动曲线?

    这两个问题只需要一个解决方案,只需要加一个step的变量,代表每一次移动的值代替固定的10px

    这个变量只需要改成下面的公式计算就行:

    var step = (target - obj.offsetLeft) / 10;
    
    • 1

    我们一开始的运动曲线是这样的,匀速状态:

    liner.png

    通过这样的公式既可以保证物体可以后退,又满足了先加速后减速的曲线运动

    easeinout.png

    这个公式如果仔细查看css的距离其实会有偏差的,比实际的目标点偏小,由于浮点数的计算问题,所以要使用公式,做近似处理。

    step = step > 0 ? Math.ceil(step) : Math.floor(step);
    
    • 1
    3.3 回调函数

    回调函数顾名思义当我调用这个定时器函数时,到达目标点了,定时器被清空了,就可以执行我传入的回调函数了。

    下面让到达800px的物体,进行变色效果。

    3-callback.gif

    当然,后面也可以根据自己的要求实现特点的效果。

    4.封装

    我们把上面写好的代码统一到一个animate.js的文件中,需要的时候引入就行了。

    function animate(obj, target, callback) {
        //排他原理
        clearInterval(obj.timer);
        obj.timer = setInterval(function () {
            //步长
            var step = (target - obj.offsetLeft) / 10;
            step = step > 0 ? Math.ceil(step) : Math.floor(step);
            if (obj.offsetLeft == target) {
                clearInterval(obj.timer);
                //回调函数
                callback && callback();
            }
            else {
                obj.style.left = obj.offsetLeft + step + 'px';
            }
        }, 15);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    为了优化代码,防止用户过度点击,我们要在定时器执行的一开始通过排他原理预先清空之前的定时器,然后再执行我们自己的定时器。

    三、基础结构

    1.png

    搭建html页面的结构其实很简单,我们主要把它分成三个部分,分别是中间的焦点图、左右两侧的按钮、底部的小圆点

    3.1 焦点图

    2.png

    <ul class="rotate-middle">
        <li><a href="#"><img src="images/1.jpg" alt="">a>li>
        <li><a href="#"><img src="images/2.jpg" alt="">a>li>
        <li><a href="#"><img src="images/3.jpg" alt="">a>li>
        <li><a href="#"><img src="images/4.jpg" alt="">a>li>
        <li><a href="#"><img src="images/5.jpg" alt="">a>li>
    ul>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    焦点图在底部先定义一个400*300的盒子,盒子里面放入一张张的图片。这时候要注意每一个li加上浮动ul盒子的大小也需要伸长到足够容纳这一行排列图片的大小。

    3.2 按钮

    <button class="btn-lt"><button>
    <button class="btn-rt">>button>
    
    • 1
    • 2

    按钮我没采用字体图标伪元素之类的,直接粗暴的使用普通的< >了,样式就合适的更改就行了。

    按钮额外要注意添加z-index提高位置,防止被图片压住了。

    3.3 小圆点

    <ol class="rotate-bottom">
        <li class="current">li>
        <li>li>
        <li>li>
        <li>li>
        <li>li>
    ol>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    底部小圆点现在我是直接在html里面写了,但是后面在js代码中会根据实际的图片数量实时更新。

    3.4 总结

    这三个基础的结构统一采用position定位(子绝父相),并且对于我们的ul也要加一个position: absolute;,因为后面要对这个盒子进行动画移动。

    四、按钮显示

    这部分我们的要求就是,让鼠标移动到图片的时候,按钮显示,离开不显示。正式写代码之前我们先来弄清楚一个问题?

    • mouseentermouseover有什么区别?

    推荐文章:mouseenter与mouseover为何这般纠缠不清?

    看完之后,选择mouseenter避免冒泡,获取时间源后,直接添加程序执行,按钮的隐藏还是显示直接使用display

    focus.addEventListener('mouseenter', function () {
            lt.style.display = 'block';
            rt.style.display = 'block';
        });
        focus.addEventListener('mouseleave', function () {
            lt.style.display = 'none';
            rt.style.display = 'none';
        });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    五、圆点

    5.1 生成

    for (var i = 0; i < ul.children.length; i++) {
        //创建ol li
        var cloneli = this.document.createElement('li');
        ol.appendChild(cloneli);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.png

    一开始,我们在html没给圆点,后面在js代码中根据图片的数量生成li

    5.2 属性

     //自定义属性
     ol.children[i].setAttribute('index', i);
    
    • 1
    • 2

    为了后面更好的移动轮播图,我们需要给每一个ol li定义一个属性index,这里我们是5张图片,index范围就是0~4

    5.3 移动

    //绑定事件
    ol.children[i].addEventListener('click', function () {
    // 排他原理
        for (var j = 0; j < ol.children.length; j++) {
            ol.children[j].className = '';
        }
        this.className = 'current';
    
        index = this.getAttribute('index');
        num = index;
        circle = index;
        animate(ul, -fixWidth * index);
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4-move.gif

    这时候,我们每点击一个圆点就获取它当前的index的值,然后调用动画函数传入目标值 ul 距离- 盒子的大小 * inddex,就可以移动图片了。

    六、按钮

    6.1 准备

    左右两侧按钮移动本质上是一致的,我们先来做右侧按钮。预期目标是我每点击一次右侧按钮,图片就会向后移动一张。

    那么这里我们还需要给每个图片也加上自定义属性吗?

    其实不用,自定义一个变量num,随着点击的次数++就行了。

    6.2 出错

    5-btn.gif

    如上面的图片一样,有几个问题:

    • 图片是动了,但是小圆点没动?
    • 到了最后一张图片,怎么返回?

    下面,我们一个个解决。

    6.2.1 小圆点跟随

    小圆点跟随简单,我们也定义一个变量circle,当按钮每点击一次,circle++,然后让对应的圆点填充颜色就行了。

    注意,这里和num我们要改善一下边界问题,到达最后一张图片是就不能继续++,要归零才行。

    if (circle == 4) {
        circle = 0;
    }
    else {
        circle++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    6.2.2 图片返回

    其实,当我们到达最后一张图片再点击是不连贯的,失去了平缓过度的效果。其实,我们可以在最后一张后面克隆第一张图片加上去。

    当图片向后移动时,会过渡到最后一张(也就是第一张的克隆版本),这个时候再点击我们就快速回到第一张图片就行了。

    //节点操作,复制照片
    var cloneimg = ul.children[0].cloneNode(true);
    ul.appendChild(cloneimg);
    
    • 1
    • 2
    • 3

    6.3 bug

    6-bug.gif

    如上图,我们点击圆点到达第三张图,然后点击右侧的按钮,没回到第四张图,为啥又退回了第二张图片。这主要是因为我们ol liindexnum cirle没同步导致的。

    如何改善,很简单,把index实时赋值给num cirle就可以改善了。

    左侧按钮,直接复制粘贴,改几个数值就行了,我不细讲了。

    七、定时器

    为了让轮播图按时移动,我们需要在最后加一个定时器效果,其实很简单,定时器内部直接调用右侧按钮的代码就行了。

    var timer = this.setInterval(function () {
            rt.click();
        }, 1000);
    
    
    • 1
    • 2
    • 3
    • 4

    7-rotate.gif

    每次间隔一秒,图片向右移动一张,鼠标接触,停止轮播图,鼠标移开,继续轮播图。

    八、总结

    本篇文章主要实现了一个简易的轮播图效果,下次再见!

    25.gif

  • 相关阅读:
    Redis持久化之AOF
    网络模型—BIO、NIO、IO多路复用、信号驱动IO、异步IO
    Java基础39 Object类(节选)
    后端的datetime类型数据转为string作为(vue)前端的时间YYYY-MM-DD HH:mm:ss
    3-10:统一记录日志
    java计算机毕业设计ssm智慧农贸信息化管理平台(源码+系统+mysql数据库+Lw文档)
    Python独具特色的语法规范点梳理
    Java多模块开发:探讨好处与弊端
    剑指 Offer 33. 二叉搜索树的后序遍历序列
    TypeScript中的泛型使用详解
  • 原文地址:https://blog.csdn.net/qq_53673551/article/details/128486591