Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript point to point angle calculation

We got 3 points: start, end and mail.

description

The mail image, moves in a curved line from the start and end point, this is done by jQuery animate.

Now the next step is to make the mail image rotate while the animation is running. So at the start point and end point it would be rotated 0 degrees, but while animating, it should rotate facing the path of the animation. (see the image)

What I have tried:

JSFiddle

// Init dom elements
var $start = $('#start');
var $end = $('#end');
var $mail = $('#mail');

// Get position coordinates
var startPos = $start.position();
var endPos = $end.Position();

// Angle calculation
var getAngle = function(currX, currY, endX, endY) {
  var angle = Math.atan2(currX - endX, currY - endY) * (180 / Math.PI);

  if (angle < 0) {
    angle = Math.abs(angle);
  } else {
    angle = 360 - angle;
  }

  return angle;
};

// Mail angle
var getMailAngle = function() {
  var currPos = $mail.position();
  var endPos = $end.position();
  return getAngle(currPos.left, currPos.top, endPos.left, endPos.top);
};

// Animate
$mail.animate({top: endPos.top, left: endPos.left}, {
  specialEasing: {left: "easeInSine", top: "linear"},

  // Executed each "animation" frame, so we rotate here.
  step: function() {
    var angle = getMailAngle();
    $(this).css('transform', 'rotate(' + angle + 'deg'));
  }
});

But the code above is not correct, the angle doesn't face up when started / ended, I have very little experience with geometry math, so I really appreciate help for the rotating calculations.

like image 265
randomKek Avatar asked Oct 04 '22 12:10

randomKek


1 Answers

First off, you need to use an easing animation that starts and ends with the same "angle". If you look at the different easing options, swing, easeInOutQuad and easeInOutSine are some of the valid options.

To calculate an approximation of the angle, you can look at the mail icon's current position and its next position (in the next animation frame). To get a good approximation you need to "manually" calculate the current and next position using the easing function. This also means you need to control the animation manually.

Here's a code snippet, and you can also see it on JSFiddle.

// Init dom elements
var $start = $('#start');
var $end = $('#end');
var $mail = $('#mail');

// Get position coordinates
var startPos = $start.offset();
var endPos = $end.offset();

// Angle calculation
var getAngle = function(currX, currY, endX, endY) {
  var angle = Math.atan2(currX - endX, currY - endY) * (180 / Math.PI);

  if (angle < 0) {
    angle = Math.abs(angle);
  } else {
    angle = 360 - angle;
  }

  return angle;
};

// Animate
var maxframe = 1000;
$({frame: 0}).animate({frame: maxframe}, {
    easing: "linear",
    duration: 1000,

  // Executed each "animation" frame, so we rotate here.
  step: function() {
      var easing = $.easing.easeInOutQuad;
      var left = easing(0, this.frame, startPos.left, endPos.left - startPos.left, maxframe);
      var leftNext = easing(0, this.frame+1, startPos.left, endPos.left - startPos.left, maxframe);

      var top = startPos.top + (endPos.top - startPos.top) * this.frame / maxframe;
      var topNext = startPos.top + (endPos.top - startPos.top) * (this.frame + 1) / maxframe;

      var angle = getAngle(left, top, leftNext, topNext);     
      $mail.offset({left: left, top: top});
      $mail.css('transform', 'rotate(' + angle + 'deg)');
  },

  // Set the final position
  complete: function() {
      $mail.offset($end.offset());
      $mail.css('transform', ''); 
  }  
});
like image 88
Anders Carstensen Avatar answered Oct 10 '22 04:10

Anders Carstensen