0

I'm a javascript beginner and I'm trying to improve my skills. I'm currently working on canvas and animation through website.

What I'm trying to do is a 'simulation' of a city car traffic. I know that it's pretty useless but it makes me work on different aspects of coding.

Currently, my code is working as I want for now BUT the animation is slow and a bit laggy. Sometimes, the code takes time to calculate the cars' positions.

Here is the code of the complete html page :

<!DOCTYPE html>
<html>
    <head>
    <meta charset="utf-8" />

    <style>
        *{
            margin: 0;
            position: absolute;
        }
        #background{
            z-index: 1;
        }
        #cars{
            z-index: 2;
        }
    </style>

</head>
<body>


    <p id='debug'></p>

    <canvas id="background"></canvas>
    <canvas id="cars"></canvas>

    <script type="text/javascript">

    // BACKGROUND CANVAS -------------------------------------------
        var background = document.getElementById("background");
        background.height=window.innerHeight;
        background.width=window.innerWidth;
        var bkg=background.getContext("2d");
    //--------------------------------------------------------------


    // ANIMATED CANVAS ------------------------------------------------
        var anim = document.getElementById("cars");
        anim.height=window.innerHeight;
        anim.width=window.innerWidth;
        var anm=anim.getContext("2d");
    // -------------------------------------------------------------




    // VARIABLES ------------------------------------------

        var width = anim.width;
        var height = anim.height;

        var numCars = 10;

        var numRoads = 7;

        if(width/numRoads/2<height/numRoads/2){
            var roadWidth = ~~(width/numRoads/2);
        } else {
            var roadWidth = ~~(height/numRoads/2);
        }

        var klaxon = new Audio('res/sounds/klaxon.wav');
    //------------------------------------------------------------------    



    // CROSSROAD LIST -----------------------------------
        var roads = [];

        for(i=0;i<numRoads+2;i++){
            for(j=0;j<numRoads+2;j++){
                var create = getRandomInt(3);
                if(create>0){
                    roads.push([~~(width/(numRoads+1)*j),~~(height/(numRoads+1)*i),1])
                } else {
                    roads.push([~~(width/(numRoads+1)*j),~~(height/(numRoads+1)*i),0])
                }
            }
        }
    // ------------------------------------------------------------




    // TURN LIST -----------------------------------------
        var turns = [];
        var roadLength = roads.length;
        for(i=0;i<roadLength;i++){
            var road = roads[i];
            if(road[2]==1){
                if(i>numRoads+1 && (i+1)%(numRoads+2)!=0 && (i)%(numRoads+2)!=0 && i<roadLength-(numRoads+2)){
                    var rightbot = [~~(road[0]+roadWidth/4),~~(road[1]+roadWidth/4),0,0,0,0];
                    var righttop = [~~(road[0]+roadWidth/4),~~(road[1]-roadWidth/4),0,0,0,0];
                    var leftbot = [~~(road[0]-roadWidth/4),~~(road[1]+roadWidth/4),0,0,0,0];
                    var lefttop = [~~(road[0]-roadWidth/4),~~(road[1]-roadWidth/4),0,0,0,0];
                    if(roads[i+1][2]==1){
                        var right = true;

                        rightbot[3] = 1;
                        leftbot[3] = 1;

                        lefttop[4] = 1;
                        leftbot[3] = 1;
                        rightbot[2] = 1;
                        righttop[5] = 1;
                    }
                    if(roads[i-1][2]==1){
                        var left = true;

                        righttop[5] = 1;
                        lefttop[5] = 1;

                        lefttop[4] = 1;
                        leftbot[3] = 1;
                        rightbot[2] = 1;
                        righttop[5] = 1;
                    }
                    if(roads[i+(numRoads+2)][2]==1){
                        var bot = true;

                        lefttop[4] = 1;
                        leftbot[4] = 1;

                        lefttop[4] = 1;
                        leftbot[3] = 1;
                        rightbot[2] = 1;
                        righttop[5] = 1;
                    }
                    if(roads[i-(numRoads+2)][2]==1){
                        var top = true;

                        rightbot[2] = 1;
                        righttop[2] = 1;

                        lefttop[4] = 1;
                        leftbot[3] = 1;
                        rightbot[2] = 1;
                        righttop[5] = 1;
                    }
                    turns.push(rightbot,righttop,leftbot,lefttop);
                }


            }
        }
    //----------------------------------------------------------------------------------




    // SPAWNPOINT LIST --------------------------------------------------
        var spawnPoints = [];
        for(i=1;i<roadLength;i++){

            if(i!=(numRoads+1) && i!=(roadLength) && i!=(roadLength-1) && i!=(roadLength-(numRoads+2))){

                var road = roads[i];
                if(road[2]==1){
                    if(i<numRoads+1 && roads[i+numRoads+2][2]==1){
                        var spawnPoint = [~~(road[0]-roadWidth/4),~~(road[1]),"bot"];
                        spawnPoints.push(spawnPoint);
                    } else if(((i+1)%(numRoads+2))==0 && roads[i-1][2]==1){
                        var spawnPoint = [~~(road[0]),~~(road[1]-roadWidth/4),"left"];
                        spawnPoints.push(spawnPoint);
                    } else if(i>=(numRoads+2)){
                        if(i>roadLength-(numRoads+2) && roads[i-(numRoads+2)][2]==1){
                            var spawnPoint = [~~(road[0]+roadWidth/4),~~(road[1]),"top"];
                            spawnPoints.push(spawnPoint);
                        } else if(i<roadLength){
                            if((i)%(numRoads+2)==0 && roads[i+1][2]==1){
                                var spawnPoint = [~~(road[0]),~~(road[1]+roadWidth/4),"right"];
                                spawnPoints.push(spawnPoint);
                            }
                        }
                    }
                }
            }
        }
    //---------------------------------------------------------------------------




    // BACKGROUND GEN ------------------------------------------------------
        bkg.beginPath();
        bkg.fillStyle = 'green';
        bkg.rect(0,0,width,height);
        bkg.fill();

        for(i=0;i<roadLength;i++){
            var road = roads[i];
            if(road[2]==1){
                if(i>numRoads+1 && (i+1)%(numRoads+2)!=0 && i<roadLength-(numRoads+2)){
                    if(roads[i+1][2]==1){

                        bkg.beginPath();
                        bkg.fillStyle = 'grey';
                        bkg.rect(road[0]-roadWidth/2,road[1]-roadWidth/2,roads[i+1][0]-road[0]+roadWidth,roadWidth);
                        bkg.fill();

                    }
                }
                if((i+1)%(numRoads+2)!=0 && i<roadLength-(numRoads+2) && (i)%(numRoads+2)!=0){
                    if(roads[i+numRoads+2][2]==1){

                        bkg.beginPath();
                        bkg.fillStyle = 'grey';
                        bkg.rect(road[0]-roadWidth/2,road[1]-roadWidth/2,roadWidth,roads[i+numRoads+2][1]-road[1]+roadWidth);
                        bkg.fill();

                    }
                }


            }
        }
        for(i=0;i<roadLength;i++){
            var road = roads[i];
            if(road[2]==1){
                if(i>numRoads+1 && (i+1)%(numRoads+2)!=0 && i<roadLength-(numRoads+2)){
                    if(roads[i+1][2]==1){

                        bkg.beginPath();
                        bkg.setLineDash([5, 3]);
                        bkg.strokeStyle = 'white';
                        bkg.moveTo(road[0],road[1]);
                        bkg.lineTo(roads[i+1][0],roads[i+1][1]);
                        bkg.stroke();
                        bkg.fill();

                    }
                }
                if((i+1)%(numRoads+2)!=0 && i<roadLength-(numRoads+2) && (i)%(numRoads+2)!=0){
                    if(roads[i+numRoads+2][2]==1){

                        bkg.beginPath();
                        bkg.setLineDash([5, 3]);
                        bkg.strokeStyle = 'white';
                        bkg.moveTo(road[0],road[1]);
                        bkg.lineTo(roads[i+numRoads+2][0],roads[i+numRoads+2][1]);
                        bkg.stroke();
                        bkg.fill();

                    }
                }


            }
        }

    //-----------------------------------------------------------------------------------------





    // CAR CLASS ---------------------------------------------------------------------------    
        function Car(){

            this.size = ~~(roadWidth/4);


            var rand = getRandomInt(spawnPoints.length);

            try {
                var initDir = spawnPoints[rand][2];
            } catch {
                location.reload(); 
            }

            this.x = spawnPoints[rand][0];
            this.y = spawnPoints[rand][1];

            switch(initDir){
                case "right":
                    this.dir = 0;
                    break;
                case "bot":
                    this.dir = (Math.PI)/2;
                    break;
                case "top":
                    this.dir = -(Math.PI)/2;
                    break;
                case "left":
                    this.dir = (Math.PI);
                    break;
            }

            this.timer = 0;

            this.speedRand = 2+Math.random();

            this.dx = Math.cos(this.dir)*(2+this.speedRand);
            this.dy = Math.sin(this.dir)*(2+this.speedRand);

            this.col = "red";
            //this.col = '#'+(~~(Math.random()*16777215).toString(16);
        }
    // -----------------------------------------------------------------------------------------




    // CAR LIST INIT ---------------------------------------------------------------
        var cars = [];
        for(i = 0 ; i < numCars ; i++){
            var car = new Car();
            cars.push(car);
        }
    //-----------------------------------------------------------------------------------------




        var backRect = document.getElementById("background");
        var intervalTurns = roadWidth/15;
        var turnsLength = turns.length;




    // DRAW UPDATE ----------------------------------------------------------------------------
        function draw(){

            // if(getRandomInt(50)==1){
                // var car = new Car();
                // cars.push(car);
            // }

            anm.globalAlpha = 0.1;
            anm.drawImage(backRect,0,0);
            anm.globalAlpha = 1;

            var carsLength = cars.length;
            for(i = 0; i < cars.length ; i++){
                var car = cars[i];
                anm.fillStyle=car.col;
                anm.beginPath();

                car.x = ~~(car.x+car.dx);
                car.y = ~~(car.y+car.dy);

                anm.rect(~~(car.x-car.size/2), ~~(car.y-car.size/2), car.size, car.size);
                anm.fill();

                if(car.timer==0){
                    for(j=0;j<turnsLength;j++){
                        var turn = turns[j];
                        if(car.x<turn[0]+intervalTurns && car.x>turn[0]-intervalTurns && car.y<turn[1]+intervalTurns && car.y>turn[1]-intervalTurns){

                            var possibleTurn = [];
                            for(i=2;i<turnsLength;i++){
                                if(turn[i]==1){
                                    possibleTurn.push(i);
                                }
                            }

                            var change = getRandomInt(possibleTurn.length);

                            var newDir = possibleTurn[change];



                            switch(newDir){
                                case 2:
                                    car.dir = -(Math.PI)/2;
                                    car.dx = Math.cos(car.dir)*(2+car.speedRand);
                                    car.dy = Math.sin(car.dir)*(2+car.speedRand);
                                    break;
                                case 5:
                                    car.dir = Math.PI;
                                    car.dx = Math.cos(car.dir)*(2+car.speedRand);
                                    car.dy = Math.sin(car.dir)*(2+car.speedRand);
                                    break;
                                case 4:
                                    car.dir = (Math.PI)/2;
                                    car.dx = Math.cos(car.dir)*(2+car.speedRand);
                                    car.dy = Math.sin(car.dir)*(2+car.speedRand);
                                    break;
                                case 3:
                                    car.dir = 0;
                                    car.dx = Math.cos(car.dir)*(2+car.speedRand);
                                    car.dy = Math.sin(car.dir)*(2+car.speedRand);
                                    break;
                            }
                            car.timer = 3;
                            break;
                        }
                    }
                } else if(car.timer > 0){
                    car.timer-=1;
                }

                if(car.x<0 || car.x>width || car.y<0 || car.y>height){
                    var rand = getRandomInt(spawnPoints.length);

                    try {
                        var initDir = spawnPoints[rand][2];
                    } catch {
                        location.reload(); 
                    }

                    car.x = spawnPoints[rand][0];
                    car.y = spawnPoints[rand][1];
                    //cars.splice(i,1);
                    switch(initDir){
                        case "right":
                            car.dir = 0;
                            car.dx = Math.cos(car.dir)*(2+car.speedRand);
                            car.dy = Math.sin(car.dir)*(2+car.speedRand);
                            break;
                        case "bot":
                            car.dir = (Math.PI)/2;
                            car.dx = Math.cos(car.dir)*(2+car.speedRand);
                            car.dy = Math.sin(car.dir)*(2+car.speedRand);
                            break;
                        case "top":
                            car.dir = -(Math.PI)/2;
                            car.dx = Math.cos(car.dir)*(2+car.speedRand);
                            car.dy = Math.sin(car.dir)*(2+car.speedRand);
                            break;
                        case "left":
                            car.dir = (Math.PI);
                            car.dx = Math.cos(car.dir)*(2+car.speedRand);
                            car.dy = Math.sin(car.dir)*(2+car.speedRand);
                            break;
                    }
                }
            }
        }
    //---------------------------------------------------------------------------------------


        setInterval(draw, 30);



    // KEYBINDS ------------------------------------------------------------------------
        document.body.onkeyup = function(e){
            if(e.keyCode == 32){
                klaxon.play();
            }
        }
    //-------------------------------------------------------------------------------

        function getRandomInt(max) {
            return ~~(Math.random() * max);
        }           
    </script>

