网页轮播图练习(案例)

轮播图也称为焦点图,是网页中比较常见的网页特效。

功能需求:

  1. 鼠标经过轮播图时,显示左右按钮,鼠标离开则隐藏;
  2. 点击右侧按钮一次,图片往左播放一张,依次类推,左侧按钮同理;
  3. 图片播放的同时,下面的小圆圈模块随之一起变化;
  4. 点击小圆圈,可以播放相应的图片;
  5. 鼠标不经过轮播图,轮播图也会自动播放图片;
  6. 鼠标放到轮播图上,自动播放就停止。

案例分析:

① 因为 JS 较多,先单独新建 JS 文件夹,再新建 JS 文件,引入页面中。

② 此时需要添加 load 事件。

鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。

④ 显示隐藏 display。

// 1.获取元素
var prev = document.querySelector('.prev');
var next = document.querySelector('.next');
var focus = document.querySelector('.focus');
// 2. 鼠标经过focus 就显示隐藏左右按钮
focus.addEventListener('mouseenter', function () {
        prev.style.display = 'block';
        next.style.display = 'block';
    })
focus.addEventListener('mouseleave', function () {
        prev.style.display = 'none';
        next.style.display = 'none';
    })

考虑到小圆圈是对应图片数量变化的,每增加一张图片又手动改代码会很麻烦,下面分析一下如何动态生成小圆圈。

动态生成小圆圈:

① 核心思路:小圆圈的个数要跟图片张数一致;

② 所以首先先得到 ul 里面图片的张数(图片放入 li 里面,所以就是 li 的个数);

扫描二维码关注公众号,回复: 15623025 查看本文章

③ 利用循环动态生成小圆圈(这个小圆圈要放入 ol 里面);

④ 创建节点 createElement(‘li’)

⑤ 插入节点 ol. appendChild(li)

⑥ 第一个小圆圈需要添加 current 类 。

点击小圆圈,发现点到哪个小圆圈就会变色,其他小圆圈不变色,此处联想到排他思想,因此,

小圆圈的排他思想:

① 点击当前小圆圈,就添加current类 ;

② 其余的小圆圈就移除这个current类 ;

③ 注意: 我们在刚才生成小圆圈的同时,就可以直接绑定这个点击事件了。   

