画布钟摆动画-画布平移

canvas pendulum animation - canvas translating

本文关键字:布平移 -画 动画 布钟摆      更新时间:2023-09-26

我试图在HTML5 Canvas中制作一个简单的钟摆,但我被卡住了。我想把它向左和向右摆动25度,所以我计算出我应该在y轴上平移大约-3.5像素的每一帧(向右摆动时平移3.5像素)。我正在使用以下代码

var rotation = Math.PI/180, //rotate about 1deg
    translation = -3.5,
    counter = 0; //count rotations
function draw() {
    var element = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    ctx.clearRect(0,0,element.width,element.height);
    ctx.translate(0, translation);
    ctx.rotate(rotation);
    //function draws all objects
    objects(element,ctx);
    if (counter == 25) {
      rotation *= -1;
      translation *= -1;
      counter = -25;
    } 
    counter += 1;
    window.requestAnimationFrame(draw);
  }

一切看起来都很好,但当钟摆改变方向时,一切都在x轴上平移,几秒钟后就从屏幕上消失了。。这个代码出了什么问题?或者我在计算中遗漏了什么?我的代码在这里https://jsfiddle.net/qskxjzv9/2/

提前感谢您的回答。

问题是,当涉及旋转,然后进行平移时,x和y将被平移到与看起来逻辑不同的方向。

为了解决这个问题,我们实际上不需要涉及平移,而只需要使用它来放置枢轴(旋转点),然后使用基于不同计算钟摆运动方式的绝对旋转。

例如,这将同时解决平移问题和平滑钟摆运动:

  • 更改绘制方法以绘制原点为(0,0)的钟摆-只需更改初始坐标,使其围绕(0,0
  • 平移到屏幕的轴心点-这是发生旋转的地方
  • 使用sin()作为因子进行旋转-这将创建一个平滑的动画,看起来更像钟摆,并且它将限制移动到范围为[-1,1]的角度
  • 使用counter来移动sin()-这是一个频率因子(你可以稍后将其转换为实际频率,比如说,让钟摆每分钟移动n次等等)。为了简单起见,我只使用了现有的counter变量并降低了它的步长值

主代码然后:

var maxRot = 25 / 180 * Math.PI,    // max 25° in both directions
    counter = 0,
    // these are better off outside loop
    element = document.getElementById('canvas');
    ctx = element.getContext('2d');
function draw() {
  // reset transform using absolute transformation. Include x translation:
  ctx.setTransform(1,0,0,1,element.width*0.5,0);
  // clear screen, compensate for initial translate
  ctx.clearRect(-element.width*0.5,0,element.width,element.height);
  // rotate using sin() with max angle
  ctx.rotate(Math.sin(counter) * maxRot);
  // draw at new orientation which now is pivot point
  objects(element, ctx);
  // move sin() using "frequency"-ish value
  counter += 0.05;
  window.requestAnimationFrame(draw);
}

Fiddle

附加

感谢@Blindman67提供的额外改进:

要控制振荡的频率,你可以做一些小的改变-首先定义频率:

var FREQUENCY = 3;

定义一个将进行转换的函数:

function sint(time) {
    return Math.sin(FREQUENCY * time * Math.PI * 0.002); // 0.002 allow time in ms
}

如果您现在更改draw()方法以采用时间参数而不是计数器:

function draw(time) {
   ...
}

然后你可以这样称呼轮换:

ctx.rotate(sint(time) * maxRot);

您需要将原点转换为要旋转的点:

  ctx.translate(element.width / 2, 0);

然后,你建议的轮换:

  ctx.rotate(rotation);

最后,翻译回来:

  ctx.translate(- element.width / 2, 0);

看到这个评论你的小提琴叉子。