我正在学习用JavaScript编写代码。我用一些定时鼠标动画编程。我正要添加一些绘制鼠标路径的代码。
这将是一个需要mousemove事件的东西,每次鼠标移动时都会在Canvas上绘制一个新的线路径。随着时间的推移,这条路径将变得更加透明,直到它消失为止。当然,新的路径总是不透明的,所以有一个连续的运动。
我想出了一种方法,我只需要requestanimationframe即可。基本上每次发生新的mousemove事件时,我都会将鼠标路径坐标添加到名为mousePathArray的对象数组中。该对象将携带路径坐标和“animationStage”计数器。此计数器将基本确定路径在“动画”的该阶段的透明度。 (后期阶段意味着更透明。)
然后每个动画帧我将调用一个函数,该函数将遍历数组中的所有对象并根据其坐标和animationStage计数器绘制线条,将计数器增加1并且如果animationStage计数器将删除数组对象到达结束号码(可能是50或者其他)。
这一切都可以完成,但听起来不是全部,只需引入一个setInterval函数就可以轻松完成,每次鼠标移动时都会调用setInterval函数。
所以值得做长远的事吗?是不是更快或者只是更好的JS练习不一起使用setInterval和rAF?
写下这一切之后,我实际上写了上面讨论过的仅限rAF的代码。粘贴这里太长了,但规则需要它。这是jsfiddle:http://jsfiddle.net/06f7zefn/2/
(我知道有很多低效率和可能很糟糕的编码练习,但是对我来说,我已经5天了!我可以制作一个isDrawing?boolean而不是在每一帧上调用animate(),我可以做一次ctx.moveTo(),其余的是LineTo(),而不必每次迭代都移动To(),因为一个点来自另一个点离开的地方)
如果我能够理解我正在谈论的主要想法,那就是我要求你提出意见的地方。而不是让与时间相关的所有内容都来自rAF调用,而不是在这里使用setInterval或setTimeout更好吗?
var canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
var currentPosX, currentPosY, prevPosX, prevPosY;
var mousePathArray = [];
canvas.addEventListener ('mousemove', mouseOp);
function mouseOp (mouseEvent) {
prevPosX = currentPosX;
prevPosY = currentPosY;
currentPosX = mouseEvent.clientX;
currentPosY = mouseEvent.clientY;
mousePathArray.push( {
x1: currentPosX,
x2: prevPosX,
y1: currentPosY,
y2: prevPosY,
animStage: 0
});
}
function animate () {
var anims = mousePathArray.length;
if (anims!=0) {
for (i=0; i<anims; i++) {
if (mousePathArray[i].animStage == 20) {
mousePathArray.splice(i, 1);
i--;
anims--;
continue;
}
drawLine(mousePathArray[i].x1, mousePathArray[i].x2, mousePathArray[i].y1, mousePathArray[i].y2, 1 - (mousePathArray[i].animStage * 0.05));
mousePathArray[i].animStage ++;
}
}
}
function drawLine (x1, x2, y1, y2, alpha) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.strokeStyle = "rgba(150, 20, 150," + alpha +")";
ctx.stroke();
}
animloop();
function animloop(){
window.requestAnimationFrame(animloop);
gameLoop();
}
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
animate();
}
答案 0 :(得分:12)
我不认为使用setInterval或setTimeout是不好的做法。当你想在将来做某事时使用setTimeout是不好的做法,但是你不知道什么时候能够做到这一点。例如,这是不好的做法:
makeHeavyDomMovements();
setTimeout(function () {
//with 3000 timeout I'm sure any device has made my changes
makeNextMove();
}, 3000);
正确的方法是:
makeHeavyDomMovements().
then(function () {
makeNextMove();
});
如果你想在将来做一些事情,比如100分钟后响应用户操作,最好使用setTimeout,或者如果你想在浏览器队列中放置一些东西,你应该使用setTimeout(或者如果需要的话使用worker)。
它与setInterval相同,如果你习惯于每隔x毫秒做一些事情,那么你正确地使用它并且这是不错的做法,这里使用的是setInterval:
var dbInterval = setInterval(function () {
if (dbIsReady()) {
clearInterval(dbInterval);
fireReadyEvent();
}
}, 300);
以下是setInterval的常规用法:
setInterval(function () {
runSync();
}, 600000);
使用环境工具而不是那些工具本身的方式会影响不良做法和良好做法。
答案 1 :(得分:3)
Luz Caballero能够很好地描述为什么rAF是setInterval
的有用替代品:
https://dev.opera.com/articles/better-performance-with-requestanimationframe/。
至于我,我现在使用rAF而不是setInterval,因为rAF有一些内置的用处,需要使用setInterval进行额外的编码:
rAF将尝试将其调用与显示刷新周期同步。这使循环中的代码成为刷新周期之间完成的“最佳机会”。
如果循环#1中的任务在请求循环#2之前无法完成,则在完成不完整循环#1之前不会调用新循环#2。这意味着浏览器不会因累积的循环积压而过度工作。
如果用户切换到其他浏览器标签,则暂停当前标签中的循环。这允许处理能力转移到新选项卡,而不是在现在不可见的循环点击中使用。这也是电池供电设备的省电功能。如果您希望循环在选项卡未聚焦时继续处理,则必须使用setInterval。
rAF回调函数自动获取高精度时间戳参数。这允许rAF循环计算&amp;使用经过的时间。该经过的时间可以用于(1)延迟直到指定的经过时间已经发生,或者(2)“赶上”基于时间的期望但是不能完成的任务。
答案 2 :(得分:1)
问题不在于setTimeout(),而在于setInterval()可能会丢失一些执行并且还有其他缺点。所以总是使用setTimeout。您拥有以下所有详细信息:https://zetafleet.com/blog/2010/04/why-i-consider-setinterval-to-be-harmful.html
答案 3 :(得分:0)
如果您100%确信这是您需要的确切时间,则可以使用它们。如果情况不明确,则不要使用它。如果您使用的是JavaScript框架,请查找生命周期挂钩,例如在angular中使用Angular生命周期挂钩。