原生手写轮播图

1.图片自动轮播,对应的页码数高亮
(文件大小有限,以致于速度太快了😣😣)
在这里插入图片描述

2.点击页码可以跳转到对应的图片

3.鼠标进入盒子,显示左右箭头,暂停轮播;鼠标移出盒子,图片轮播继续

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style type="text/css">
        * {
            padding: 0;
            margin: 0;
            list-style: none;
            border: 0;
        }

        .all {
            width: 500px;
            height: 200px;
            padding: 7px;
            border: 1px solid #ccc;
            margin: 100px auto;
            position: relative;
        }

        .screen {
            width: 500px;
            height: 200px;
            overflow: hidden;
            position: relative;
        }

        .screen li {
            width: 500px;
            height: 200px;
            overflow: hidden;
            float: left;
        }

        .screen ul {
            position: absolute;
            left: 0;
            top: 0px;
            width: 3500px;
        }

        .all ol {
            position: absolute;
            right: 10px;
            bottom: 10px;
            line-height: 20px;
            text-align: center;
        }

        .all ol li {
            float: left;
            width: 20px;
            height: 20px;
            background: #fff;
            border: 1px solid #ccc;
            margin-left: 10px;
            cursor: pointer;
        }

        .all ol li.current {
            background: yellow;
        }

        #arr {
            display: none;
        }

        #arr span {
            width: 40px;
            height: 40px;
            position: absolute;
            left: 5px;
            top: 50%;
            margin-top: -20px;
            background: #000;
            cursor: pointer;
            line-height: 40px;
            text-align: center;
            font-weight: bold;
            font-family: '黑体';
            font-size: 30px;
            color: #fff;
            opacity: 0.3;
            border: 1px solid #fff;
        }

        #arr #right {
            right: 5px;
            left: auto;
        }
    </style>
    <script src="js/get.js"></script>
    <script src="js/animation.js"></script>
</head>

