做了一个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; // 在动画结束后将状态变为未启动动画
});
}
});
你问我为什么会有这样的现象,我只能告诉你,这是js的游戏规则。
就好比打篮球,你上篮被打手了就会得到罚球一样,这是打篮球的规则;
这个规则也不是无中生有的,是合乎逻辑的。
你上篮上的好好的,别人打你的手,裁判判你一次罚球的机会,这合情合理吧。
回到js的游戏中:
母函数创建了一个闭包函数,闭包函数引用(也可以说成是绑定、绑架)了母函数内的变量,那么闭包函数每次执行的时候都需要引用母函数内的变量,这时候,js规定母函数的执行上下文不销毁,时刻准备给闭包函数创建执行上下文,这合情合理吧。
所以你要记住js的这条名为闭包的规则。
只需记住,闭包母函数的上下文永远不会被销毁,存在于内存当中。
让我们再次重温一次闭包的官方定义,是不是一下子觉得官方闭包的定义从未有过的贴切和准确:
所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分