根据 x, y 位置计算角度

Posted

技术标签:

【中文标题】根据 x, y 位置计算角度【英文标题】:Calculate angle based on x, y position 【发布时间】: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($inputdeg)`;
  

  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($inputdeg); ` 【参考方案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 + 90deg)`;

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

【讨论】:

谢谢赫尔曼,我尝试了一些解决方案,也是你的,但由于某种原因仍然无法正常工作,我添加了一个步长间隔以使距离更大一些以进行比较。【参考方案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 作为事件点来改进它。

【讨论】:

以上是关于根据 x, y 位置计算角度的主要内容,如果未能解决你的问题,请参考以下文章

canvas - 圆圈内 hover效果

鼠标拖拽旋转js

旋转 UIL 标签辊

知道两个点的坐标X,Y,如何计算出两点间的距离以及角度,公式是啥

在 3D 中查找 X、Y 和 Z 轴的角度 - OpenGL/C++

从3个点检索正角度或负角度