【问题标题】:Calculate angle based on x, y position根据 x, y 位置计算角度
【发布时间】:2020-05-25 19:37:01
【问题描述】:

我正在尝试根据要到达的位置计算球上箭头的角度。 箭头在移动,但方向完全无法解释,谁能指点一下?

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 所期望的吗?
  • 是的,它的期望度数:` set rotation(input) { this.throwObject.style.transform = rotate(${input}deg); } `

标签: javascript math arrows atan2


【解决方案1】:

也许你的角度函数有一些错误。这对我有用:

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

【讨论】:

  • Math.atan2() 期望第一个参数是 y 位置。
  • 是的,第一个参数传递的肯定是Y,后面是X。我想错误一定是在别的地方,如果你可以看一下codepen,你可以看到奇怪的旋转行为.
【解决方案2】:

当你调用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)

这将导致dxdyangle() 中变为0,从而使Math.atan2() 的结果不可预测。

我不确定您的其余代码,但也许您打算像这样调用angle()

this.rotation = this.angle(this.x, this.y, px, py);

【讨论】:

  • 我得到 X-Y 并将其存储在 px-py 中,然后我设置新值,所以基本上以前的值存储在 px-py 中。
【解决方案3】:

我可以看到几个小问题。

首先,角度方法计算的弧度在 -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;
}

其次,由于 js 坐标的工作方式,您的元素在 0 度处的起始角度不是通过此方法计算的实际 0 度。一个快速的解决方法是添加 90 度以使其匹配:

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

在这些转换之后仍然有点麻烦,但我认为这是正确计算的开始。我的猜测是问题的一部分是有如此接近的计算点。

【讨论】:

  • 谢谢赫尔曼,我尝试了一些解决方案,也是你的,但由于某种原因仍然无法正常工作,我添加了一个步长间隔以使距离更大一些以进行比较。
【解决方案4】:

发生这种情况是因为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,就像 Herman 建议的那样,否则它会逆时针运行。
  • 是的,这是因为角度的范围是 -180 到 180 而不是 0 到 360(输入顶点的顺序也很重要)。
【解决方案5】:

intervalCounter 变为 0 时会发生什么?前一个点移动到事件点,所以dydx 变为 0,你有一个抖动:-180 + 90+180 + 900 + 90as defined in Math.atan2。之后,前一个点固定到intervalCounter &lt; 30,并且前一个点和事件点之间的距离越来越大,所以角度接近预期。

无论如何,这是一个糟糕的坐标过滤器。您可以通过实现简单的 exponential filtering 或使用固定大小(在您的情况下为 30)queue 作为事件点来改进它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-18
    • 1970-01-01
    相关资源
    最近更新 更多