You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动