最近在做项目时, 需要进行两个组件联动, 一个轮询获取到消息, 然后将其传递给另外一个组件进行横向滚动展示, 结果滚动的速度越来越快。这里记录一下来提醒自己。消息滚动的代码在最下面, 方便下次使用。
问题背景: 最近在做一个需求, 组件 A 获取消息采用的是轮询, 组件 A 获取到新的消息后, 将组件 A 中的消息传递给另外一个组件 B, 当组件 B 接收到消息时就让消息在页面上滚动播放。
实现思路 : 这个项目应用的框架为 VUE, 当组件 A 获取到新的消息之后, 就触发中央事件总线, 在组件 B 中进行事件监听, 将其添加进入一个数组, 当判断定时器没有运动时, 就触发滚动的函数。消息滚动的函数是从消息数组中提取出第一条, 然后利用定时器进行消息滚动, 当消息滚动到边缘时清除定时器。
问题: 消息在滚动的过程中, 该开始还能够按照给定的速度进行滚动, 可是当时间变长后就会出现消息滚动的速度越来越快的问题。
原因 : 当出现这个问题时, 我第一个念头就是 setInterval 没有被清掉, 因为当定时器没有清掉之后又再次调用定时器就会导致多个定时器同时执行, 比如第一次是一个计时器, 再点一下是就是两个定时器, 这时候每次就是 +2, 所以速度不断提升。我看了一下我设置的滚动函数, 里面当消息滚动到边缘时, 就清除这个定时器, 所以在滚动函数中没有问题。我又看了下中央事件总线的事件监听器, 发现问题在这里。因为我在判断一个定时器是否被销毁时, 直接判断其类型是 数字 还是 null, 由于当定时器开始运行时, 每一次返回的都是一个 ID(数字), 而不是一开始的对象, 导致当一条消息开始滚动时, 又接收到一条新的消息, 然后就使得两个定时器同时运行, 从而出现这个问题。
解决方式 : 当消息滚动到盒子边缘销毁定时器时, 将其赋值为 null,然后修改中央事件的事件监听, 将其判断没有定时器的条件修改为 null, 然后满足条件的调用消息滚动函数。
横向滚动的代码 :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#title {
position: relative;
width: 10%;
margin: 30px auto;
line-height: 30px;
height: 30px;
border: 1px solid red;
overflow: hidden;
}
#content {
position: absolute;
left: 0;
line-height: 30px;
display: inline-block;
}
</style>
</head>
<body>
<div id="title">
<span id="content">123</span>
</div>
<script>
var wrapEle = document.getElementById('title');
let contentEle = document.getElementById('content');
let arr = [{news: '这是一条新闻'}
];
let timer = null;
move(wrapEle, contentEle);
function move(wrap, item) {clearInterval(timer);
if (!arr.length) {return false;}
item.innerHTML = arr[0].news;
arr.splice(0, 1);
let allWidth = getCurrentStyle(wrap, 'width');
let itemWidth = getCurrentStyle(item, 'width');
item.style.left = allWidth + 'px';
let speed = 2;
let time = 50;
timer = setInterval(() => {let itemPlace = getCurrentStyle(item, 'left');
if (itemPlace < -itemWidth) {clearInterval(timer);
}
item.style.left = itemPlace - speed + 'px';
}, time)
}
function getCurrentStyle (ele, attr) {return window.getComputedStyle ? parseInt(window.getComputedStyle(ele, null)[attr]) : parseInt(ele.currentStyle[attr] );
}
</script>
</body>
</html>