Canvas图片水平旋转动画实现求助:动画优化场景技术问询
如何用Canvas实现旋转硬币动画并测试性能
嘿,我明白你正在做动画优化,想试试用Canvas来测试旋转动画的性能,但对Canvas不太熟——别担心,我会一步步带你搞定这个旋转效果,还会给你性能测试的小技巧!
先看看你已经写的代码,你已经拿到了Canvas上下文、设置了画布尺寸和中心坐标,甚至准备好了角度变量和图片对象,已经迈出了一大步~接下来我们把代码补全,实现类似GIF的旋转效果:
完整实现代码
var cvs = document.getElementById('coin-spin'); var ctx = cvs.getContext('2d'); var w = cvs.width = 400; var h = cvs.height = 400; var cx = w / 2; var cy = h / 2; var rotationAngle = 0; // 记录旋转的弧度值 var coinImg = new Image(); // 替换成你自己的硬币图片路径 coinImg.src = 'your-coin-image.png'; // 必须等图片加载完成再启动动画,不然会画不出东西! coinImg.onload = function() { requestAnimationFrame(animateLoop); }; function animateLoop() { // 第一步:清空整个画布,避免上一帧的残留 ctx.clearRect(0, 0, w, h); // 敲黑板:保存当前画布状态,不然旋转会影响后续所有绘制 ctx.save(); // 把画布原点移到中心,这样旋转会围绕中心转(而不是左上角) ctx.translate(cx, cy); // 旋转画布(rotationAngle是弧度,数值越大转得越快) ctx.rotate(rotationAngle); // 绘制硬币图片:让图片中心和画布中心对齐,所以x/y设为负的图片宽高一半 ctx.drawImage(coinImg, -coinImg.width/2, -coinImg.height/2); // 恢复之前的画布状态,避免后续操作被旋转影响 ctx.restore(); // 更新旋转角度(可以调整0.1这个值来控制转速,比如0.05更慢,0.2更快) rotationAngle += 0.1; // 请求下一帧动画:这比setInterval性能好,会和浏览器刷新同步 requestAnimationFrame(animateLoop); }
关键知识点解释
- 图片加载时机:一定要在
img.onload里启动动画,因为图片是异步加载的,直接画的话会是空的。 - 状态管理:
ctx.save()和ctx.restore()是Canvas的核心技巧之一,每次旋转前保存状态,旋转后恢复,就不会让后续的绘制也跟着旋转。 - 旋转中心控制:先
translate到画布中心,再旋转,这样硬币会围绕中心转;绘制图片时用负的宽高一半,保证图片中心和画布中心重合。 - 高性能循环:用
requestAnimationFrame代替setInterval,它会自动适配浏览器的刷新频率,减少卡顿,更适合做动画。
性能测试小技巧
如果要测试这个动画的性能,你可以加一些简单的统计:
1. 统计运行时间
// 在动画启动前加 console.time('coin-animation'); // 如果要停止动画并查看时间,可以加个按钮触发: function stopAnimation() { console.timeEnd('coin-animation'); cancelAnimationFrame(animateLoop); }
2. 统计帧率(FPS)
帧率能直接反映动画的流畅度,加这段代码到animateLoop里:
var frameCount = 0; var lastTime = Date.now(); function animateLoop() { // ... 之前的绘制代码 frameCount++; var now = Date.now(); // 每1秒输出一次帧率 if (now - lastTime >= 1000) { console.log(`当前帧率: ${frameCount} FPS`); frameCount = 0; lastTime = now; } requestAnimationFrame(animateLoop); }
进阶:模拟3D翻面效果
如果你的参考GIF是硬币翻面的3D效果(不是单纯的平面旋转),可以准备正面和反面两张图,在旋转到特定角度时切换:
// 准备两张图片 var frontImg = new Image(); var backImg = new Image(); frontImg.src = 'coin-front.png'; backImg.src = 'coin-back.png'; var isShowingFront = true; var rotationAngle = 0; // 确保两张图都加载完成后启动动画 function checkImagesLoaded() { if (frontImg.complete && backImg.complete) { requestAnimationFrame(animateLoop); } } frontImg.onload = checkImagesLoaded; backImg.onload = checkImagesLoaded; function animateLoop() { ctx.clearRect(0, 0, w, h); ctx.save(); ctx.translate(cx, cy); // 切换图片:当旋转超过180度(π弧度)时翻面 if (rotationAngle > Math.PI) { isShowingFront = !isShowingFront; rotationAngle -= Math.PI; // 重置角度的循环部分 } var currentImg = isShowingFront ? frontImg : backImg; // 可以加一点缩放模拟翻面时的透视感 var scale = isShowingFront ? 1 : Math.cos(rotationAngle); ctx.scale(scale, 1); ctx.rotate(rotationAngle); ctx.drawImage(currentImg, -currentImg.width/2, -currentImg.height/2); ctx.restore(); rotationAngle += 0.08; requestAnimationFrame(animateLoop); }
这样就能模拟出硬币翻面的3D效果啦~
内容的提问来源于stack exchange,提问作者Mevia




