javascript - 使用 requestAnimationFrame() 重新启动时如何保持箭头位置不变?
问题描述
我试图理解 MDN 上的一个例子。
当前代码的效果对我来说有点令人困惑-如果您稍等片刻然后重新开始,则暂停后箭头的位置不是它最初停止的位置。
如何确保从我离开的地方重新开始?我想了一个方法如下,但我不是很满意。
const spinner = document.querySelector('div')
let rotateCount = 0
let startTime = null
let rAF
let spinning = false
let previousRotateCount = 0
let hasStopped = false
let rotateInterval
function draw(timestamp) {
if (hasStopped) {
// The angle of each rotation is constant
rotateCount += rotateInterval
rotateCount %= 360
} else {
if (!startTime) {
startTime = timestamp
}
rotateCount = (timestamp - startTime) / 3
rotateCount %= 360
rotateInterval = rotateCount - previousRotateCount
previousRotateCount = rotateCount
}
console.log(rotateCount)
spinner.style.transform = `rotate(${rotateCount}deg)`
rAF = requestAnimationFrame(draw)
}
document.body.addEventListener('click', () => {
if (spinning) {
hasStopped = true
cancelAnimationFrame(rAF)
} else {
draw()
}
spinning = !spinning
})
html {
background-color: white;
height: 100%;
}
body {
height: inherit;
background-color: #f00;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
}
div {
display: inline-block;
font-size: 10rem;
}
<div>↻</div>
我希望有更好的方法。非常感谢你。
解决方案
这个示例代码有点奇怪,他们定义了一个rotateCount
从未实际使用过的全局,并且他们对时间戳的使用是......是的,令人困惑。
他们let rotateCount = (timestamp - startTime) / 3;
使得动画将需要 1080 毫秒来执行一整圈(360 度 x 3 = 1080 毫秒)。
但这是基于当前时间和开始时间之间的差异。所以事实上,即使你暂停了动画,重新启动时它也会像从未暂停过一样。
为此,您需要实际使用全局rotateCount
变量(通过不在 内部重新定义新变量draw
),并每次将其递增自上次绘制以来所需的旋转量,而不是自整体开始以来。
然后,您只需要确保在恢复动画时更新最后绘制的时间戳,并让动画真正暂停和恢复。
const spinner = document.querySelector('div');
const duration = 1080; // ms to perform a full revolution
const speed = 360 / duration;
let rotateCount = 0;
let rAF;
// we'll set this in the starting code
// (in the click event listener)
let lastTime;
let spinning = false;
// Create a draw() function
function draw(timestamp) {
// get the elapsed time since the last time we did fire
// we directly get the remainder from "duration"
// because we're in a looping animation
const elapsed = (timestamp - lastTime) % duration;
// for next time, lastTime is now
lastTime = timestamp;
// add to the previous rotation how much we did rotate
rotateCount += elapsed * speed;
// Set the rotation of the div to be equal to rotateCount degrees
spinner.style.transform = 'rotate(' + rotateCount + 'deg)';
// Call the next frame in the animation
rAF = requestAnimationFrame(draw);
}
// event listener to start and stop spinner when page is clicked
document.body.addEventListener('click', () => {
if(spinning) {
cancelAnimationFrame(rAF);
spinning = false;
} else {
// reset lastTime with either the current frame time
// or JS time if unavailable
// so that in the next draw
// we see only the time that did elapse
// from now to then
lastTime = document.timeline?.currentTime || performance.now();
// schedule the next draw
requestAnimationFrame( draw )
spinning = true;
}
});
html {
background-color: white;
height: 100%;
}
body {
height: inherit;
background-color: red;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
}
div {
display: inline-block;
font-size: 10rem;
user-select: none;
}
<div>↻</div>
推荐阅读
- amazon-web-services - AWS 访客专用应用程序的最佳流程是什么
- javascript - 如何在浏览器中配置 prettier 来识别 html
- reactjs - reactjs KeyboardDatePicker + react hook form:如何获取YYYY-MM-DD格式的日期
- reactjs - 内置电子和反应应用程序中的空白屏幕(由于未找到文件)
- react-native - React Native 中的 require("file:///...") 使用本地资源,可能吗?
- python - (sqlite3.IntegrityError) NOT NULL 约束失败:surfers.id
- c++ - 如何将位集拆分为数组
- scala - 如何将通用测试配置应用于所有项目?
- node.js - 在 JSON 中附加每个元素的坐标
- python - 在 Python 中是否有可能有一个函数来创建许多具有递增更改名称的新变量