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 :)