基于X,Y坐标计算角度

5

我正在尝试根据箭头所要去的位置计算箭头在球上的角度。箭头移动,但是它的运动方向无法解释。有人可以给一些指导吗?

Codepen链接: Codepen

我把完整代码添加到这里(根据输入进行了编辑):我新增了一个步骤,用于增大角度计算的差异,不确定这是否是正确的方法,但它似乎更加实用。并且在角度计算方法中添加了+/-90,但看起来还是不对劲。


class Throwable {

  constructor(){
    this.throwObject = null;
    this.canDrag = null;
    this.initialDiffX = 0;
    this.initialDiffY = 0;
    this.previousX = 0;
    this.previousY = 0;
    this.intervalCounter = 0;
  }

  set x(input) {
    this.throwObject.style.left = input + 'px';
  }

  set y(input) {
    this.throwObject.style.top = input + 'px';
  }

  set rotation(input) {
    this.throwObject.style.transform = `rotate(${input}deg)`;
  }

  init(){
    this.throwObject = document.querySelector('.throwable');
    this.throwObject.addEventListener('mousedown', this.activateDrag.bind(this));
    this.throwObject.addEventListener('mouseup', this.deactivateDrag.bind(this));
    document.addEventListener('mousemove', this.drag.bind(this));
  }

  activateDrag(event) {
    this.canDrag = true;
    this.initialDiffX = event.clientX - this.throwObject.offsetLeft;
    this.initialDiffY = event.clientY - this.throwObject.offsetTop;
  }

  deactivateDrag() {
    this.canDrag = false;
  }

  drag(event) {
    if(this.canDrag === true) {
      if(this.intervalCounter >= 30) {
         this.intervalCounter = 0;
      }
      if(this.intervalCounter === 0) {
        this.previousX = event.clientX;
        this.previousY = event.clientY;
      }
      this.intervalCounter++;
      this.y = event.clientY- this.initialDiffY;
      this.x = event.clientX - this.initialDiffX;
      this.rotation = this.angle(event.clientX, event.clientY, this.previousX, this.previousY);
    }
  }

  angle(ex, ey, cx, cy) {
    var dy = ey - cy;
    var dx = ex - cx;
    return Math.atan2(dy, dx) * 180 / Math.PI + 90;
  }

  // Untility
  log(logObject) {
    let logStr = '';
    for(let key in logObject) {
      logStr += `${key}: ${logObject[key]}<br>`;
    }
    document.getElementById('log').innerHTML = logStr;
  }
}

let throwable = new Throwable();
throwable.init();

我在比较两个不同的值时犯了一个错误,我已经修复了它,现在它工作得更好了,但有时仍会出现一些奇怪的行为,似乎不知道在某些点上该去哪里。但比以前好多了。


“angle()”函数返回的是角度值,你确定“this.rotation”需要的就是这个吗? - Moutah
是的,它期望角度值: set rotation(input) { this.throwObject.style.transform =rotate(${input}deg); } - Barry
5个回答

3
也许你的角度函数存在一些错误。这是我的解决方案:
angle(cx, cy, ex, ey) {
    var dy = ey - cy ;
    var dx = cx - ex ;
    return Math.atan2(dx, dy) * 180 / Math.PI;
}

2
Math.atan2() 函数期望第一个参数为 y 坐标。 - obscure
True,传递的第一个参数必须是Y,其次是X。 我认为错误一定出现在其他地方,如果您可以查看codepen,您会看到奇怪的旋转行为。 - Barry

1
这是由于在Math.atan2()和CSS旋转变换之间测量角度的方式有所不同。
对于我们人类来说,模拟时钟上的12点位置指的是角度0 - CSS旋转也一样自然。
但是,Math.atan2()测量的角度是从水平x轴开始的。因此,根据您的输入坐标,它可能是3点或9点位置。
然而,有一个简单的解决方法。计算角度后,
Math.atan2(dy, dx) * 180 / Math.PI

就像减去90度一样

Math.atan2(dy, dx) * 180 / Math.PI - 90

我已经添加了它,但它却相反地在减去。我像赫尔曼建议的那样加了90,否则它会逆时针运动。 - Barry
是的,这是因为角度范围从-180到180,而不是从0到360(同时您输入顶点的顺序也很重要)。 - obscure

0
当你调用this.angle()时,你会通过两次this.throwObject.offset...传递给它,一次是直接传递,一次是通过pxpy传递:
let px = this.throwObject.offsetLeft;
let py = this.throwObject.offsetTop;

this.rotation = this.angle(this.throwObject.offsetLeft, this.throwObject.offsetTop, px, py)

这将导致在 angle() 中的 dxdy 均为 0,使得 Math.atan2() 的结果不可预测。
我不确定你代码的其余部分,但也许你的意思是这样调用 angle()
this.rotation = this.angle(this.x, this.y, px, py);

我获取X-Y并将其存储在px-py中,然后设置新值,因此基本上以前的值都存储在px-py中。 - Barry

0

我看到了一些小问题。

首先,角度方法计算的是-180到180范围内的弧度,而您希望它在0到360之间。因此,在计算角度后,您需要进行类似以下的转换:

angle(ex, ey, cx, cy) {
    var dy = ey - cy;
    var dx = ex - cx;
    var theta = Math.atan2(dy, dx) * 180 / Math.PI;
    if (theta < 0) theta += 360; // convert to [0, 360]
    return theta;
}

首先,由于JavaScript坐标的工作方式,你元素的起始角度为0度并不是由该方法计算出的实际0度。一个快速解决办法是添加90度以使其匹配:
set rotation(input) {
    this.throwObject.style.transform = `rotate(${input + 90}deg)`;
}

这些转换后仍然有点不稳定,但我认为这是正确计算的开始。我猜问题的一部分是由于计算点太接近。


谢谢你,赫尔曼。我尝试了几种解决方案,包括你的,但由于某些原因它仍然不能正常工作。我添加了一个步骤间隔来使距离更大以进行比较。 - Barry
有人可以解释一下+90的偏移吗?当我根据sin/cos计算位置,然后使用tan2重新转换角度时,我的角度偏差为-90度。 - Joshua Taylor Eppinette
在计算 dx 时,将 ex - cx 参数翻转后,我不再需要 -90 度的偏移量。 - Joshua Taylor Eppinette

0

intervalCounter 变为 0 时会发生什么?上一个点移动到事件点,因此 dydx 变为 0,你会看到抖动:-180 + 90+180 + 900 + 90 如 Math.atan2 中所定义。之后,上一个点将保持不变,直到 intervalCounter < 30,并且上一个点和事件点之间的距离会增加,因此角度接近预期值。

无论如何,这是一个糟糕的坐标过滤器。你可以通过实现简单的指数过滤或使用固定大小(在你的情况下为 30)的队列来改进它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接