(I'm new to coding in Javascript/HTML/CSS so excuse my terrible style!)
I'd like to animate an object in an array along a set of coordinates in that array at a static speed:
Example of the array:
_myPtArr = [{x:297, y:30},{x:299,y:47},{x:350,y:56},{x:305,y:176},{x:278,y:169},
{x:303,y:108},{x:269,y:79},{x:182,y:90},{x:137,y:81},{x:173,y:33},{x:231,y:38}];
What I did to do this was; create a function that runs at the beginning of the program to take in any array and add a distance property (named "dis") to the first of the two points it's calculating the distance of, and that looks like this:
function findDist(array) {
for (var i = 0; i<array.length-1;i++){
var p = array[i],
q = array[i+1],
dx = p.x - q.x,
dy = p.y - q.y,
dist = Math.sqrt(dx*dx + dy*dy);
array[i].dis=dist;
}
}
I did this because the way I was animating them was creating a variable named "_tick" which would move them from one point to another by incrementing from 0 thru 1, 0 being the starting point, and 1 being the ending point. And I was going to multiply the tick times the distance so that different line lengths would animate at the same speed. But I haven't gotten anything like that to work! I'm stuck! Here's the function/s which do what I was just talking about:
function calculateInnerPts(pts, pos){
var ptArr = [];
console.log(pts.length);
for(var i = 0; i < pts.length-1; i++){
ptArr[i] = {x: pts[i].x + (pts[i+1].x - pts[i].x) * pos, y: pts[i].y +
(pts[i+1].y - pts[i].y) * pos};
}
return ptArr;
}
Being called in a series like so:
In the enterFrameHandler (constant iteration) with "section" just incrementing to grab each set of 2 points/objects, so after _tick gets to "1", section++. :
ballAnim([_myPtArr[_section],_myPtArr[_section+1]],_tick);
then outside:
function ballAnim(pts,pos){
var iPts = pts;
for(var i = 0; i < pts.length-1; i++){
iPts = calculateInnerPts(iPts, pos);
}
drawBall(iPts[0]);
}
Finally, I draw the ball:
function drawBall(pts){
var w = 6,
h = 2;
ctx.clearRect(0,0,400,300);
ctx.beginPath();
ctx.arc(pts.x, pts.y, 5, 0, Math.PI * 2);
ctx.fillStyle="blue";
ctx.fill();
ctx.strokeStyle="black";
ctx.closePath();
ctx.stroke();
}
Could anyone shed any light on how I can acplish this given this system, or even another better way of acplishing the same effect? I would like to put in any array and get the same result (meaning the ball animates in a static speed)!
NOTE: I would NOT like to use anything but plain ol' Javascript/HTML/CSS; no API's!
Thank you all so much, this munity is so amazing, and this is my first post, so go easy on me! XD
(I'm new to coding in Javascript/HTML/CSS so excuse my terrible style!)
I'd like to animate an object in an array along a set of coordinates in that array at a static speed:
Example of the array:
_myPtArr = [{x:297, y:30},{x:299,y:47},{x:350,y:56},{x:305,y:176},{x:278,y:169},
{x:303,y:108},{x:269,y:79},{x:182,y:90},{x:137,y:81},{x:173,y:33},{x:231,y:38}];
What I did to do this was; create a function that runs at the beginning of the program to take in any array and add a distance property (named "dis") to the first of the two points it's calculating the distance of, and that looks like this:
function findDist(array) {
for (var i = 0; i<array.length-1;i++){
var p = array[i],
q = array[i+1],
dx = p.x - q.x,
dy = p.y - q.y,
dist = Math.sqrt(dx*dx + dy*dy);
array[i].dis=dist;
}
}
I did this because the way I was animating them was creating a variable named "_tick" which would move them from one point to another by incrementing from 0 thru 1, 0 being the starting point, and 1 being the ending point. And I was going to multiply the tick times the distance so that different line lengths would animate at the same speed. But I haven't gotten anything like that to work! I'm stuck! Here's the function/s which do what I was just talking about:
function calculateInnerPts(pts, pos){
var ptArr = [];
console.log(pts.length);
for(var i = 0; i < pts.length-1; i++){
ptArr[i] = {x: pts[i].x + (pts[i+1].x - pts[i].x) * pos, y: pts[i].y +
(pts[i+1].y - pts[i].y) * pos};
}
return ptArr;
}
Being called in a series like so:
In the enterFrameHandler (constant iteration) with "section" just incrementing to grab each set of 2 points/objects, so after _tick gets to "1", section++. :
ballAnim([_myPtArr[_section],_myPtArr[_section+1]],_tick);
then outside:
function ballAnim(pts,pos){
var iPts = pts;
for(var i = 0; i < pts.length-1; i++){
iPts = calculateInnerPts(iPts, pos);
}
drawBall(iPts[0]);
}
Finally, I draw the ball:
function drawBall(pts){
var w = 6,
h = 2;
ctx.clearRect(0,0,400,300);
ctx.beginPath();
ctx.arc(pts.x, pts.y, 5, 0, Math.PI * 2);
ctx.fillStyle="blue";
ctx.fill();
ctx.strokeStyle="black";
ctx.closePath();
ctx.stroke();
}
Could anyone shed any light on how I can acplish this given this system, or even another better way of acplishing the same effect? I would like to put in any array and get the same result (meaning the ball animates in a static speed)!
NOTE: I would NOT like to use anything but plain ol' Javascript/HTML/CSS; no API's!
Thank you all so much, this munity is so amazing, and this is my first post, so go easy on me! XD
Share Improve this question asked Aug 23, 2013 at 13:38 Super Fighting RobotSuper Fighting Robot 1589 bronze badges3 Answers
Reset to default 5I'm sure the answers above are more elegant, but I've been working on this code all morning and I want to share ...
Demo of code here
//requestAnimFrame shim via Paul Irish
window.requestAnimFrame = (function(callback){
return (window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback){window.setTimeout(callback, 1000/60);});
})();
//main code
window.onload = function(){
//setup canvas
var canvas = document.getElementById('mycanvas');
var ctx = canvas.getContext('2d');
//variables
var canvasWidth = 400, canvasHeight = 400; //as set up in the <canvas> tag
var animationTime = 10000; //animation length in milliseconds
//clear canvas function
var clearCanvas = function(){
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
};
//define the line; build function for displaying it
var _myPtArr = [
{x:297, y:30}, {x:299,y:47}, {x:350,y:56}, {x:305,y:176},
{x:278,y:169}, {x:303,y:108}, {x:269,y:79}, {x:182,y:90},
{x:137,y:81}, {x:173,y:33}, {x:231,y:38}
];
var lineColor = 'red';
var drawLine = function(){
ctx.beginPath();
ctx.moveTo(_myPtArr[0].x, _myPtArr[0].y);
for(var i=1, z=_myPtArr.length; i<z; i++){
ctx.lineTo(_myPtArr[i].x, _myPtArr[i].y);
}
ctx.strokeStyle = lineColor;
ctx.stroke();
};
//define the ball; build function for displaying it
var myBall = {x: 0, y: 0, radius: 5, fillStyle: 'blue', strokeStyle: 'black'};
var drawBall = function(){
ctx.beginPath();
ctx.arc(myBall.x, myBall.y, myBall.radius, 0, (Math.PI*2));
ctx.closePath();
ctx.fillStyle = myBall.fillStyle;
ctx.strokeStyle = myBall.strokeStyle;
ctx.fill();
ctx.stroke();
};
//build an array of objects for each line to help ball position calculations
var myBallVectors = [];
var fullDistance = 0, p, q, dx, dy, tempDistance, tempTime, fullTime = 0;
for(var i=1, z=_myPtArr.length; i<z; i++){
p = _myPtArr[i-1], q = _myPtArr[i];
dx = q.x - p.x;
dy = q.y - p.y;
tempDistance = Math.sqrt((dx*dx)+(dy*dy));
fullDistance += tempDistance;
myBallVectors.push({x: p.x, y: p.y, dX: dx, dY: dy, magnitude: tempDistance});
}
for(var i=0, z=myBallVectors.length; i<z; i++){
myBallVectors[i]['start'] = fullTime * animationTime;
tempTime = myBallVectors[i].magnitude/fullDistance;
fullTime += tempTime
myBallVectors[i]['duration'] = tempTime * animationTime;
myBallVectors[i]['finish'] = fullTime * animationTime;
}
//function to work out where ball is in space and time
var myBallPosition = function(){
if(Date.now() > breakTime){
//move on to next line
currentLine++;
//check to see if animation needs to finish
if(currentLine >= myBallVectors.length){
doAnimation = false;
}
//otherwise, set new break point
else{
breakTime = startTime + parseInt(myBallVectors[currentLine].finish);
}
}
if(doAnimation){
//calculate ball's current position
var timePassed = Date.now() - (startTime + myBallVectors[currentLine].start)
var percentageLineDone = timePassed/myBallVectors[currentLine].duration;
myBall.x = myBallVectors[currentLine].x + (myBallVectors[currentLine].dX * percentageLineDone);
myBall.y = myBallVectors[currentLine].y + (myBallVectors[currentLine].dY * percentageLineDone);
}
};
//setup and display initial scene
drawLine();
myBall.x = _myPtArr[0].x;
myBall.y = _myPtArr[0].y;
drawBall();
//animation loop function
animate = function(){
clearCanvas();
drawLine();
myBallPosition();
drawBall();
if(doAnimation){
requestAnimFrame(function(){
animate();
});
}
};
//setup control variables, and start animation
var startTime = Date.now(), currentLine = 0;
var breakTime = startTime + parseInt(myBallVectors[currentLine].finish);
var doAnimation = true;
animate();
};
Animating an object over a polyline at an even rate of speed
What is speed?
Speed is distance traveled per second (or minute,hour,etc).
For example: speed is miles per hour.
So animating at a uniform speed really means animating at uniform distance per second.
And animating at a uniform speed over your polyline really means placing balls at uniform distances over that polyline, regardless of where each line segment ends.
The important aspect is the balls must always be dropped the same distance apart.
For example: If you need to travel 300 pixels each second, you might have to travel through many points until you reach that distance of 300 pixels. Or if the points are really far apart, you might not even fully travel to the next point.
The following code builds on your _myPtArr and findDist.
It drops balls at a uniform distance along the polyline defined by _myPtArr.
The total polyline is divided into an arbitrary 20 uniform length steps.
Each new ball is dropped every 1/4th second.
You can change the drop speed by either:
- Changing the number of steps (balls) required to plete the polyline.
- Change the timer interval so balls are dropped faster/slower.
To make it appear that a single ball is moving, just clear the previous ball before drawing the next ball.
To make the ball more move smoothly:
- Increase the steps, maybe to 200.
- Decrease the time interval, maybe to 25.
Here is code and a Fiddle: http://jsfiddle/m1erickson/3Fmm2/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery./jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
_myPtArr = [{x:297, y:30},{x:299,y:47},{x:350,y:56},{x:305,y:176},{x:278,y:169},
{x:303,y:108},{x:269,y:79},{x:182,y:90},{x:137,y:81},{x:173,y:33},{x:231,y:38}];
var totDistance=0;
for (var i = 0; i<_myPtArr.length-1;i++){
var p = _myPtArr[i],
q = _myPtArr[i+1],
dx = p.x - q.x,
dy = p.y - q.y,
dist = Math.sqrt(dx*dx + dy*dy);
totDistance+=dist;
_myPtArr[i].dist=dist;
_myPtArr[i].untraveled=dist;
}
var steps=20;
var distancePerStep=totDistance/steps;
var totTraveled=0;
var currentPoint=0;
// drop first ball
drawBall(_myPtArr[0].x,_myPtArr[0].y);
animate();
function animate(){
var mustTravel=distancePerStep;
while(mustTravel>0){
// check if we're done
if(currentPoint >= _myPtArr.length-1){console.log("done"); return;}
var pt0=_myPtArr[currentPoint];
var pt1=_myPtArr[currentPoint+1];
var dx=pt1.x-pt0.x;
var dy=pt1.y-pt0.y;
var lastX,lastY;
if(pt0.untraveled<mustTravel){
// travel this whole segment
drawSegment(pt0,pt1);
lastX=pt1.x;
lastY=pt1.y;
// and reduce d by length traveled
mustTravel -= pt0.untraveled;
pt0.untraveled = 0;
if(mustTravel<1){mustTravel=0;}
// go onto the next point
currentPoint++;
}else{
// travel only part of this segment
// It has enough available length to plete travel
// start at the previously traveled point on the segment
var prevTraveled = pt0.dist - pt0.untraveled;
var x1 = pt0.x + dx * prevTraveled/pt0.dist;
var y1 = pt0.y + dy * prevTraveled/pt0.dist;
// travel only part of segment
var x2 = x1 + dx * mustTravel/pt0.dist;
var y2 = y1 + dy * mustTravel/pt0.dist;
// draw a segment
drawSegment({x:x1,y:y1},{x:x2,y:y2});
lastX=x2;
lastY=y2;
// update segement and untraveled
pt0.untraveled -= mustTravel;
mustTravel=0;;
}
}
// drop a ball
drawBall(lastX,lastY);
setTimeout(animate,250);
}
function drawSegment(pt0,pt1){
console.log("segment:"+pt0.x+"/"+pt0.y+" -- "+pt1.x+"/"+pt1.y);
ctx.beginPath();
ctx.moveTo(pt0.x,pt0.y);
ctx.lineTo(pt1.x,pt1.y);
ctx.stroke();
}
function drawBall(x,y){
console.log("ball:"+x+"/"+y);
ctx.beginPath();
ctx.arc(x,y,5,0,Math.PI*2,false);
ctx.closePath();
ctx.fill();
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=360 height=350></canvas>
</body>
</html>
I'm not sure how you code performs, but I don't see a function or other kind of code to control the animation speed.
The first thing you must know is that for the human eye to perceive any change as smooth it most occur in less than 30 milliseconds. The second thing you need is a function that runs at regular intervals which can be achieved with either setTimeout or setInterval.
So the first step is to execute you animation function indirectly by setTimeout or setInterval, I reend you to start at a 30 milliseconds rate and then adjust the time interval depending on the performance of your code.
Once you have that, you need to know how many times the animation function must be executed in order to reach the desired speed, for example if you want you animation to last 1 second your animation function must be executed 33 times, so the your point(s) must move distance/33 pixels each time. The speed would be measured then in pixels per animation frame... you must remember that because of distance is measured into straight line, speed has also a straight line meaning, so you just cannot add it to x and y, because the resulting speed will be different than you expect, so the proper approach is:
speed = distance/frames;
.
.
.
dx = end.x-current.x;
dy = end.y-current.y;
d = Math.sqrt(dx*dx+dy*dy);
current.x += speed*dx/d;
current.y += speed*dy/d;
a more efficient approach would be:
dx = end.x-start.x;
dy = end.y-start.y;
speed.x = dx/frames;
speed.y = dy/frames;
.
.
.
current.x += speed.x;
current.y += speed.y;
... so you would have two speed controls: frames per second, and pixels per frame. This way you can animate all the balls at exactly the same speed or animate them at proportional speeds so all start and end moving at the same time, make them move at fixed speed, accelerate, slow down and all sort of effects... is up to you.