根据 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...
,一次直接,一次通过px
和py
:
let px = this.throwObject.offsetLeft;
let py = this.throwObject.offsetTop;
this.rotation = this.angle(this.throwObject.offsetLeft, this.throwObject.offsetTop, px, py)
这将导致dx
和dy
在angle()
中变为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 时会发生什么?前一个点移动到事件点,所以dy
,dx
变为 0,你有一个抖动:-180 + 90
,+180 + 90
,0 + 90
as defined in Math.atan2。之后,前一个点固定到intervalCounter < 30
,并且前一个点和事件点之间的距离越来越大,所以角度接近预期。
无论如何,这是一个糟糕的坐标过滤器。您可以通过实现简单的 exponential filtering 或使用固定大小(在您的情况下为 30)queue 作为事件点来改进它。
【讨论】:
以上是关于根据 x, y 位置计算角度的主要内容,如果未能解决你的问题,请参考以下文章
知道两个点的坐标X,Y,如何计算出两点间的距离以及角度,公式是啥