Tyler330
Tyler330

Reputation: 415

Canvas animation loop

I am continuing to animate a model of a transportation system. The code builds stations, connects them with lines, builds "pods" to run along them, then works out the shortest path between random start and end stations.

jsfiddle

The problems I am having are 
a) the pods are traveling one at a time, rather than simultaneously.
b) the pod color is supposed to be related to the number of passengers, but this is clearly not happening.
c) I suspect I am producing a bottleneck in calculating the animation points, as it seems to run more hesitantly than it should.

I would also like to be able to flag the arrival of each pod, so that it can be reloaded, and start a new journey.

Something is wrong with the animation section, but I can't work out what. Any help would be appreciated!

animate();

var lastTime = 0;
var speed = 1; // higher is slower

function animate(time) {
    for (var i = 0; i < podArray.length; i++) {
        var aPod = podArray[i];
        // calculate incremental points along the path
        var points = calcWaypoints(aPod.wayStations);

        // return if the desired time hasn't elapsed
        if ((time - lastTime) < speed) {
            requestAnimationFrame(animate);
            return;
        }
        lastTime = time;

        //function animate(){
        ctx3.clearRect(0, 0, layer3.width, layer3.height);
        if (t < points.length - 1) {
            requestAnimationFrame(animate);
        }
        // draw pod from the last waypoint to the current waypoint

        ctx3.beginPath();
        ctx3.moveTo(aPod.startX, aPod.startY); //(points[t - 1].x, points[t - 1].y);
        ctx3.arc(points[t].x, points[t].y, 4, 0, Math.PI * 2, true);
        ctx3.fillStyle = aPod.color;
        ctx3.fill();
        t++;
    }

}

Upvotes: 0

Views: 96

Answers (1)

James Montagne
James Montagne

Reputation: 78650

  • a) Your calcWaypoints function takes a parameter that is a list of waystation, and then you ignore that parameter and calculate all points for all pods. This is creating one big array of all points, starting with the first pod's points, then the 2nd's, etc. This is why it starts with the first, then the 2nd then the third.

    function calcWaypoints(nextArray) {
        var frams = 100;
        var waypoints = [];
    
        for (var i = 1; i < nextArray.length; i++) {
            var pt0 = nextArray[i - 1];
            var pt1 = nextArray[i];
            var dx = pt1[0] - pt0[0];
            var dy = pt1[1] - pt0[1];
    
            for (var j = 0; j < frams; j++) {
                var x = pt0[0] + dx * j / frams //+ dxOff;
                var y = pt0[1] + dy * j / frams //+ dyOff;
                waypoints.push({
                    x: x,
                    y: y
                });
            };
        }
    
        return waypoints;
    }
    
  • b) This has the same cause as the previous, because you are using the same points for each pod, the pods are written over one another. In addition, you are clearing the canvas between each point. This clear should be moved out of the loop. ctx3.clearRect(0, 0, layer3.width, layer3.height);

  • c) For some reason you are re-calculating all points with every call to animate. You should calculate them all upfront once and then not in animate. You are also creating way too many calls to requestAnimationFrame in your loop.

I hacked together a few of the above changes and it is working. You should still clean it up a lot more:

layer1 = document.getElementById('layer1');
ctx1 = layer1.getContext('2d');
layer2 = document.getElementById('layer2');
ctx2 = layer2.getContext('2d');
layer3 = document.getElementById('layer3');
ctx3 = layer3.getContext('2d');

window.requestAnimFrame = (function (callback) {
    return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
        window.setTimeout(callback, 1000 / 60);
    };
})();

//STATIONS*************************************
var station = [
    ['A', 150, 100],
    ['B', 300, 100],
    ['C', 200, 175],
    ['D', 100, 250],
    ['E', 300, 250],
    ['G', 400, 250],
    ['F', 350, 200],
    ['Airport', 500, 200],
    ['Central', 500, 350]
];

