我试图使用MapBoxGL动画相机旋转,同时提供暂停旋转并通过复选框回调重新启动旋转的选项。“暂停”/“停止”旋转很好,但是“重新启动”似乎是在它应该在的地方,如果它从来没有暂停,而不是捡起动画停止的地方。
var animation;
function rotateCamera(timestamp) {
map.rotateTo((timestamp / 600) % 360, {duration: 0});
animation = requestAnimationFrame(rotateCamera);
}当地图加载时,将使用以下方法调用动画:
animation = rotateCamera(0);回调如下所示:
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声明为未定义,这似乎也不起作用。我被困住了!谢谢你的帮助。
发布于 2019-03-12 02:00:44
传递给requestAnimationFrame回调的时间戳是一个DOMHighResTimestamp,类似于performance.now()返回的时间戳。此时间戳表示自当前文档的生存期开始(可以是再复杂一点)开始执行回调以来所经过的毫秒数。
因此,即使没有运行requestAnimationFrame循环,这个时间戳也会增加,就像Date.now()一样。
let animation = 0;
inp.onchange = e => {
if (inp.checked) start();
else {
cancelAnimationFrame(animation);
}
};
function start(timestamp) {
_log.textContent = timestamp;
animation = requestAnimationFrame(start);
}<input type="checkbox" id="inp">
<pre id="_log"></pre>
在您的代码中,您可能只希望保留自上一帧以来所经过的时间。要做到这一点,您可以简单地将timestamp保存在一个全局可用的变量中,然后在回调中保存
var elapsed = timestamp - last_frame;
last_frame = timestamp;并且记住也要处理简历的情况,在那里timestamp将是未定义的,elapsed应该被重置。
现在,我想指出的是,您对问题的描述还可以完全指出另一个问题:您可以同时运行多个循环。
由于您使用单个animation变量来保存frame_id (由cancelAnimationFrame使用),如果在循环已经运行时调用了rotateCamera,那么第一个frame_ids就会丢失,并且它们的rAF循环确实会继续。
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();<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:
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);
}
}<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>
https://stackoverflow.com/questions/55112399
复制相似问题