js添加鼠标经过离开动画闪烁问题

做了一个js动画,模拟动态导航栏,当鼠标经过/离开的时候,一个动画会跟随到当前点击的li处,但是为何我的代码会存在一直闪烁的问题;

以下是源代码:


<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="动画函数封装.js"></script>



    <style>
        * {
            margin: 0;
            padding: 0;
        }


        .nav {
            position: relative;
        }

        .ul {
            width: 800px;
            height: 30px;
            background-color: beige;
        }



        .ul li {
            float: left;
            list-style: none;
            width: 100px;
            height: 28px;
            line-height: 30px;
            text-align: center;
            margin: 0 10px;
            padding-left: 2px;
            padding-right: 2px;
            border: 1px solid skyblue;
        }

        .cloud {
            position: absolute;
            top: 0;
            left: 0;
            background: rgba(0, 0, 255, 0.3);
            width: 103px;
            height: 28px;


        }
    </style>






</head>

<body>


    <div class="nav">
        <span class="cloud"></span>
        <!-- // 要动的盒子加绝对定位 -->

        <ul class="ul">
            <li>天猫</li>
            <li>聚划算</li>
            <li>天猫超市</li>
            <li>司法拍卖</li>
            <li>飞猪旅行</li>
            <li>天天特卖</li>
        </ul>


    </div>

    <script>
        // 鼠标经过 云就在当前的li
        // 鼠标离开,云就离开当前的li,跑到初试位置
        // 鼠标点击,云就在当前的位置;

        var lis = document.querySelector('.ul').querySelectorAll('li')  // 获取所有的li
        var cloud = document.querySelector('.cloud');

        for (var i = 0; i < lis.length; i++) {
            lis[i].addEventListener('mouseenter', function () {

                if (cloud.timer) {
                    clearInterval(cloud.timer);
                }

                animate(cloud, this.offsetLeft)

            });




            lis[i].addEventListener('mouseleave', function () {


                if (cloud.timer) {
                    clearInterval(cloud.timer);
                }

                animate(cloud, 0)

            })

        }

    </script>

</body>

</html>

以下是动画函数:


function animate(obj, target, callback) {
    // console.log(callback);  callback = function() {}  调用的时候 callback()

    // 先清除以前的定时器,只保留当前的一个定时器执行
    clearInterval(obj.timer);
    obj.timer = setInterval(function () {
        // 步长值写到定时器的里面
        // 把我们步长值改为整数 不要出现小数的问题
        // var step = Math.ceil((target - obj.offsetLeft) / 10);
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);


        if (obj.offsetLeft == target) {
            // 停止动画 本质是停止定时器
            clearInterval(obj.timer);
            // 回调函数写到定时器结束里面
            // if (callback) {
            //     // 调用函数
            //     callback();
            // }
            callback && callback();
        }
        // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';

    }, 15);
}

事件设置有点问题吧。。。记录下状态,当移动到你鼠标的地方后,记录当前鼠标指向对象,如果对象相同,则不进行动画

否则,你的云移动过来后,再次移动,触发了 mouseout,然后云离开鼠标后,又触发 mouseenter 了

不明白你写 离开事件的意义是什么 , 删除下部分代码


            lis[i].addEventListener('mouseleave', function () {
 
 
                if (cloud.timer) {
                    clearInterval(cloud.timer);
                }
 
                animate(cloud, 0)
 
            })

加一个变量控制一下


var isAnimating = false;  // 初始状态为未启动动画

lis[i].addEventListener('mouseenter', function () {

    if (!isAnimating) {  // 如果动画未启动,则启动新的动画
        isAnimating = true;  // 将状态变为已启动动画
        animate(cloud, this.offsetLeft, function() {
            isAnimating = false;  // 在动画结束后将状态变为未启动动画
        });
    }

});

lis[i].addEventListener('mouseleave', function () {

    if (!isAnimating) {  // 如果动画未启动,则启动新的动画
        isAnimating = true;  // 将状态变为已启动动画
        animate(cloud, 0, function() {
            isAnimating = false;  // 在动画结束后将状态变为未启动动画
        });
    }

});
  • 帮你找了个相似的问题, 你可以看下: https://ask.csdn.net/questions/7589540
  • 你也可以参考下这篇文章:js数组排序,根据数组中对象的某一属性进行排序 支持数字和字符串的排序,也支持按两个属性进行排序
  • 除此之外, 这篇博客: 关于js 闭包的理解及特点中的 闭包函数绑架了母函数的上下文变量,从而导致了母函数的上下文不会被销毁。 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 你问我为什么会有这样的现象,我只能告诉你,这是js的游戏规则。
    就好比打篮球,你上篮被打手了就会得到罚球一样,这是打篮球的规则;
    这个规则也不是无中生有的,是合乎逻辑的。
    你上篮上的好好的,别人打你的手,裁判判你一次罚球的机会,这合情合理吧。
    回到js的游戏中:
    母函数创建了一个闭包函数,闭包函数引用(也可以说成是绑定、绑架)了母函数内的变量,那么闭包函数每次执行的时候都需要引用母函数内的变量,这时候,js规定母函数的执行上下文不销毁,时刻准备给闭包函数创建执行上下文,这合情合理吧。
    所以你要记住js的这条名为闭包的规则。
    只需记住,闭包母函数的上下文永远不会被销毁,存在于内存当中。
    让我们再次重温一次闭包的官方定义,是不是一下子觉得官方闭包的定义从未有过的贴切和准确:

    所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分

  • 您还可以看一下 何韬老师的简单粗暴学习数据结构与算法——JS版(一)课程中的 课程简介小节, 巩固相关知识点