Levi Levi - 26 days ago 15
HTML Question

Moving object from A to B smoothly across canvas

I am trying to move an object smoothly from point A to point B using HTML canvas and regular javascript.

Point A is a set of coordinates

Point B is in the case the cursor location.

I made a jsfiddle of what I have so far: https://jsfiddle.net/as9fhmw8/

while(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
ctx.save();
ctx.beginPath();
ctx.translate(projectile.x, projectile.y);
ctx.arc(0,0,5,0,2*Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.stroke();
ctx.restore();
if(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
var stepsize = (projectile.mouseX - projectile.x) / (projectile.y - projectile.mouseY);
projectile.x += (stepsize + 1);
}
if(projectile.mouseY < projectile.y)
{
var stepsize = (projectile.y - projectile.mouseY) / (projectile.mouseX - projectile.x);
projectile.y -= (stepsize + 1);
}
}


Essentially what I can't figure out to do is to make the while loop slower (so that it appears animated in stead of just going through every iteration and showing the result).

I also can't figure out how to prevent the Arc from duplicating so that it creates a line that is permanent, instead of appearing to move from point a to point b.

Answer

Smooth animation here is really about determining how far to move your object for each iteration of the loop.

There is a little math involved here, but it's not too bad.

Velocity

Velocity in your case is just the speed at which your particles travel in any given direction over a period of time. If you want your particle to travel 200px over the course of 4 seconds, then the velocity would be 50px / second.

With this information, you can easily determine how many pixels to move (animate) a particle given some arbitrary length of time.

pixels = pixelsPerSecond * seconds

This is great to know how many pixels to move, but doesn't translate into individual X and Y coordinates. That's where vectors come in.

Vectors

A vector in mathematics is a measurement of both direction and magnitude. For our purposes, it's like combining our velocity with an angle (47°).

One of the great properties of vectors is it can be broken down into it's individual X and Y components (for 2-Dimensional space).

So if we wanted to move our particle at 50px / second at a 47° angle, we could calculate a vector for that like so:

function Vector(magnitude, angle){
   var angleRadians = (angle * Math.PI) / 180;

   this.magnitudeX = magnitude * Math.cos(angleRadians);
   this.magnitudeY = magnitude * Math.sin(angleRadians);
}

var moveVector = new Vector(50, 47);

The wonderful thing about this is that these values can simply be added to any set of X and Y coordinates to move them based on your velocity calculation.

Mouse Move Vector

Modeling your objects in this way has the added benefit of making things nice and mathematically consistent. The distance between your particle and the mouse is just another vector.

We can back calculate both the distance and angle using a little bit more math. Remember that guy Pythagoras? Turns out he was pretty smart.

function distanceAndAngleBetweenTwoPoints(x1, y1, x2, y2){
   var x = x2 - x1,
       y = y2 - y1;

   return {
      // x^2 + y^2 = r^2
      distance: Math.sqrt(x * x + y * y),

      // convert from radians to degrees
      angle: Math.atan2(y, x) * 180 / Math.PI
   }
}

var mouseCoords = getMouseCoords();
var data = distanceAndAngleBetweenTwoPoints(particle.x, particle.y, mouse.x, mouse.y);

//Spread movement out over three seconds
var velocity = data.distance / 3;

var toMouseVector = new Vector(velocity, data.angle);

Smoothly Animating

Animating your stuff around the screen in a way that isn't jerky means doing the following:

  1. Run your animation loop as fast as possible
  2. Determine how much time has passed since last time
  3. Move each item based on elapsed time.
  4. Re-paint the screen

For the animation loop, I would use the requestAnimationFrame API instead of setInterval as it will have better overall performance.

Clearing The Screen

Also when you re-paint the screen, just draw a big rectangle over the entire thing in whatever background color you want before re-drawing your items.

ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);

Putting It All Together

Here is a Fiddle demonstrating all these techniques: https://jsfiddle.net/jwcarroll/2r69j1ok/3/

Comments