// 3.动态生成小圆圈  有几张图片,就生成几张小圆圈
    var ul = focus.querySelector('ul');
    var ol = focus.querySelector('.circle');
    for (var i = 0; i < ul.children.length; i++) {
        // 创建一个小 li
        var li = document.createElement('li');
        // 记录当前小圆圈的索引号 通过自定义属性来做
        li.setAttribute('index', i);
        // 把小 li 插入到 ol 里面
        ol.appendChild(li);
        // 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click', function () {
            // 干掉所有人 把所有的小 li 清除 current 类名
            for (var i = 0; i < ol.children.length; i++) {
                ol.children[i].className = '';
            }
            // 留下我自己 当前的小 li 设置 current 类名
            this.className = 'current';

接下来,就是点击小圆圈实现滚动图片的效果,此时用到 animate 动画函数:

① 引入 animate 动画函数的 js文件(注意,因为 index.js 依赖 animate.js 所以,animate.js 要写到 index.js 上面)

<!-- 引入JS文件 -->
    <!-- 这个animate.js必须写在 index.js的上面引入 -->
    <script src="JS/animate.js"></script>
    <script src="JS/index.js"></script>

② 使用动画函数的前提,该元素必须有定位;

③ 注意是 ul 移动而不是小 li ;

④ 滚动图片的核心算法: 点击某个小圆圈 , 就让图片滚动 小圆圈的索引号乘以图片的宽度做为ul 移动距离;

⑤ 此时需要知道小圆圈的索引号, 我们可以在生成小圆圈的时候,给它设置一个自定义属性,点击的时候获取这个自定 义属性即可。

// 留下我自己 当前的小 li 设置 current 类名
            this.className = 'current';
// 5. 点击小圆圈,移动图片,当然移动的是 ul
// ul 的移动距离 = 小圆圈的索引号 乘以 图片的宽度 注意是负值
// 当我们点击了某个小 li 就拿到当前小 li 的索引号
 var index = this.getAttribute('index');
 animate(ul, -index * focusWidth);

同理,接下来要实现点击左右按钮也能滚动图片的效果,点击右侧按钮一次,就让图片向左滚动一张,方法如下:

① 声明一个变量 num, 点击一次,自增1, 让这个变量乘以图片宽度,就是 ul 的滚动距离 ;

② 图片无缝滚动原理 ;

③ 把 ul 第一个 li 复制一份,放到 ul 的最后面 ;

④ 当图片滚动到克隆的最后一张图片时, 让 ul 快速的、不做动画的跳到最左侧: left 为 0 ;

⑤ 同时 num 赋值为 0,可以从新开始滚动图片了 。

但要是手动在HTML和CSS复制图片(改结构)会显得麻烦,设想通过修改JS实现自动克隆最后一张图片的效果,于是:

① 克隆 ul 第一个 li cloneNode() 括号里加 true 深克隆 复制里面的子节点 ,加 false 就是浅克隆,不复制节点 ;

② 添加到 ul 最后面 appendChild

接下来就是实现小圆圈跟随按钮一起变化:

点击按钮,小圆圈跟随变化

① 最简单的做法是再声明一个变量 circle,每次点击自增1,注意,左侧按钮也需要这个变量,因此要声明全局变量;

② 但是图片有7张(最后一张是复制的),小圆圈只有6个少一个,必须加一个判断条件;

③ 如果 circle == 6 就 重新复原为 0 。

考虑到图片数量是动态变化(如果想增加或减少图片),因此 判断 circle == ol. children.length 即可(即判断是否等于小圆圈的数量)

 // 把 ol 里面的第一个小 li 设置类名为 current
    ol.children[0].className = 'current';
 // 6. 克隆第一张图片(li)放到最后面
    var first = ul.children[0].cloneNode(true);
    ul.appendChild(first);
 // 7. 点击右侧按钮,图片滚动一张
    var num = 0; // num 是全局变量
 // 声明一个变量 circle 控制小圆圈的播放
    var circle = 0;
// 右侧按钮
    next.addEventListener('click', function () {
        // 如果走到了最后复制的一张图片,此时 ul 要快速复原 left 改为 0
        if (num == ul.children.length - 1) {
            ul.style.left = 0;
            num = 0;
        }
        num++;
        animate(ul, -num * focusWidth);
        // 8. 点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放
        circle++;
        // 如果circle == 6 说明走到最后克隆的这张图片 我们就复原为0
        if (circle == ol.children.length) {
            circle = 0;
        }
        // 先清除其余小圆圈的 current 类名
        for (var i = 0; i < ol.children.length; i++) {
            ol.children[i].className = '';
        }
        // 留下当前的小圆圈的current类名
        ol.children[circle].className = 'current';
    })

做到这里,基本实现了点击按钮图片和小圆圈同步跟随变化了,但存在一个小 BUG,就是点击小圆圈后到某张图片,点击按钮应该会跳到下一张图片,但结果是会跳到这张图片的上一张图片,这个问题是因为点击按钮的动画是通过 num 变量控制的,与 小圆圈播放图片是 li 点击事件 里边index 的索引号控制的,因此要让索引号等于num 即可,为了让小圆圈、按钮、图片三者实现一个同步的变化,就必须让三者值相等,即获取了小圆圈的索引号后直接让三者相等(此处  num 和circle  是全局变量,在 li 点击事件在点击事件外已声明)

var index = this.getAttribute('index');
// 当我们点击了某个小 li 就要把这个 li 的索引号给 num 和 circle
   num = circle = index;

左侧按钮

点击左侧按钮,由于 num 赋值为0,则设置前提条件,跳转到复制第一张图的最后一张图片,其索引号是 ul.children.length - 1 ,则 ul 移动的距离就可以算出来:

prev.addEventListener('click', function () {
        if (num == 0) {
            num = ul.children.length - 1;
            ul.style.left = -num * focusWidth + 'px';
        }
        num--;
        animate(ul, -num * focusWidth);
    })

同理,因为一开始赋值 circle 为 0,那 circle-- 就是负值,利用这个判断条件,当 circle < 0 时,就要将小圆圈改为最后一个小圆圈,即 ol. children.length - 1 , 而小圆圈的样式变化,即类名变化,因为和右侧按钮的代码是一样的,可以将其封装在一个函数内,进而优化代码

circle--;
        // 如果circle < 0 说明第一张图片 则小圆圈要改为第6个小圆圈
        if (circle < 0) {
            circle = ol.children.length - 1;
        }
        // 调用函数
        circleChange();
// 优化代码,封装函数
    function circleChange() {
        for (var i = 0; i < ol.children.length; i++) {
            ol.children[i].className = '';
        }
        // 留下当前的小圆圈的current类名
        ol.children[circle].className = 'current';
    }

到此为止,基本效果已经实现。

接下来就是实现动画自动播放的效果:

自动播放可以联想到定时器,则

① 添加一个定时器 ;

② 自动播放轮播图,实际就类似于点击了右侧按钮 ;

③ 此时我们使用手动调用右侧按钮点击事件 next.click() ;

④ 鼠标经过focus 就停止定时器 ;

⑤ 鼠标离开focus 就开启定时器 。

于是,定时器的函数可以这样写

// 10. 自动播放轮播图
    var timer = setInterval(function () {
        // 手动调用右侧按钮点击事件
        next.click();
    }, 2000)

鼠标经过停止定时器(清除定时器)和离开(把定时器的代码直接拉上去,不需要定义)的效果要回到之前写的代码里去修改,增加几行代码,如下:

// 2. 鼠标经过focus 就显示隐藏左右按钮以及停止和开启定时器
    focus.addEventListener('mouseenter', function () {
        prev.style.display = 'block';
        next.style.display = 'block';
        clearInterval(timer);
        timer = null;// 清除定时器变量
    })
    focus.addEventListener('mouseleave', function () {
        prev.style.display = 'none';
        next.style.display = 'none';
        timer = setInterval(function () {
            // 手动调用右侧按钮点击事件
            next.click();
        }, 2000)
    })

最后,轮播图还涉及到一个问题,就是点击按钮播放图片会过快,由此引入一个概念——节流阀

节流阀是为了防止轮播图按钮连续点击造成播放过快。

节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。

核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。

开始设置一个变量

var flag = true;

If (flag)

{flag = false; do something} 关闭水龙头

利用回调函数 动画执行完毕, flag = true 打开水龙头

因此,在点击事件里,先执行了一遍代码,但由于 flag = false,所以下面就不会去执行代码了,而在 animate 动画函数里通过回调函数重新开启 flag = true 即可

if (flag) {
            flag = false;//关闭节流阀
            if (num == ul.children.length - 1) {
                ul.style.left = 0 + 'px';
                num = 0;
            }
            num++;
            animate(ul, -num * focusWidth, function () {
                flag = true;//打开节流阀
            });
            // 8. 点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放
            circle++;
            // 如果circle == 6 说明走到最后克隆的这张图片 我们就复原为0
            if (circle == ol.children.length) {
                circle = 0;
            }
            //调用函数
            circleChange();
        }

由此,网页上的轮播图效果基本完善,下面看看效果吧

有没有抖音里的图文视频的感觉呀,那个也是轮播图哦。

 PS:完整的JavaScript代码如下:

window.addEventListener('load', function () {
    // 1. 获取元素
    var prev = document.querySelector('.prev');
    var next = document.querySelector('.next');
    var focus = document.querySelector('.focus');
    var focusWidth = focus.offsetWidth;// 获取图片的宽度
    // 2. 鼠标经过focus 就显示隐藏左右按钮
    focus.addEventListener('mouseenter', function () {
        prev.style.display = 'block';
        next.style.display = 'block';
        clearInterval(timer);
        timer = null;// 清除定时器变量
    })
    focus.addEventListener('mouseleave', function () {
        prev.style.display = 'none';
        next.style.display = 'none';
        timer = setInterval(function () {
            // 手动调用右侧按钮点击事件
            next.click();
        }, 2000)
    })
    // 3.动态生成小圆圈  有几张图片,就生成几张小圆圈
    var ul = focus.querySelector('ul');
    var ol = focus.querySelector('.circle');
    // console.log(ul.children.length);
    for (var i = 0; i < ul.children.length; i++) {
        // 创建一个小 li
        var li = document.createElement('li');
        // 记录当前小圆圈的索引号 通过自定义属性来做
        li.setAttribute('index', i);
        // 把小 li 插入到 ol 里面
        ol.appendChild(li);
        // 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click', function () {
            // 干掉所有人 把所有的小 li 清除 current 类名
            for (var i = 0; i < ol.children.length; i++) {
                ol.children[i].className = '';
            }
            // 留下我自己 当前的小 li 设置 current 类名
            this.className = 'current';
            // 5. 点击小圆圈,移动图片,当然移动的是 ul
            // ul 的移动距离 = 小圆圈的索引号 乘以 图片的宽度 注意是负值
            // 当我们点击了某个小 li 就拿到当前小 li 的索引号
            var index = this.getAttribute('index');
            // 当我们点击了某个小 li 就要把这个 li 的索引号给 num 和 circle
            num = circle = index;
            // console.log(focusWidth);
            animate(ul, -index * focusWidth);
        })
    }
    // 把 ol 里面的第一个小 li 设置类名为 current
    ol.children[0].className = 'current';
    // 6. 克隆第一张图片(li)放到最后面
    var first = ul.children[0].cloneNode(true);
    ul.appendChild(first);
    // 7. 点击右侧按钮,图片滚动一张
    var num = 0;
    // 声明一个变量 circle 控制小圆圈的播放
    var circle = 0;
    // flag 节流阀
    var flag = true;
    next.addEventListener('click', function () {
        // 如果走到了最后复制的一张图片,此时 ul 要快速复原 left 改为 0
        if (flag) {
            flag = false;//关闭节流阀
            if (num == ul.children.length - 1) {
                ul.style.left = 0 + 'px';
                num = 0;
            }
            num++;
            animate(ul, -num * focusWidth, function () {
                flag = true;//打开节流阀
            });
            // 8. 点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放
            circle++;
            // 如果circle == 6 说明走到最后克隆的这张图片 我们就复原为0
            if (circle == ol.children.length) {
                circle = 0;
            }
            //调用函数
            circleChange();
        }
    })

    // 9. 左侧按钮做法
    prev.addEventListener('click', function () {
        if (flag) {
            if (num == 0) {
                num = ul.children.length - 1;
                ul.style.left = -num * focusWidth + 'px';
            }
            num--;
            animate(ul, -num * focusWidth, function () {
                flag = true;
            });
            circle--;
            // 如果circle < 0 说明第一张图片 则小圆圈要改为第6个小圆圈
            if (circle < 0) {
                circle = ol.children.length - 1;
            }
            // 调用函数
            circleChange();
        }
    })

    // 优化代码,封装函数
    function circleChange() {
        for (var i = 0; i < ol.children.length; i++) {
            ol.children[i].className = '';
        }
        // 留下当前的小圆圈的current类名
        ol.children[circle].className = 'current';
    }

    // 10. 自动播放轮播图
    var timer = setInterval(function () {
        // 手动调用右侧按钮点击事件
        next.click();
    }, 2000)
})

★★★ 欢迎批评指正!

猜你喜欢

转载自blog.csdn.net/weixin_44566194/article/details/126953789