HTML5 Canvas atan2关闭了90度

时间:2018-03-06 00:45:04

标签: javascript html5 canvas html5-canvas

我试图让绿色三角形围绕其中心旋转,并朝向鼠标位置定位。我能够做到这一点,你可以在这里查看完整的代码和结果:

https://codepen.io/Carpetfizz/project/editor/DQbEVe

考虑以下代码行:

  r = Math.atan2(mouseY - centerY, mouseX - centerX)

  ctx.rotate(r + Math.PI/2)

我随意地将Math.PI/2添加到我的角度计算中,因为没有它,旋转似乎是90度(通过检查)。我想要更好地理解atan2正在计算的坐标系,这样我可以证明将角度偏移90度的原因(并希望简化代码)。

编辑:

据我了解,Math.atan2正在测量蓝色所示的角度。不应该旋转两个三角形,蓝色角度朝向鼠标指针(橙色点)?嗯 - 显然不是因为它是相同的角度而且它们是两个不同的方向,但我似乎无法向自己证明这一点。

enter image description here

4 个答案:

答案 0 :(得分:2)

画布上的坐标系使用0°指向。这意味着你想要点“向上”的任何东西必须最初正确绘制。

在这种情况下您需要做的就是更改此图:

https://i.imgur.com/Zopefrd.png

rotated指向“向上”0°

你可以将数学回到你期望的那样。

var ctx = c.getContext("2d"), img = new Image;
img.onload = go; img.src = "https://i.stack.imgur.com/Yj9DU.jpg";

function draw(pos) {
  var cx = c.width>>1, 
      cy = c.height>>1,
      angle = Math.atan2(pos.y - cy, pos.x - cx);
      
  ctx.setTransform(1,0,0,1,cx, cy);
  ctx.rotate(angle);
  ctx.drawImage(img, -img.width>>1, -img.height>>1);
}

function go() {
  ctx.globalCompositeOperation = "copy";
  window.onmousemove = function(e) {draw({x: e.clientX, y: e.clientY})}
}
html, body {margin:0;background:#ccc}
#c {background:#fff}
<canvas id=c width=600 height=600></canvas>

答案 1 :(得分:1)

当你在数学课上做反复数词时,你通常会处理一个向上增加的y轴。然而,在大多数计算机图形系统中,包括画布图形,y增加向下。 [删除了错误的陈述]

编辑:我必须承认我之前写的错误有两个原因:

  • 通过添加π而不是π/ 2来补偿轴方向的变化。
  • 画布上下文rotate函数顺时针旋转以获得正角度,仅此一项就可以补偿y轴的翻转。

我在Plunker中使用了您的代码副本,现在我意识到90°旋转只是补偿您正在绘制的图形图像的起始方向。如果箭头指向右边开始,而不是直接向上,则不需要添加π/ 2.

答案 2 :(得分:1)

这是因为Math.atan2的工作原理。

From MDN

  

这是在正X轴和点(x,y)之间以弧度为单位的逆时针角度。
  A simple diagram showing the angle returned by atan2(y, x), picked from MDN

在上图中,正X轴是从交汇点到最右边位置的水平段。

为了更清楚,这里是这个图的交互式版本,其中x,y值被转换为[-1~1]值。

&#13;
&#13;
const ctx = canvas.getContext('2d'),
  w = canvas.width,
  h = canvas.height,
  radius = 0.3;

ctx.textAlign = 'center';
canvas.onmousemove = canvas.onclick = e => {
  // offset mouse values so they are relative to the center of our canvas
  draw(as(e.offsetX), as(e.offsetY));
}

draw(0, 0);

function draw(x, y) {
  clear();
  drawCross();
  drawLineToPoint(x, y);
  drawPoint(x, y);

  const angle = Math.atan2(y, x);
  drawAngle(angle);
  writeAngle(angle);
}

function clear() {
  ctx.clearRect(0, 0, w, h);
}

function drawCross() {
  ctx.lineWidth = 1;
  ctx.beginPath();
  ctx.moveTo(s(0), s(-1));
  ctx.lineTo(s(0), s(1));
  ctx.moveTo(s(-1), s(0));
  ctx.lineTo(s(0), s(0));
  ctx.strokeStyle = ctx.fillStyle = '#2e404f';
  ctx.stroke();
  // positive X axis
  ctx.lineWidth = 3;
  ctx.beginPath();
  ctx.moveTo(s(0), s(0));
  ctx.lineTo(s(1), s(0));
  ctx.stroke();
  ctx.lineWidth = 1;
  ctx.font = '20px/1 sans-serif';
  ctx.fillText('+X', s(1) - 20, s(0) - 10);
}

function drawPoint(x, y) {
  ctx.beginPath();
  ctx.arc(s(x), s(y), 10, 0, Math.PI * 2);
  ctx.fillStyle = 'red';
  ctx.fill();
  ctx.font = '12px/1 sans-serif';
  ctx.fillText(`x: ${x.toFixed(2)} y: ${y.toFixed(2)}`, s(x), s(y) - 15);
}

function drawLineToPoint(x, y) {
  ctx.beginPath();
  ctx.moveTo(s(0), s(0));
  ctx.lineTo(s(x), s(y));
  ctx.strokeStyle = 'red';
  ctx.setLineDash([5, 5]);
  ctx.stroke();
  ctx.setLineDash([0]);
}

function drawAngle(angle) {
  ctx.beginPath();
  ctx.moveTo(s(radius), s(0));
  ctx.arc(s(0), s(0), radius * w / 2,
    0, // 'arc' method also starts from positive X axis (3 o'clock)
    angle,
    true // Math.atan2 returns the anti-clockwise angle
  );
  ctx.strokeStyle = ctx.fillStyle = 'blue';
  ctx.stroke();
  ctx.font = '20px/1 sans-serif';
  ctx.fillText('∂: ' + angle.toFixed(2), s(0), s(0));
}

// below methods will add the w / 2 offset
// because canvas coords set 0, 0 at top-left corner

// converts from [-1 ~ 1] to px
function s(value) {
  return value * w / 2 + (w / 2);
}
// converts from px to [-1 ~ 1]
function as(value) {
  return (value - w / 2) / (w / 2);
}
&#13;
<canvas id="canvas" width="500" height="500"></canvas>
&#13;
&#13;
&#13;

所以现在,如果我们回到你的图像,它当前指向顶部(正Y轴),而你刚测量的角度对x轴是实际的,所以它没有&# 39; t指向你想要的地方。

现在我们知道了问题,解决方案非常简单:

  • 或者像您一样将+ Math.PI / 2偏移量应用于您的角度,
  • 修改原始图像,使其直接指向正X轴。

答案 3 :(得分:0)

我遇到了同样的问题,并且能够通过跟随轴“技巧”达到预期的结果:

// Default usage (works fine if your image / shape points to the RIGHT)
let angle = Math.atan2(delta_y, delta_x); 

// 'Tricky' usage (works fine if your image / shape points to the LEFT)
let angle = Math.atan2(delta_y, -delta_x); 

// 'Tricky' usage (works fine if your image / shape points to the BOTTOM)
let angle = Math.atan2(delta_x, delta_y);

// 'Tricky' usage (works fine if your image / shape points to the TOP)
let angle = Math.atan2(delta_x, -delta_y);