</body>
</html>

I'm pretty sure that the lag comes from the constant check for turns (as there are a lot) in the draw function but I want to ensure that it is the problem.

Also, I tried to reduce lag using ~~ instead of Math.floor(), variables for the list length, and breaks but I'm not sure it makes a big difference, does it ?

I'm aware that my request is paintful and not very interesting, so if you have any question about the code or the request, I'll be happy to answer you as quick as possible.

Thanks in advance and have a good day :)

sad
  • 26
  • 6
  • 1
    https://stackoverflow.com/questions/313893/how-to-measure-time-taken-by-a-function-to-execute check this post on how to mesure blocks of code . A good candidate to improve is the triple for block – Panos K May 07 '18 at 09:09
  • Thanks, I tried the performance.now() method and works fine. Finally, it shows me the same result as the performance test from Firefox : 2ms for the complete darw() function. So I can't figure out what's making the cars not moving in a fluid way. Should I make another post to ask why my code isn't fluid ? – sad May 07 '18 at 09:31

1 Answers1

1

You can see where the code is spending its time using the Developer Tools in any major modern browser. Select the devtools from the menu, or press Ctrl+Shift+I or F12 (or Cmd+Shift+I on Mac OS). There's likely a Performance or Profiling tab (for instance, in Chrome, it's Performance). You can use the features there to run your code, and see what functions it's spending the most time in, etc. Your browser's devtools will likely be reasonably well-documented. There's an article on using Chrome's Performance tab here.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • I already tried to look in there but my setInterval function is always taking less than 2ms which is I think more than enough. Also, I'm thinking, maybe it's just a coding issue and not a performance issue but I can't see where it could be.. – sad May 07 '18 at 09:15