requestAnimationFrame 没有按预期工作

requestAnimationFrame doesn't work as expected

我的脚本有问题,我不明白哪里出了问题。

这里是:

let angle = 0
let multiplier = .01

function raf() {

  if(angle >= 1 || angle < 0) {
    multiplier = - multiplier 
  }

  angle = angle + multiplier

  if(angle < 0) console.log(angle)

  requestAnimationFrame(raf)
}

requestAnimationFrame(raf)

目的是将角度提高0.01,当达到1时,应该减少到0,然后再提高到1。

我有时会得到一个负角 (-8.etc),我不明白为什么(console.log 应该表明了这一点)。

我为此做了一支笔:https://codepen.io/mourtazag/pen/QvjjLy

谢谢大家!

穆尔塔扎。

您遇到了不幸的浮点数学问题

0.94 - 0.01
0.9299999999999999

这些错误会随着您越来越多地减去 0.01 而复合,所以最后不是执行 0.01 - 0.01 会得到 0,代码而是执行类似

的操作
0.009999999999999247 - 0.01 

略显消极。如果你需要这样限制,你可能只用 Math.max(0, yourValue) 和 Math.min(1, yourValue) 限制最小值和最大值。

这是一个浮点问题

var a = 0;
for (var i = 0; i < 100; ++i) {
  a += 0.01;
} 
console.log(a);
for (var i = 0; i < 101; ++i) {
  a -= 0.01;
} 
console.log(a);
a += 0.01;
console.log(a);

打印

1.0000000000000007
-0.010000000000000087
-8.673617379884035e-17

考虑使用整数或时钟并从中计算角度

您没有收到 -8,您收到 -8.XXXXXXe-17。注意最后的 e-17 。那是科学记数法,所以你的数字实际上是 -0.00000000000000008XXXXX。您可以使用 angle.toFixed(20) 查看没有科学记数法的浮点数。该数字是您要显示的小数位数。

如果只增加和减少 0.01,为什么会得到这么小的数字?请问。发生这种情况是因为浮点数不精确,因为小数不可能是 "saved to the end",不像整数。例如,想想 1/3 有无限多的小数。在这里您可以阅读更多关于 JavaScript 和浮点精度的信息:Dealing with float precision in Javascript

另一个问题是有一个负角(这么小,但仍然是负的,尽管你实际上检查了负角)。发生这种情况是因为您在 increasing/decreasing 之前检查了负角,所以如果您的乘数为负而角度为 0.0001whatever(由于浮点精度问题),它仍然高于零,所以你的乘数仍然是负的,但实际上比你的角度大,所以当应用乘数时,角度将是负的。

我能想到的办法来解决这个问题:

let angle = 0
let multiplier = .01

function raf() {

  angle = angle+multiplier;

  if(angle > 1 || angle < 0) {
    multiplier = -multiplier 
    angle = angle<0?0:1;
  }
  console.log(angle.toFixed(20))

  requestAnimationFrame(raf)
}

requestAnimationFrame(raf)

有了这个,你可以在处理后检查你的角度是否在限制范围内,这样你就可以克服精度问题:https://codepen.io/anon/pen/eWppGy

另一种方法是使用整数(因为它们没有精度问题,正如我上面所说的)并在使用它时将角度除以 100,如下所示:

let angle = 0
let multiplier = 1

function raf() {

  angle = angle+multiplier;

  if(angle >= 100 || angle <= 0) {
    multiplier = -multiplier;
    angle = angle<=0?0:100;
  }
  console.log(angle/100)

  requestAnimationFrame(raf)
}

requestAnimationFrame(raf)

笔数:https://codepen.io/anon/pen/XRmmVq

题外话:换个角度。作为经过时间的函数而不是增量变化。

let start = Date.now();
let speed = 1 / 10000;  //1 in every 10 seconds

function raf() {
 let v = (Date.now() - start) * speed;
 let angle = v&1? 1-v%1: v%1;
  
 document.body.textContent = "Angle: " + angle.toFixed(3);
 requestAnimationFrame(raf);
}

//or with some sugar:
speed = 180/10000;
function raf() {
    let minAngle = -90, maxAngle = 90, delta = maxAngle - minAngle;

 let v = (Date.now() - start) * speed;
 let angle = (v/delta)&1? maxAngle-v%delta: minAngle + v%delta;
  
 document.body.textContent = "angle: " + angle.toFixed(3);
 requestAnimationFrame(raf);
}

raf();

或者加点糖;当我们不讨论从 01

的范围时的整个计算

let start = Date.now();
let speed = 180 / 10000;
let minAngle = -30, maxAngle = 45;

function raf() {
 let delta = maxAngle - minAngle;
 let v = (Date.now() - start) * speed;
 let backwards = (v/delta)&1;
 let angle = backwards? maxAngle - v%delta: minAngle + v%delta;

 document.body.textContent = "angle: "+ angle.toFixed(3) +", direction: " + (backwards? "backward": "forward");

 requestAnimationFrame(raf);
}

raf();