function draw2() {
    for (var i = 0; i < station.length; i++) {
        var radius = 10;
        ctx2.beginPath();
        ctx2.arc(station[i][1], station[i][2], radius, 0, 2 * Math.PI);
        ctx2.stroke();
        ctx2.fillStyle = 'yellow';
        ctx2.fill();
        //ADD STATION LETTERS
        ctx2.font = '10pt Calibri';
        ctx2.fillStyle = 'black';
        ctx2.textAlign = 'center';
        ctx2.fillText(station[i][0], station[i][1], (station[i][2]) + 4);

    }
}
draw2();

//END STATIONS*************************************	
//START LINES**************************************
var lineArray = [
    ['A', 'B', ],
    ['B', 'A', 'C'],
    ['C', 'B', 'D', 'E'],
    ['D', 'C', 'E'],
    ['E', 'D', 'F', 'G', 'Central'],
    ['F', 'E', 'G'],
    ['G', 'F', 'E', 'Airport'],
    ['Airport', 'G', 'Central'],
    ['Central', 'E', 'Airport']
];


function drawLines() {
        for (m = 0; m < lineArray.length; m++) {
        ctx1.lineWidth = 1;
        ctx1.beginPath();
        for (p = 1; p < lineArray[m].length; p++) {
            var startStat = lineArray[m][0];
            var stat = lookUp(startStat);
            var beginX = station[stat][1];
            var beginY = station[stat][2];
            ctx1.moveTo(beginX, beginY);

            var endStat = lineArray[m][p];
            var endSt = lookUp(endStat);
            var closeX = station[endSt][1];
            var closeY = station[endSt][2];
            ctx1.lineTo(closeX, closeY);
        }

        ctx1.stroke();
    }
}
drawLines();
//END LINES*************************************

//SHORTEST PATH*********************************
function Graph() {
    var neighbors = this.neighbors = {}; // Key = vertex, value = array of neighbors.

    this.addEdge = function (u, v) {
        if (neighbors[u] === undefined) { // Add the edge u -> v.
            neighbors[u] = [];
        }
        neighbors[u].push(v);
        if (neighbors[v] === undefined) { // Also add the edge v -> u in order
            neighbors[v] = []; // to implement an undirected graph.
        } // For a directed graph, delete
        neighbors[v].push(u); // these four lines.
    };

    return this;
}


function shortestPath(graph, source, target) {
    var stationPath = [];
    var coordPath = [];
    if (source == target) { // Delete these four lines if
        print(source); // you want to look for a cycle
        return; // when the source is equal to
    } // the target.
    var queue = [source],
        visited = {
            source: true
        },
        predecessor = {},
        tail = 0;
    while (tail < queue.length) {
        var u = queue[tail++], // Pop a vertex off the queue.
            neighbors = graph.neighbors[u];
        for (var i = 0; i < neighbors.length; ++i) {
            var v = neighbors[i];
            if (visited[v]) {
                continue;
            }
            visited[v] = true;
            if (v === target) { // Check if the path is complete.
                var path = [v]; // If so, backtrack through the path.
                while (u !== source) {
                    path.push(u);
                    u = predecessor[u];
                }
                path.push(u);
                path.reverse();
                print(path.join(' &rarr; '));
                //console.log('Path: ' + path);

                for (s = 1; s < path.length; s++) {
                    stationPath.push(path[s]);
                }
                //console.log('Waypoints: ' + stationPath);
                for (t = 0; t < stationPath.length; t++) {
                    staCo = lookUp(stationPath[t]);
                    staCoX = station[staCo][1];
                    staCoY = station[staCo][2];
                    coordPath.push([staCoX, staCoY]);
                }
                //console.log(coordPath);
                return coordPath;
            }
            predecessor[v] = u;
            queue.push(v);
        }

    }
    print('there is no path from ' + source + ' to ' + target);
}

function print(s) { // A quick and dirty way to display output.
    s = s || '';
    document.getElementById('display').innerHTML += s + '<br>';
}

function findShortestPath(s1, s2) {
    var graph = new Graph();
    for (w = 0; w < lineArray.length; w++) {
        var baseStation = lineArray[w][0];
        for (z = 1; z < lineArray[w].length; z++) {
            graph.addEdge(baseStation, lineArray[w][z]);
        }
    }
    return (shortestPath(graph, s1, s2));
};


