Рассчитать угол на основе положения x, y - PullRequest
3 голосов
/ 10 февраля 2020

Я пытаюсь рассчитать угол наклона стрелки на шаре, основываясь на положении, в котором он собирается. Стрелка движется, но в совершенно необъяснимом направлении, кто-нибудь может дать несколько указателей?

Доступен кодовый код: Кодовый код

Я добавил полный код здесь (ИЗМЕНЕНО на основе input): я добавил шаг, чтобы увеличить разницу для вычисления угла, не уверен, что это правильный путь к go, но он кажется немного более функциональным. Плюс добавил +/- 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();

Я сделал ошибку при сравнении двух разных значений, я исправил это, он работает намного лучше, иногда все еще имеет какое-то странное поведение, кажется, что он не знает, где go в некоторых точках. Но работает лучше, чем раньше.

Ответы [ 5 ]

1 голос
/ 10 февраля 2020

Возможно, у вас есть ошибки в вашей функции угла. Это работает для меня:

angle(cx, cy, ex, ey) {
    var dy = ey - cy ;
    var dx = cx - ex ;
    return Math.atan2(dx, dy) * 180 / Math.PI;
}
0 голосов
/ 11 февраля 2020

Что происходит, когда intervalCounter становится 0? Предварительная точка перемещается в точку события, поэтому dy, dx становится 0, и у вас появляется дрожание: -180 + 90, +180 + 90, 0 + 90 , как определено в Math.atan2 . После этого точка превиуса фиксируется до intervalCounter < 30, и у вас есть некоторое расстояние между точками предвидения и события, так что угол близок к ожидаемому.

В любом случае, это плохой фильтр координат. Вы можете улучшить его, применяя простую экспоненциальную фильтрацию или используя фиксированный размер (30 в вашем случае) очередь для точки события.

0 голосов
/ 10 февраля 2020

Это происходит потому, что есть разница, как измеряются углы между Math.atan2() и преобразованием CSS.

Для нас, людей, естественно, что 12-часовая позиция на аналоговых часах относится к угол 0 - то же самое для CSS rotate.

Math.atan2 (), однако измеряет угол, начиная с горизонтальной оси x. Поэтому, в зависимости от ваших входных координат, это будет 3 или 9 часов.

Однако это легко исправить. После вычисления угла

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

просто вычтите 90 градусов, как

Math.atan2(dy, dx) * 180 / Math.PI - 90
0 голосов
/ 10 февраля 2020

Есть несколько небольших проблем, которые я вижу.

Во-первых, метод углов вычисляет радианы в диапазоне от -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;
}

Во-вторых, начальный угол вашего элемента в 0 градусов не является фактическим 0 градусами, рассчитанными этим методом из-за того, как работают js координаты. Быстрое решение состоит в том, чтобы добавить 90 градусов, чтобы привести его в соответствие:

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

После этого преобразования все еще немного дергается, но я думаю, что это начало правильных вычислений. Я полагаю, что часть проблемы заключается в том, что для расчета используются такие близкие точки.

0 голосов
/ 10 февраля 2020

Когда вы звоните 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 быть 0 в angle(), делая результат Math.atan2() непредсказуемым.

Я не уверен насчет остальной части вашего кода, но, возможно, вы хотели назвать angle() следующим образом:

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