如何在HTML5 Canvas中使用缓动函数实现精准旋转动画?
实现Canvas精准缓动旋转动画(不依赖FPS)
核心修正点
你的代码问题出在旋转角度的计算逻辑上:错误引入_fpsEstimate作为除数,且误解了Canvas旋转的工作方式。Canvas每次绘制都是基于初始画布状态的,因此你需要直接设置当前帧的总旋转角度,而非增量角度。
正确实现思路
- 用
performance.now()获取精准时间戳,计算当前动画已进行时间占总时长的比例(timeFraction,范围0-1)。 - 通过缓动函数将
timeFraction转换为带缓动效果的进度值(_progress)。 - 用
_progress * _desiredAngle得到当前帧应旋转的总角度,直接传入_rotate函数即可。 - 动画结束(
timeFraction >= 1)时,强制设置旋转角度为_desiredAngle,确保精准到位。
修改后的完整代码
let _canvas = document.querySelector('canvas'); let _context = _canvas.getContext('2d'); let _coordinates = { x: 100, y: 100 }; let _desiredAngle = 90; let _time = 3000; //// FUNCTIONS /////////////////////////////////////// /** * Timing function for easeInSine * @param {number} timeFraction Timing fraction * @return {number} Progress */ function _timingFunction(timeFraction) { return 1 - Math.cos((timeFraction * Math.PI) / 2); } /** * Rotate canvas * @param {number} degree Degree to rotate * @param {Object} coordinates X & Y coordinates */ function _rotate(degree, coordinates) { let [_x, _y] = [coordinates.x, coordinates.y]; _context.save(); _context.translate(_x, _y); _context.rotate(degree * Math.PI / 180); _context.translate(-_x, -_y); } /** * Animate object */ function _animate() { let _start = performance.now(); let _width = 50; let _height = 50; requestAnimationFrame(function animate(time) { _context.clearRect(0, 0, 500, 500); let _timeFraction = Math.min((time - _start) / _time, 1); // 限制在0-1范围内 let _progress = _timingFunction(_timeFraction); let _rotateAmount = _progress * _desiredAngle; // 直接计算当前总旋转角度 _rotate(_rotateAmount, _coordinates); _context.strokeRect( _coordinates.x - (_width / 2), _coordinates.y - (_height / 2), _width, _height ); if (_timeFraction < 1) { requestAnimationFrame(animate); } else { // 动画结束时强制设置为目标角度,确保精准 _context.clearRect(0, 0, 500, 500); _rotate(_desiredAngle, _coordinates); _context.strokeRect( _coordinates.x - (_width / 2), _coordinates.y - (_height / 2), _width, _height ); console.log('complete !'); } }); } //// INIT //////////////////////////////////////////// _animate();
<canvas width='500' height='500'></canvas>
关键说明
- 完全移除
_fpsEstimate,动画进度仅依赖精准时间计算,不受帧率波动影响。 - 用
Math.min限制_timeFraction不超过1,避免动画结束后进度超出范围。 - 动画结束时额外绘制一次目标角度的图形,彻底消除时间计算可能带来的微小偏差。
- 缓动函数
_timingFunction实现了和CSSeaseInSine一致的先慢后快效果,与你提供的CSS示例行为匹配。
内容的提问来源于stack exchange,提问作者Justin Byrne
相关产品推荐
相关产品推荐