function lookUp(sta) {
    //console.log(sta);
    for (n = 0; n < station.length; n++) {
        if (sta == station[n][0]) {
            return n;

            break;
        }
    }
}

//BUILD PODS*************************************
var podArray = [];

function Pod(startX, startY, wayStations, riders, color) {
    this.startX = startX;
    this.startY = startY;
    this.wayStations = wayStations;
    this.riders = riders;
    this.color = color;
}
var colorArray = ['gold', 'orange', 'red', 'green', 'blue', 'black'];

function randomPass() {
    occ = 1 + Math.floor(Math.random() * 6);
    return occ;
}

//PROGRAM PODS*********************************************	
for (i = 0; i < 3; i++) { //NUMBER OF PODS
    //Start Station
    var startOff = Math.floor(Math.random() * station.length);
    var begSta = station[startOff][0];
    var fromX = station[startOff][1];
    var fromY = station[startOff][2];
    //END STATION	
    var destNum = Math.floor(Math.random() * station.length);
    while (startOff == destNum) {
        destNum = Math.floor(Math.random() * station.length);
    }
    var endSta = station[destNum][0];

    function getWayStations(beg, end) {
        var fsp = findShortestPath(beg, end);
        var nextArray = [];
        nextArray.push([fromX, fromY]);
        for (var f = 0; f < fsp.length; f++) {
            nextArray.push(fsp[f]);
        }
        return nextArray;
    }

    //LOAD POD DATA
    podArray.push(new Pod(
    startX = fromX,
    startY = fromY,
    wayStations = getWayStations(begSta, endSta),
    riders = randomPass(),
    color = colorArray[riders - 1]))
}

//END PROGRAM PODS*********************************************	

// calc waypoints traveling along nextArray
function calcWaypoints(nextArray) {
    var frams = 100;
    var waypoints = [];

    for (var i = 1; i < nextArray.length; i++) {
        var pt0 = nextArray[i - 1];
        var pt1 = nextArray[i];
        var dx = pt1[0] - pt0[0];
        var dy = pt1[1] - pt0[1];

        for (var j = 0; j < frams; j++) {
            var x = pt0[0] + dx * j / frams //+ dxOff;
            var y = pt0[1] + dy * j / frams //+ dyOff;
            waypoints.push({
                x: x,
                y: y
            });
        };
    }

    return (waypoints);
}


animate();

var lastTime = 0;
var speed = 1; // higher is slower

function animate(time) {
    // return if the desired time hasn't elapsed
    if ((time - lastTime) < speed) {
        requestAnimationFrame(animate);
        return;
    }
    lastTime = time;
  
    ctx3.clearRect(0, 0, layer3.width, layer3.height);
    
    var callAgain = false;
    
    for (var i = 0; i < podArray.length; i++) {
        var aPod = podArray[i];
        // calculate incremental points along the path
        var points = calcWaypoints(aPod.wayStations);

        if (t < points.length - 1) {
            callAgain = true;
        }
        
      // draw pod from the last waypoint to the current waypoint
        if(points.length > t){
            ctx3.beginPath();
            ctx3.moveTo(aPod.startX, aPod.startY); //(points[t - 1].x, points[t - 1].y);
            ctx3.fillStyle = aPod.color;
            ctx3.arc(points[t].x, points[t].y, 4, 0, Math.PI * 2, true);
            ctx3.fill();
        }
       
    }
	t++;
    
    if(callAgain) requestAnimationFrame(animate);
}
<canvas id='layer1' style='z-index: 2;  
				position:absolute;
				left:0px;
				top:0px;
				' height='600px' width='1000'>This text is displayed if your browser does not support HTML5 Canvas.</canvas>
<canvas id='layer2' style='z-index: 3;
				position:absolute;
				left:0px;
				top:0px;
				' height='600px' width='1000'>This text is displayed if your browser does not support HTML5 Canvas.</canvas>
<canvas id='layer3' style='z-index: 1;
				position:absolute;
				left:0px;
				top:0px;
				' height='600px' width='1000'>This text is displayed if your browser does not support HTML5 Canvas.</canvas>
<div id="display">
    </id>

Upvotes: 1

Related Questions