首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >requestAnimationFrame似乎在调用cancelAnimationFrame之后更新变量

requestAnimationFrame似乎在调用cancelAnimationFrame之后更新变量
EN

Stack Overflow用户
提问于 2019-03-12 00:33:59
回答 1查看 653关注 0票数 0

我试图使用MapBoxGL动画相机旋转,同时提供暂停旋转并通过复选框回调重新启动旋转的选项。“暂停”/“停止”旋转很好,但是“重新启动”似乎是在它应该在的地方,如果它从来没有暂停,而不是捡起动画停止的地方。

代码语言:javascript
复制
var animation;

function rotateCamera(timestamp) {
    map.rotateTo((timestamp / 600) % 360, {duration: 0});
    animation = requestAnimationFrame(rotateCamera);
}

当地图加载时,将使用以下方法调用动画:

代码语言:javascript
复制
animation = rotateCamera(0);

回调如下所示:

代码语言:javascript
复制
d3.selectAll("input[name='camerarotation-selection']").on("change", function() {
    if (d3.select("input[name='selection']").property("checked")) {
        rotateCamera(map.getBearing());
    } else {
        cancelAnimationFrame(animation);
    }
});

如果我在console.log函数中使用时间戳变量,我可以看到,尽管进行了cancelAnimationFrame调用,但它仍然在增加。我尝试在重新启动时将animation声明为未定义,这似乎也不起作用。我被困住了!谢谢你的帮助。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-03-12 02:00:44

传递给requestAnimationFrame回调的时间戳是一个DOMHighResTimestamp,类似于performance.now()返回的时间戳。此时间戳表示自当前文档的生存期开始(可以是再复杂一点)开始执行回调以来所经过的毫秒数。

因此,即使没有运行requestAnimationFrame循环,这个时间戳也会增加,就像Date.now()一样。

代码语言:javascript
复制
let animation = 0;
inp.onchange = e => {
  if (inp.checked) start();
  else {
    cancelAnimationFrame(animation);
  }
};

function start(timestamp) {
  _log.textContent = timestamp;
  animation = requestAnimationFrame(start);
}
代码语言:javascript
复制
<input type="checkbox" id="inp">
<pre id="_log"></pre>

在您的代码中,您可能只希望保留自上一帧以来所经过的时间。要做到这一点,您可以简单地将timestamp保存在一个全局可用的变量中,然后在回调中保存

代码语言:javascript
复制
var elapsed = timestamp - last_frame;
last_frame = timestamp;

并且记住也要处理简历的情况,在那里timestamp将是未定义的,elapsed应该被重置。

现在,我想指出的是,您对问题的描述还可以完全指出另一个问题:您可以同时运行多个循环。

由于您使用单个animation变量来保存frame_id (由cancelAnimationFrame使用),如果在循环已经运行时调用了rotateCamera,那么第一个frame_ids就会丢失,并且它们的rAF循环确实会继续。

代码语言:javascript
复制
let common_id = 0; // OP's `animation` variable
let first_id = 0;
let second_id = 0;

function loop1(timestamp) {
  common_id = first_id = requestAnimationFrame(loop1);
  log_1.textContent = "loop 1: " + timestamp;
}

function loop2(timestamp) {
  common_id = second_id = requestAnimationFrame(loop2);
  log_2.textContent = "loop 2: " + timestamp;
}

btn.onclick = e => {
  console.log("first loop's id", first_id);
  console.log("second loop's id", second_id);
  console.log('clearing common_id', common_id);
  cancelAnimationFrame(common_id);
}

loop1();
loop2();
代码语言:javascript
复制
<button id="btn">stop the loop</button>

<pre id="log_1"></pre>
<pre id="log_2"></pre>

我认为这在您的代码中是可能的,因为input[name='camerarotation-selection']可以更改多次,而input[name='selection']还没有转换,甚至因为input[name='camerarotation-selection']可能是多个元素。

为了避免这种情况,您可以保留一个信号量变量,允许您知道循环是否正在运行,并且只在没有运行时才启动它。或者,您甚至可以只使用一个信号量,并在cancelAnimationFrame回调的早期退出,就可以完全摆脱rAF:

代码语言:javascript
复制
let stopped = true;

function loop(timestamp) {
  if (stopped) return; // exit early
  requestAnimationFrame(loop);
  log.textContent = timestamp;
}
// you can click it several times
btn_start.onclick = e => {
  if (stopped === true) { // only if not running yet
    stopped = false;
    requestAnimationFrame(loop);
  }
}
btn_stop.onclick = e => {
  stopped = true; // deal only with the semaphore
};

btn_switch.onclick = e => {
  stopped = !stopped;
  if (stopped === false) { // we were paused
    // start again
    requestAnimationFrame(loop);
  }
}
代码语言:javascript
复制
<button id="btn_start">start the loop</button>
<button id="btn_stop">stop the loop</button>
<button id="btn_switch">switch the loop</button>

<pre id="log"></pre>

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55112399

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档