简体   繁体   English

为什么 setInterval() 在 Firefox 中表现不同

[英]Why does setInterval() behaves differently in Firefox

I had to animate the rotation using vanila JS and encountered the problem with Firefox. I used setInterval() to animate each animation step and then clearInteval() to stop the animation. In this example I animated the rotation of an element.我不得不使用 vanila JS 为旋转设置动画,并遇到了 Firefox 的问题。我使用setInterval()为每个 animation 步骤设置动画,然后使用clearInteval()停止 animation。在这个例子中,我为元素的旋转设置了动画。 It works fine in Chrome, but doesn't quite finish the animation in Firefox, as if Firefox takes longer to process each step.它在 Chrome 中工作正常,但并没有完全完成 Firefox 中的 animation,好像 Firefox 处理每个步骤需要更长的时间。 I created an example, demonstrating this behaviour.我创建了一个示例来演示这种行为。

 const circle = document.getElementById('circle') const angle = document.getElementById('angle') const degToMove = 90 //rotate 90 degrees const animStepLength = 10 // 10ms is one animation step const animLength = 300 //whole animation length -> 30 animation steps -> 3deg rotation per step const rotateCircle = () => { rotInterval = setInterval(()=>{ //what rotation value currently is let currentVal = circle.style.transform.match(/[-+]?\d+/); //add 3 deg rotation per step to it circle.style.transform = `rotate(${+currentVal[0] + (degToMove*animStepLength) / animLength}deg)` //text output angle.innerHTML = `${+currentVal[0] + (degToMove*animStepLength) / animLength} deg` }, animStepLength) setTimeout(() => { //after all steps are done clear the interval clearInterval(rotInterval) }, animLength); } circle.addEventListener('click', rotateCircle)
 body{ display: flex; align-items: center; justify-content: center; margin: 0; height: 100vh; font-family: sans-serif; } #circle{ border-radius: 50%; background-color: skyblue; width: 100px; height: 100px; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; }
 <div id="circle" style='transform: rotate(0deg)'> <span> Click to rotate </span> <span id="angle">0 deg</span> </div>

Also available as jsfiddle也可以作为jsfiddle

While Chrome rotates to 90 -> 180 -> 270 -> 360... Firefox goes to 57 -> 114 -> 171 -> 228 ->... in this particular example.当 Chrome 旋转到 90 -> 180 -> 270 -> 360... Firefox 转到 57 -> 114 -> 171 -> 228 ->... 在这个特定的例子中。 Well, this is basically +1 rad increase, but it has to do something with the selected values for animLength and animStepLength .好吧,这基本上是 +1 rad 增加,但它必须对animLengthanimStepLength的选定值做一些事情。 If I select them differently, Firefox shows different values.如果我 select 它们不同, Firefox 显示不同的值。

The simple CSS animation would work here but there are reasons for me to use JS here.简单的 CSS animation 可以在这里工作,但我有理由在这里使用 JS。

You can never guarantee that a setTimeout or setInterval handler will be called when you ask it to.您永远无法保证setTimeoutsetInterval处理程序会在您要求时被调用。 You should always compare the current time against the initial time to figure out what kind of progress your animation should show, usually by figuring out what percentage of the animation has elapsed.您应该始终将当前时间与初始时间进行比较,以确定您的 animation 应该显示什么样的进度,通常是通过计算 animation 已经过去的百分比。 Look for the elapsedPercentage variable in the example below.在下面的示例中查找elapsedPercentage变量。

Using setInterval is considered bad practice because of that reason.由于这个原因, 使用setInterval被认为是不好的做法 The suggested way to animate is to use nested requestAnimationFrame ;建议的动画方式是使用嵌套的requestAnimationFrame

The script below can use lots of improvements but it does show you how to properly update your animation based on how long it's been since the animation started.下面的脚本可以使用很多改进,但它确实向您展示了如何根据 animation 启动后的时间正确更新 animation。

 const circle = document.getElementById('circle'); const angle = document.getElementById('angle'); const degToMove = 90; //rotate 90 degrees let rotationStartTime = null; let targetRotation = 0; let rotationStart = 0 let animationTime = 3000; function startRotating() { rotationStartTime = new Date().getTime(); rotationStart = parseInt(circle.style.transform.match(/[-+]?\d+/)[0]); targetRotation += degToMove; rotateCircle(); } function rotateCircle() { const currentVal = parseInt(circle.style.transform.match(/[-+]?\d+/)[0]); const currentTime = new Date().getTime(); const elapsedPercentage = (currentTime - rotationStartTime) / animationTime; let newVal = Math.min(targetRotation, Math.round(rotationStart + (elapsedPercentage * degToMove))); circle.style.transform = `rotate(${newVal}deg)`; //text output angle.innerHTML = `${newVal} deg`; if (newVal < targetRotation) { window.requestAnimationFrame(rotateCircle); } } circle.addEventListener('click', startRotating)
 body{ display: flex; align-items: center; justify-content: center; margin: 0; height: 100vh; font-family: sans-serif; } #circle{ border-radius: 50%; background-color: skyblue; width: 100px; height: 100px; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; }
 <div id="circle" style='transform: rotate(0deg)'> <span> Click to rotate </span> <span id="angle">0 deg</span> </div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM