为什么 setInterval() 在 Firefox 中表现不同
Why does setInterval() behaves differently in Firefox
我不得不使用 vanila JS 为旋转设置动画,并遇到了 Firefox 的问题。我使用 setInterval()
为每个动画步骤设置动画,然后使用 clearInteval()
停止动画。在这个例子中,我为一个元素的旋转设置了动画。它在 Chrome 中运行良好,但在 Firefox 中不能完全完成动画,好像 Firefox 需要更长的时间来处理每个步骤。我创建了一个示例来演示这种行为。
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>
也可用 jsfiddle
而 Chrome 旋转到 90 -> 180 -> 270 -> 360 ...
在此特定示例中,Firefox 转到 57 -> 114 -> 171 -> 228 -> ...。
嗯,这基本上是 +1 rad 增加,但它必须对 animLength
和 animStepLength
的 selected 值做一些事情。如果我 select 它们不同,Firefox 会显示不同的值。
简单的 CSS 动画在这里可以工作,但我有理由在这里使用 JS。
您永远无法保证 setTimeout
或 setInterval
处理程序会在您要求时被调用。您应该始终将当前时间与初始时间进行比较,以确定您的动画应该显示什么样的进度,通常是通过计算动画已经过去的百分比。在下面的示例中查找 elapsedPercentage
变量。
Using setInterval
is considered bad practice because of that reason. The suggested way to animate is to use nested requestAnimationFrame;
下面的脚本可以使用很多改进,但它确实向您展示了如何根据动画开始后的时长来正确更新动画。
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>
我不得不使用 vanila JS 为旋转设置动画,并遇到了 Firefox 的问题。我使用 setInterval()
为每个动画步骤设置动画,然后使用 clearInteval()
停止动画。在这个例子中,我为一个元素的旋转设置了动画。它在 Chrome 中运行良好,但在 Firefox 中不能完全完成动画,好像 Firefox 需要更长的时间来处理每个步骤。我创建了一个示例来演示这种行为。
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>
也可用 jsfiddle
而 Chrome 旋转到 90 -> 180 -> 270 -> 360 ...
在此特定示例中,Firefox 转到 57 -> 114 -> 171 -> 228 -> ...。
嗯,这基本上是 +1 rad 增加,但它必须对 animLength
和 animStepLength
的 selected 值做一些事情。如果我 select 它们不同,Firefox 会显示不同的值。
简单的 CSS 动画在这里可以工作,但我有理由在这里使用 JS。
您永远无法保证 setTimeout
或 setInterval
处理程序会在您要求时被调用。您应该始终将当前时间与初始时间进行比较,以确定您的动画应该显示什么样的进度,通常是通过计算动画已经过去的百分比。在下面的示例中查找 elapsedPercentage
变量。
Using setInterval
is considered bad practice because of that reason. The suggested way to animate is to use nested requestAnimationFrame;
下面的脚本可以使用很多改进,但它确实向您展示了如何根据动画开始后的时长来正确更新动画。
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>