<body>
    <div class="all" id='box'>
        <div class="screen">
            <ul>
                <li><img src="images/laugh01.gif" width="500" height="200" /></li>
                <li><img src="images/laugh02.gif" width="500" height="200" /></li>
                <li><img src="images/laugh03.gif" width="500" height="200" /></li>
                <li><img src="images/laugh04.gif" width="500" height="200" /></li>
                <li><img src="images/laugh05.gif" width="500" height="200" /></li>
            </ul>
            <ol>
                <li class="current">1</li>
                <li>2</li>
                <li>3</li>
                <li>4</li>
                <li>5</li>
            </ol>

        </div>
        <div id="arr"><span id="left">&lt;</span><span id="right">&gt;</span></div>
    </div>

    <script>
        // 需求1:鼠标移入到显示图片的位置,就显示对应的箭头,鼠标移出就隐藏箭头
        /*
            思路分析

            1.获取事件源:div#box盒子
            2.事件类型:鼠标的移入onmouseover和移出onmouseout
            3.确定事件处理
            3.1 移入:显示div#arr display = block
            3.2 移出:隐藏div#arr display = none
        */

        // 1.获取事件源:div#box盒子
        let box = getId('box');
        let arr = getId('arr');

        // 自动轮播:向右 延时器处理
        // 定时器
        let timeId = setInterval(rightClick, 2000);

        // 2.事件类型:鼠标的移入onmouseover和移出onmouseout
        // 3.确定事件处理
        // 3.1 移入:显示div#arr display = block
        box.onmouseover = function () {
            arr.style.display = 'block';

            // 清除自动轮播(定时器)
            clearInterval(timeId);
        };
        // 3.2 移出:隐藏div#arr display = none
        box.onmouseout = function () {
            arr.style.display = 'none';

            // 加载自动轮播(定时器)
            timeId = setInterval(rightClick, 2000);
        };

        /*
            需求2:实现左右按钮的无缝轮播

            思路分析:
            1.特别思路,实现轮播图的无缝连接,会在原来的图片上Ul>li,第一张的前面增加一张最后的图片,还会在最后一张的后面增加第一张图片
            1.1 获取屏幕部分:get('.screen')
            1.2 获取屏幕下面的图片部分: 屏幕.querySelectorAll('ul>li')
            1.3 克隆两个:第一个li和最后一个li(深克隆)
            1.4 将克隆出来的第一个元素放到ul的最后,将克隆出来的最后一个元素放到ul的第一个li之前
            1.5 让图片正常显示:显示第一张,让ul瞬间移动到第一张

            2.获取事件源:两个 getId('left') getId('right')
            3.确定事件类型:点击事件 onclick
            4.事件处理
            * 定义一个观察者:index = 1 前面动态添加了一张最后的图片:占了0的位置
            4.1 right的点击事件 right.onlick = rightClick;
            4.1.1 index++:但是要注意 index 不能超过5
            4.1.2 根据index的值 * screen.offsetWidth 做动画效果
            4.2 left的点击事件 left.onclick = leftClick;
            4.2.1 index--:但是要注意 index 不能小于1
            4.2.2 根据index的值 * screen.offsetWidth 做动画效果

        */
        // 1.特别思路,实现轮播图的无缝连接,会在原来的图片上Ul>li,第一张的前面增加一张最后的图片,还会在最后一张的后面增加第一张图片
        let screen = get('.screen');

        // 获取所有的li
        let lis = screen.querySelectorAll('ul>li');
        let ols = screen.querySelectorAll('ol>li');
        // console.log(lis);
        // 克隆两个元素:第一个li和最后一个li
        let firstLi = lis[0].cloneNode(true);               // 深克隆
        let lastLi = lis[lis.length - 1].cloneNode(true);   // 深克隆
        // console.log(firstLi, lastLi);

        // 最后一张放第一个位置,第一张放最后一个位置
        lis[0].parentNode.insertBefore(lastLi, lis[0]);     // 在第一张之前插入最后一张
        lis[0].parentNode.appendChild(firstLi);


        // 1.5 让图片正常显示:显示第一张,让ul瞬间移动到第一张
        screen.firstElementChild.style.left = '-500px';

        // 2.获取事件源:两个 getId('left') getId('right')
        let left = getId('left');
        let right = getId('right');
        // 3.确定事件类型:点击事件 onclick
        // 4.事件处理
        //     * 定义一个观察者:index = 1 前面动态添加了一张最后的图片:占了0的位置
        let index = 1;
        // 4.1 right的点击事件 right.onlick = rightClick;
        right.onclick = rightClick;

        function rightClick() {
            // 4.1.1 index++:但是要注意 index 不能超过5
            if (index == 5) {
                // 说明:已经是最后一张,要看第一张
                index = 1;

                // 瞬移到第一张
                lis[0].parentElement.style.left = '0px';
            } else {
                index++;
            }

            // 4.1.2 根据index的值 * screen.offsetWidth 做动画效果
            animateMove(lis[0].parentElement, -index * screen.offsetWidth);

            // 4.1.3 让对应的页面高亮显示:当前页面正常显示,上一个页面应该不显示
            ols[index - 1].classList.add('current');
            // 如果当前是第一张图片:index = 1,是从最后一张过来的
            ols[index > 1 ? index - 2 : ols.length - 1].classList.remove('current');
        }

        // 4.2 left的点击事件 left.onclick = leftClick;
        left.onclick = leftClick;

        function leftClick() {
            // 4.2.1 index--:但是要注意 index 不能小于1

            // 如果index当前已经是1,第一张,用户在点击left,想看第5张
            // 1.将当前的效果瞬移到最后一张(就是第一张):障眼法,瞬移用户看不到效果
            if (index == 1) {
                lis[0].parentNode.style.left = '-3000px';
                // index重置为5
                index = 5;
            } else {
                index--;
            }

            // 4.2.2 根据index的值 * screen.offsetWidth 做动画效果
            animateMove(lis[0].parentElement, -index * screen.offsetWidth);

            // 4.2.3 根据index实现页面的高亮显示
            ols[index == 5 ? 0 : index].classList.remove('current');
            ols[index - 1].classList.add('current');
        }

        // 页码点击事件
        ols.forEach(function (item, key) {
            // item就是ol>li
            item.addEventListener('click', function () {
                // 页码变化效果:原始页码(删掉current)
                ols[index - 1].classList.remove('current');

                // 动画:当前index是key + 1
                index = key + 1;

                // 动画
                animateSlow(lis[0].parentElement, { left: -index * screen.offsetWidth });

                // 页码变化效果:当前图片对应的页码显示
                this.classList.add('current');
            });
        });




        // 总结
        // 轮播图:原生代码实现

        // 轮播图的实现如果要考虑无缝轮播,一般的思路是添加首尾两张图片
        // 第一张的前面放最后一张,最后一张的后面放第一张:用来瞬移,做障眼法,欺骗用户

    </script>
</body>

</html>

get.js

/* 当前文件封装获取元素的API */

/**
 * 通过ID获取元素
 * @param {string} id
 * @return {element} 获取到的元素 或者 null
 * */
function getId(id) {
    return document.getElementById(id);
}

/**
 * 根据选择器获取一个元素
 * @param {string} selector
 * @return {element} 获取到的元素 或者 null
 * 
*/
function get(selector) {
    return document.querySelector(selector);
}

/**
 * 根据选择器获取多个元素
 * @param {string} selector
 * @return {element} 获取到的伪数组
 * 
*/
function getAll(selector) {
    return document.querySelectorAll(selector);
}

animation.js

/**
 * 动画效果封装
 * @param {element} obj,要移动的元素
 * @param {number} target,目标位置,不带单位
 * @param {number} step = 10,每次移动的效果,默认为10px
 * @param {number} time = 20,每次间隔多久实现定时器的触发,默认20ms
 *
 * */
function animateMove(obj, target, step = 10, time = 20) {
    // 先清理定时器:防止用户多次触发
    // 基于对象.非标准属性,不会出现在元素中,只能在内存中存在:我们可以选择将元素自己的定时器存储到自己的属性当中
    // console.log(obj.timeId);
    clearInterval(obj.timeId);

    // 定时器处理
    obj.timeId = setInterval(function () {
        // 获取到元素的left的值:offset
        let currentLeft = obj.offsetLeft;

        // 求出下一个次的位置:有可能target已经比当前元素的位置currentLeft小
        // 判定目标与当前元素的位置的关系
        // console.log(currentLeft, target);

        // let声明的变量是块级作用域:如果是在if里面声明,if外面不能用:在外面声明,在里面赋值
        let next;
        // 添加一个开关:控制向左还是向右
        let toRight = true;         // 默认向右

        if (currentLeft < target) {
            // 元素在左,目标在右
            next = currentLeft + step;
        } else {
            // 元素在右,目标在左
            next = currentLeft - step;
            // 改变开关
            toRight = false;
        }
        // console.log(next);

        // 判定next是否超出了目标
        if (toRight) {
            // 动画是向右:判定当前的next应该小于等于target才移动
            if (next < target) {
                // 还没过线
                obj.style.left = next + 'px';
            } else {
                // 过线了
                obj.style.left = target + 'px';
                // 已经到了
                clearInterval(obj.timeId);
            }
        } else {
            // 动画是向左:判定当前的next应该大于等于target才移动
            if (next >= target) {
                // 没有过线
                obj.style.left = next + 'px';
            } else {
                // 过线了
                obj.style.left = target + 'px';
                // 到达终点
                clearInterval(obj.timeId);
            }
        }

    }, time);
}


function animateSlow(obj, target, step = 0.1, time = 20, fn = '') {
    // 防止用户多次操作
    clearInterval(obj.timeId);

    // 开始定时器
    obj.timeId = setInterval(function () {
        // 增加一个开关
        let isAllOk = true;     // 认定所有的属性,都已经到位了

        // 获取当前用户需要的样式属性,然后进行相应的数据改变
        // 传入的target是一个对象,样式的属性名字 是对象的属性名,要拿到只能遍历
        for (let key in target) {
            // key 就是属性名 ,target[key]就是值
            // console.log(key, target[key]);

            // 以前获取方式:固定做左右,利用offsetLeft拿到位置,现在属性为left
            // 以前是不能使用obj[key],因为obj.style.语法只能获取行内
            // console.log(obj.style[key]);          // 等价于 obj.style.left,以前

            // 现在有办法拿到行外样式:getComputedStyle(元素对象)[key]
            // console.log(getComputedStyle(obj)[key]);
            let current = getComputedStyle(obj, null)[key];     // 带单位:不能参与数学运算
            current = parseInt(current);
            // console.log(current);

            // 计算步长:移动的距离
            let steps = (target[key] - current) * step;
            // console.log(steps);

            // 上述steps有可能是负值:向左移动
            steps = steps > 0 ? Math.ceil(steps) : Math.floor(steps);

            // 一步完成
            // steps = target[key] > current ? Math.ceil((target[key] - current) * step) : Math.floor((target[key] - current) * step);
            // console.log(steps);

            // 下一个位置
            let next = current + steps;

            // 修改元素的样式:修改元素样式,只有一个方式 obj.style[key]
            obj.style[key] = next + 'px';



            // 判定
            // if (next == target[key]) {
            //     // 到头了
            //     clearInterval(obj.timeId);
            // }

            // 判定:判定开关
            if (next != target[key]) {
                // 没有到位:关闭开关
                isAllOk = false;
            }

            // console.log(key, target[key], next, isAllOk);

        }

        // 开关确定定时器是否结束
        if (isAllOk) {
            // 清空定时器
            clearInterval(obj.timeId);

            // 如果用户传入了回调函数,帮助用户调用:typeof可以判定出是否是函数
            if (typeof fn == 'function') {
                // 说明用户传入了一个函数:帮助用户调用
                fn();
            }

        }

        // 终止计时器
        // clearInterval(obj.timeId);
    }, time);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值