如何使requestAnimationFrame()独立于refreshrates

时间:2017-10-20 15:55:02

标签: javascript animation

我有一种情况,我希望在1秒内将600px宽div的宽度设置为0px。我可以使用requestAnimationFrame()。但如果动画需要1秒钟,我就不会100%确定。

它看起来像这样:

let width = 600;
let animation;
function animateWidth(){
  if(width <= 0){
    window.cancelAnimationFrame();
  }else{
    width -= 10; // (600px/60fps)
  }
  document.getElementById('id1').setAttribute('width', width);
  animation = window.requestAnimationFrame(animateWidth);
}
animation = window.requestAnimationFrame(animateWidth);

问题是,当设备具有不同的fps时,它将影响动画的持续时间(30fps需要2秒,而60fps则需要1秒)。我想确保动画的持续时间总是一秒钟。如果fps-rate不同,我想根据持续时间更改宽度的新值(因此以30 fps动画,我们将每步更改宽度20(600px / 30fps))。

  • 使用requestAnimationFrame时,有什么办法可以解决这个问题吗?如果我能得到帧之间的平均间隔或fps,我认为。
  • 我是否担心某些事情不是一个大问题?
  • 我可以在不同设备(移动设备,个人电脑,平板电脑等)上获得什么fps?

文档:https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame

2 个答案:

答案 0 :(得分:2)

requestAnimationFrame传递自页面加载到回调以来的时间,例如

const startValue = 600;
const endValue = 0;

// return a value between start and end as l goes from 0 to 1
function lerp(start, end, l) {
  return start + (end - start) * l;
}

const startTime = performance.now();
const durationInSeconds = 1;
const element = document.getElementById('id1');

function animateWidth(now) {
  const timeSinceStartInSeconds = (now - startTime) * 0.001;

  // l goes from 0 to 1 over durationInSeconds;
  const l = Math.min(timeSinceStartInSeconds / durationInSeconds, 1);

  element.setAttribute('width', lerp(startValue, endValue, l));

  // if we haven't finished request another animation frame
  if (l < 1) {
    requestAnimationFrame(animateWidth);
  }
}
requestAnimationFrame(animateWidth);
#id1 { background: red; }
<canvas id="id1"></canvas>

如果是我,我可能会尝试将它变成一个功能

// return a value between start and end as l goes from 0 to 1
function lerp(start, end, l) {
  return start + (end - start) * l;
}

function animateAttribute(element, attribute, startValue, endValue, duration) {
  const startTime = performance.now();

  function animate(now) {
    const timeSinceStart = (now - startTime) * 0.001;

    // l goes from 0 to 1 over durationInSeconds;
    const l = Math.min(timeSinceStart / duration, 1);

    element.setAttribute(attribute, lerp(startValue, endValue, l));

    // if we haven't finished request another animation frame
    if (l < 1) {
      requestAnimationFrame(animate);
    }
  }
  requestAnimationFrame(animate);
}

const element = document.getElementById('id1');
const attribute = 'width';
const start = 600;
const end= 0;
const duration = 1;

animateAttribute(element, attribute, start, end, duration);
#id1 { background: red; }
<canvas id="id1"></canvas>

此外,通过将l传递给these functions中的任何一个并将结果传递给lerp,您可以更改动画与these styles of motion中的任何一个匹配的方式。

至于你的其他问题

  

使用requestAnimationFrame时,有什么方法可以实现这一点吗?如果我能得到帧之间的平均间隔或者我认为可行的fps。

您可以计算帧之间的间隔。例如:

let then = performance.now();
function animate(now) {
  deltaTimeInMilliseconds = (now - then);
  then = now;

  ...
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
  

我是否担心某些事情不是一个大问题?

只有你可以回答这个问题

  

我可以在不同设备(移动设备,个人电脑,平板电脑等)上获得什么fps?

大多数设备都是60fps,但游戏玩家监视器的运行速度为120fps或240fps,VR(和WebVR)通常运行速度为90fps或更高,你可以使用VR内的浏览器。您是否关心任何这些情况取决于您。

答案 1 :(得分:0)

自动画开始以来测量时间并将其用作宽度计算中的参考将是一种常见的做法。

使用此代码,无论设备具有什么FPS,动画都将持续一秒钟。

const el = document.getElementById('id1');
const start = Date.now();

function animateWidth() {
  const t = (Date.now() - start) / 1000; // 1000 - one second

  if(t >= 1) {
    el.setAttribute('width', 0);
  }
  else {
    el.setAttribute('width', 600 * (1 - t));
    window.requestAnimationFrame(animateWidth);
  }
}

animateWidth();