Game froze in <canvas> js - javascript
I`m writing a pong game on js and html using canvas. Besides main body of the game I have the starting menu with "play button"(all the buttons are just images i counted their coordinates and just use ), then next menu where user chooses the background and then eventually game itself and then after reaching 3 points by user or computer the game ends and there's a win or fail window appering where you can press the "back to menu" button and go back to the very first menu with "play button" and start everything again. Program does everything okay when you first launch it but after you for example lose and go back to main menu, choose the background, game starts aaaaaaand when ball reaches 2 points the game get frozen and sometimes after minute or so somehow loads the menu with backgrounds. I really have no idea what's wrong and why it gets frozen and strangely on the second time you play. Please help me ;((
PS. Im just a beginner idk how to insert the images here in stackoverflow
const canvas = document.getElementById("pongping");
const context = canvas.getContext('2d');
/////////////1)function to draw the rect
function drawPryamokutnik(x,y,w,h,color) {
context.fillStyle = color;
context.fillRect(x,y,w,h);
}
//////////////////////2)рисуем круг ///////////////////////////////////////////////////////////////////////////////
function drawKolo(x,y,radius,color) {
context.fillStyle = color;
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2, false);
context.closePath();
context.fill();
}
///////////////////////3)drawText/////////////////////////////////////////////
function drawText(text,x,y,color){
context.fillStyle = color;
context.font = "80px Arial";
context.fillText(text,x,y);
}
///////////////////////4)создаем пластинку пльзователя//////////////////////////////////////////////////////////////
const rectleft = {
x:20,
y:canvas.height/2-100,
width: 25,
height: 200,
color: "WHITE",
score:0
};
///////////////////////5)создаем пластинку компьютера//////////////////////////////////////////////////////////////
const rectright = {
x:canvas.width-45,
y:canvas.height/2-100,
width: 25,
height: 200,
color: "WHITE",
score:0
};
///////////////////////6)создаем шайбу/////////////////////////////////////////////////////////////////////////////
const ball = {
x: canvas.width/2,
y: canvas.height/2,
speed: 5,
radius: 20,
velocityX: 5,
velocityY: 5,
colour:"WHITE"
};
//////////////////////8)создаем разделительную сетку///////////////////////////////////////////////////////////////
const net = {
x:canvas.width/2-1,
y:0,
width: 2,
height:10,
color:"WHITE"
};
//////////////////////9)создаем функцию сетки/////////////////////////////////////////////////////////////////////
function drawNet(){
for(let i=0; i<=canvas.height;i+=15)
{
drawPryamokutnik(net.x,net.y+i,net.width,net.height,net.color)
}
}
//drawPryamokutnik(0,0,canvas.width, canvas.height, "BLACK");
//drawText("so you wanna die young?",200,300,"WHITE");
canvas.addEventListener("mousemove",moovePaddle);
function moovePaddle(etv){
let rect=canvas.getBoundingClientRect();
rectleft.y=etv.clientY-rect.top-rectleft.height/2;
}
function update(){
ball.x += ball.velocityX;
ball.y += ball.velocityY;
let computerLevel= 0.1;
rectright.y+=(ball.y-(rectright.y+rectright.height/2))*computerLevel;
if (ball.y + ball.radius > canvas.height || ball.y - ball.radius < 0) {
ball.velocityY = -1 * ball.velocityY;
}
let player = (ball.x>canvas.width/2)? rectright:rectleft;
if(colission(ball,player)){
let collidePoint = ball.y - (player.y + player.height / 2);
collidePoint = collidePoint /(player.height / 2);
let angleRead = Math.PI/4 * collidePoint;
let direction = (ball.x < canvas.width / 2) ? 1 : -1;
ball.velocityX = direction * ball.speed * Math.cos(angleRead);
ball.velocityY = direction * ball.speed * Math.sin(angleRead);
ball.speed += 1;
}
if (ball.x - ball.radius < 0 ){
rectright.score++;
ResetBall();
}
else if(ball.x+ball.radius>canvas.width)
{rectleft.score++;
ResetBall();}
}
function ResetBall() {
ball.x=canvas.width/2;
ball.y=canvas.height/2;
ball.speed=5;
ball.velocityX=-ball.velocityX;
}
function colission(b,p){
b.top=b.y-b.radius;
b.bottom=b.y+b.radius;
b.left=b.x-b.radius;
b.right=b.x+b.radius;
p.top=p.y;
p.bottom=p.y+p.height;
p.left=p.x;
p.right=p.x+p.width;
return b.right>p.left && b.bottom>p.top && b.left<p.right && b.top < p.bottom;
}
function render(){
const back = new Image();
if(backchek===1)
{back.src = "back1.png";}
if(backchek===2)
{back.src = "back2.png";}
if(backchek===3)
{back.src = "back3.png";}
context.drawImage(back, 0, 0);
//drawPryamokutnik(0,0,canvas.width,canvas.height,"BLACK");
drawNet();
drawText(rectleft.score,canvas.width/4,canvas.height/5,"WHITE");
drawText(rectright.score,3*canvas.width/4,canvas.height/5,"WHITE");
drawPryamokutnik(rectleft.x,rectleft.y,rectleft.width,rectleft.height,rectleft.color);
drawPryamokutnik(rectright.x,rectright.y,rectright.width,rectright.height,rectright.color);
drawKolo(ball.x,ball.y,ball.radius,ball.colour);
}
let slide;
function game() {
slide=1;
menu();
if(startcheck===true)
{backchoose();
slide=2;
if(backchek===1||backchek===2||backchek===3)
{render();
update();
if(rectright.score>2)
{
losing();
slide=3;
// startcheck=false;
if(backagain===true)
{slide=1;
game();
}}
if(rectleft.score>2)
{//startcheck=false;
winning();
slide=3;
if(backagain===true)
{slide=1;
game();
}}
}
}
}
let framePerSecond=65;
setInterval(game,1000/framePerSecond);
function menu(){
const menuu = new Image();
menuu.src = "menu.png";
context.drawImage(menuu, 0, 0);
}
function losing(){
const lose = new Image();
lose.src = "lose.png";
context.drawImage(lose, 0, 0);
}
function winning(){
const win = new Image();
win.src = "win.png";
context.drawImage(win, 0, 0);
}
function backchoose(){
const backk = new Image();
backk.src = "back choose .png";
context.drawImage(backk, 0, 0);
}
let backchek;
let backagain=false;
let startcheck=false;
canvas.addEventListener("mousedown", clicked, false);
function clicked(e){
e.preventDefault();
let rectt=canvas.getBoundingClientRect();
const x = e.clientX;
const y = e.clientY - rectt.top;
let xprtr;
let yprtr;
let hipot;
let yby;
let xby;
let yey;
let xex;
if(x===496 && y===471&& slide===1)
{startcheck=true;
//backagain=false;
}
if(x>496){xprtr=x-496;}
if(x<496){xprtr=496-x;}
if(y>471){yprtr=y-471;}
if(y<471){yprtr=471-y;}
hipot=yprtr*yprtr+xprtr*xprtr;
if(hipot<=10609&&slide===1)
{startcheck=true;}
if(slide===2&&(y>217&&y<430&&x>75&&x<404)){backchek=1;}
if(slide===2&&(y>217&&y<430&&x>585&&x<924)){backchek=2;}
if(slide===2&&(y>508&&y<773&&x>344&&x<675)){backchek=3;}
if(slide===3&&y>519&&(y<702&&x>224&&x<768))
{backagain=true;
backchek=0;
startcheck=false;
rectright.score=0;
rectleft.score=0;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Pong20</title>
</head>
<body>
<canvas id="pongping" width="1000" height="800"></canvas>
<script src="pongping.js"></script>
</body>
Does anything pop up in the console? That might give some hint as to what is going wrong.
I noticed that in your game() function you are recursively calling game(). This may cause a recursive overflow.
At the same time, you have setInterval calling game again and again. I recommend you delete the lines:
let framePerSecond=65;
setInterval(game,1000/framePerSecond);
and replace them with
window.requestAnimationFrame(game)
This will still initialize your game properly, but it will also not block the thread, so users can still interact with the page as expected. It may not keep the proper frame rate that you're expecting, but that function passes a timestamp to the function it calls, so you could keep track of the time since last call and update movements based on that. Check out this example.
then inside your game function, replace every call to
game()
with
window.requestAnimationFrame(game)
This will hopefully stop your game from crashing.
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
Related
How to direct sound in p5.js - Binaural sound
I' m try trying to direct the sound, how to direct the sound where the circle is? Taking into account that the circle will have to change position (I would like to replace the opposite sound with a binaural sound, and I don't know if there is an easier way to do it) The code only works on mobile. thanks in andavance. https://editor.p5js.org/matteomuiafrate1999/sketches/P7XX_n-RS?fbclid=IwAR1D3dGhW_zvqlRuU-_kTAP-mzrg9urKv1bSXu9Qd72WA-Swu6lVHtCwoU8
It's not entirely clear what you are asking, but if you want to adjust how much of a particular sound comes from which channel (Right vs Left) based on spacial arrangement you can use a Panner3D. let osc, pan; let playing = false; let direction = 0; function setup() { createCanvas(windowWidth, windowHeight); angleMode(DEGREES); osc = new p5.Oscillator('sine'); osc.freq(261.625565); osc.disconnect(); pan = new p5.Panner3D(); pan.process(osc); pan.setFalloff(Math.min(width, height) / 2, 1); // By default the sound source will be omni-directional pan.panner.coneInnerAngle = 60; pan.panner.coneOuterAngle = 360; // You can also adjust how quite the sound outside the // outer code is with coneOuterGain which defaults to 0 } function doubleClicked() { if (!playing) { playing = true; osc.start(); osc.amp(0.3, 0.1); } else { playing = false; osc.amp(0, 0.1); } } function mouseMoved() { if (pan) { pan.set(mouseX - width / 2, 0, mouseY - height / 2, 0.1); } } function mouseDragged() { mouseMoved(); } function draw() { background(100); circle(width / 2, height / 2, 20); arc(mouseX, mouseY, 40, 40, direction - 20, direction + 20); if (mouseIsPressed) { direction = (direction + 3) % 360; pan.orient(cos(direction), 0, sin(direction), 0.1); } } <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
Create shapes by for loop in canvas
EDIT:i will post all my code the html and js,and excuse me for too many comments I am trying to create rectangles in canvas by for loop (there is input user) and I want to access them in another function to do some stuff, the main problem is how to access the shapes's name after loop I have tried this but when i call them in another function it gives me, undefined "object name" var canvas = document.querySelector('canvas'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; var c = document.getElementById("myCanvas"); //drawing the base off the towers var base_twr1 = c.getContext("2d"); base_twr1.beginPath(); base_twr1.moveTo(550, 500); base_twr1.lineTo(300, 500); base_twr1.lineWidth = 10; base_twr1.strokeStyle = '#ff0000'; base_twr1.closePath(); base_twr1.stroke(); var base_twr2 = c.getContext("2d"); base_twr2.beginPath(); base_twr2.moveTo(900, 500); base_twr2.lineTo(650, 500); base_twr2.closePath(); base_twr2.stroke(); var base_twr3 = c.getContext("2d"); base_twr3.beginPath(); base_twr3.moveTo(1250, 500); base_twr3.lineTo(1000, 500); base_twr3.closePath(); base_twr3.stroke(); //drawing the towers var twr1 = c.getContext("2d"); twr1.beginPath(); twr1.moveTo(430, 300); twr1.lineTo(430, 500); twr1.closePath(); twr1.stroke(); var twr2 = c.getContext("2d"); twr2.beginPath(); twr2.moveTo(780, 300); twr2.lineTo(780, 500); twr2.closePath(); twr2.stroke(); var twr3 = c.getContext("2d"); twr3.beginPath(); twr3.moveTo(1130, 300); twr3.lineTo(1130, 500); twr3.closePath(); twr3.stroke(); //array to know each tower what contains //to avoid collisions var disks_in_twrs = []; var twr1_holder = []; var twr2_holder = []; var twr3_holder = []; //start function check the user input //and call another function if everthing //is fine function btn_start() { disks_number = document.getElementById("disk_input").value; disks_number = parseInt(disks_number); if (disks_number > 0) { if (disks_number < 8) put_disks(disks_number); } else alert('write number'); } var width_disks_start = 305; var height_disks_start = 490; var disk_width = 220; function put_disks(disks) { for (i = 0; i < disks; i++) { // var r = Math.floor((Math.random() * 256)); // var g = Math.floor((Math.random() * 256)); // var b = Math.floor((Math.random() * 256)); str1 = "disk"; width_disks_start = width_disks_start + 10; height_disks_start = height_disks_start - 20; disk_width = disk_width - 30; // eval("disks_in_twrs.push(str1 + i)" ); // disks_in_twrs[i]=c.getContext("2d"); // disks_in_twrs[i].rect((Math.random)*100,(Math.random)*100,150,100); // disks_in_twrs[i].stroke(); // alert(disks_in_twrs); twr1_holder.push(str1 + i); // ctx.fillStyle = 'rgb(' + r + ',' + g + ', ' + b + ')'; // alert(str1 + i); //twr1_holder[i] = c.getContext("2d"); eval("var disk"+i+"= c.getContext('2d');"); // twr1_holder[i].rect(width_disks_start, height_disks_start, disk_width, 20); eval("disk"+i+".rect(width_disks_start, height_disks_start, disk_width, 20);"); // twr1_holder[i].strokeStyle = "black"; eval("disk"+i+".strokeStyle = 'black';"); // twr1_holder[i].stroke(); eval("disk"+i+".stroke();"); // alert(disk1.toSource()); } } function hide_me(){ alert("byeeeeeeeeeeeeeeeee"); twr1.fillRect(430, 500, 250, 250); // disk2.rect(515, 51, 6, 20); // disk2.strokeStyle = 'red'; } <!DOCTYPE html> <html> <head> <title>tower of Hanoi</title> <style type="text/css"> canvas{ border : 1px solid black; } </style> </head> <body> <label>how many disk do you want ?</label> <input type="text" id="disk_input"> <button id="start" onclick="btn_start()">start</button> <label>note that maximum disk is 8 :P</label> <button id="make_hidden" onclick="hide_me()" >make me hide</button> <canvas id="myCanvas" > </canvas> <script src="tower.js"> </script> </body> </html>
There's a lot going on here! I recommend attacking each issue in your code separately and building up understanding gradually, because this is an application that requires a lot of different components (DOM manipulation/event handlers, JS canvas, objects/arrays/loops, design, etc). If you're uncomfortable with any of these concepts, pick one area (such as DOM manipulation) and spend time working on simple, understandable examples, then apply what you learned to the main application. Firstly, almost always avoid eval entirely. Mozilla says never to use it! If you're using it, it probably means your design has gone haywire somewhere along the line, which I would contend is the case here. As for event handlers and document manipulation, I recommend avoiding onclick. Adding event listeners in your script can take care of the job; you'll likely be listening for clicks on the canvas to enable interaction later on. Next: using canvas. You generally only need to retrieve the context once per application, not before each drawing. Your drawing code looks good other than this, except that it's not very DRY, which is usually a signal to redesign. The hardest part is designing your code to meet your goals, which I'm not entirely clear on. Are you making an interactive Towers of Hanoi app, or one that simply animates a solver algorithm and requires no user input? Either way, I opted to use object constructors to represent Towers and Disks. Using arrays to hold these objects means you identify towers and disks by their position in an array rather than evaling a string name. Whenever you want to perform an action on your towers, such as drawing them, all you need to do is loop through the towers and call draw on each one. Later, when it comes to handling user input or writing a solver algorithm, it should be fairly easy to manipulate these arrays to suit your needs (e.g., figuring out which disk was clicked on, moving disks between towers, etc). Keep in mind the below example is just a quick sketch to get you going and may not follow best design principles or ones that meet your needs. For example, I've hard-coded most drawing coordinate values, so it's non-responsive, so many exercises are left for the reader to improve on. const Disk = function(width, color) { this.width = width; this.color = color; }; const Tower = function(x, disks) { this.x = x; this.disks = []; this.width = 20; }; Tower.prototype.draw = function(c, ctx) { ctx.lineWidth = this.width; ctx.strokeStyle = "#000"; ctx.beginPath(); ctx.moveTo(this.x, 0); ctx.lineTo(this.x, c.height); ctx.stroke(); this.disks.forEach((e, i) => { ctx.fillStyle = e.color; ctx.fillRect( this.x - e.width / 2, c.height - (i + 1) * this.width, e.width, this.width ); }); }; const draw = (c, ctx, towers) => { ctx.clearRect(0, 0, c.width, c.height); towers.forEach(t => t.draw(c, ctx)); }; const initialize = disks => { const towers = [ new Tower(c.width / 5), new Tower(c.width / 2), new Tower(c.width - c.width / 5) ]; for (let i = disks; i > 0; i--) { towers[0].disks.push( new Disk(i * 30, `hsl(${Math.random() * 360}, 50%, 50%`) ); } return towers; }; document.getElementById("initialize-form") .addEventListener("submit", e => { e.preventDefault(); towers = initialize(parseInt(e.target.elements[0].value), towers); draw(c, ctx, towers); }); document.getElementById("btn-hide").addEventListener("click", e => document.getElementById("menu").style.display = "none" ); const c = document.getElementById("hanoi"); c.width = 600; c.height = 200; const ctx = c.getContext("2d"); let towers; body { margin: 0; } #hanoi { padding: 0.5em; } #initialize-form { display: inline-block; } #menu { padding: 0.5em; display: inline-block; } <div id="menu"> <form id="initialize-form"> <label>Enter disks:</label> <input type="number" min="1" max="8" value="6"> <button type="submit">start</button> </form> <button id="btn-hide">hide</button> </div> <canvas id="hanoi"></canvas>
For what you are trying to do you should consider using a canvas library, maybe Konva: https://konvajs.github.io/ Here is an example: <script src="https://cdn.rawgit.com/konvajs/konva/2.1.7/konva.min.js"></script> <div id="container"></div> <script> function KonvaRect(x, y, fill, draggable) { return new Konva.Rect({ x: x, y: y, width: 50, height: 50, fill: fill, stroke: 'black', strokeWidth: 4, draggable: draggable }); } var boxes = []; boxes.push(KonvaRect(50, 10, '#00D2FF', true)); boxes.push(KonvaRect(200, 10, '#0000FF', true)); boxes.push(KonvaRect(125, 10, '#FF0000', false)); var layer = new Konva.Layer(); boxes.forEach(function(b) { layer.add(b) }); var stage = new Konva.Stage({ container: 'container', width: 600, height: 170 }); stage.add(layer); function moveCenter() { boxes.forEach(function(b) { b.move({ x:0, y: Math.random() * 10 }) }); layer.batchDraw(); } boxes[0].on('mouseover', function() { moveCenter(); }); </script> On this example I put 3 boxes in an array and when we detect the mouse over the light blue box all boxes move randomly down, also both blue boxes you can click and drag around the canvas. And for the record there are many many other libraries out there...
Make identical objects move independently in Javascript Canvas
So I am trying to make a simple space game. You will have a ship that moves left and right, and Asteroids will be generated above the top of the canvas at random X position and size and they will move down towards the ship. How can I create Asteroid objects in seperate positions? Like having more than one existing in the canvas at once, without creating them as totally seperate objects with seperate variables? This sets the variables I would like the asteroid to be created on. var asteroids = { size: Math.floor((Math.random() * 40) + 15), startY: 100, startX: Math.floor((Math.random() * canvas.width-200) + 200), speed: 1 } This is what I used to draw the asteroid. (It makes a hexagon shape with random size at a random x coordinate) function drawasteroid() { this.x = asteroids.startX; this.y = 100; this.size = asteroids.size; ctx.fillStyle = "#FFFFFF"; ctx.beginPath(); ctx.moveTo(this.x,this.y-this.size*0.5); ctx.lineTo(this.x+this.size*0.9,this.y); ctx.lineTo(this.x+this.size*0.9,this.y+this.size*1); ctx.lineTo(this.x,this.y+this.size*1.5); ctx.lineTo(this.x-this.size*0.9,this.y+this.size*1); ctx.lineTo(this.x-this.size*0.9,this.y); ctx.fill(); } I included ALL of my code in this snippet. Upon running it, you will see that I currently have a ship that moves and the asteroid is drawn at a random size and random x coordinate. I just need to know about how to go about making the asteroid move down while creating other new asteroids that will also move down. Thank You for all your help! I am new to javascript. // JavaScript Document ////// Variables ////// var canvas = {width:300, height:500, fps:30}; var score = 0; var player = { x:canvas.width/2, y:canvas.height-100, defaultSpeed: 5, speed: 10 }; var asteroids = { size: Math.floor((Math.random() * 40) + 15), startY: 100, startX: Math.floor((Math.random() * canvas.width-200) + 200), speed: 1 } var left = false; var right = false; ////// Arrow keys ////// function onkeydown(e) { if(e.keyCode === 37) { left = true; } if(e.keyCode === 39) { right = true; } } function onkeyup(e) { if (e.keyCode === 37) { left = false; } if(e.keyCode === 39) { right = false; } } ////// other functions ////// //function to clear canvas function clearCanvas() { ctx.clearRect(0,0,canvas.width,canvas.height); } // draw the score in the upper left corner function drawscore(score) { var score = 0; ctx.fillStyle = "#FFFFFF"; ctx.fillText(score,50,50); } // Draw Player ship. function ship(x,y) { var x = player.x; var y = player.y; ctx.fillStyle = "#FFFFFF"; ctx.beginPath(); ctx.moveTo(x,y); ctx.lineTo(x+15,y+50); ctx.lineTo(x-15,y+50); ctx.fill(); } // move player ship. function moveShip() { document.onkeydown = onkeydown; document.onkeyup = onkeyup; if (left === true && player.x > 50) { player.x -= player.speed; } if (right === true && player.x < canvas.width - 50) { player.x += player.speed; } } // Draw Asteroid function drawasteroid() { this.x = asteroids.startX; this.y = 100; this.size = asteroids.size; ctx.fillStyle = "#FFFFFF"; ctx.beginPath(); ctx.moveTo(this.x,this.y-this.size*0.5); ctx.lineTo(this.x+this.size*0.9,this.y); ctx.lineTo(this.x+this.size*0.9,this.y+this.size*1); ctx.lineTo(this.x,this.y+this.size*1.5); ctx.lineTo(this.x-this.size*0.9,this.y+this.size*1); ctx.lineTo(this.x-this.size*0.9,this.y); ctx.fill(); } // move Asteroid function moveAsteroid() { //don't know how I should go about this. } // update setInterval (update, 1000/canvas.fps); function update() { // test collisions and key inputs moveShip(); // redraw the next frame of the animation clearCanvas(); drawasteroid(); drawscore(); ship(); } <!doctype html> <html> <head> <meta charset="UTF-8"> <title>My Game</title> <script src="game-functions.js"></script> <!-- <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script> --> </head> <body> <canvas id="ctx" width="300" height="500" style="border: thin solid black; background-color: black;"></canvas> <br> <script> ////// Canvas setup ////// var ctx = document.getElementById("ctx").getContext("2d"); </script> </body> </html>
You want to make the creation of asteroids dynamic...so why not set up a setInterval that gets called at random intervals as below. You don't need a separate declaration for each Asteroids object you create. You can just declare a temporary one in a setInterval function. This will instantiate multiple different objects with the same declaration. Of course you need to store each object somewhere which is precisely what the array is for. You also have to make sure that asteroids get removed from the array whenever the moveAsteroid function is called if they are off of the canvas. The setInterval function below should be called on window load and exists alongside your main rendering setInterval function. You are also going to have to change your moveAsteroid function a bit to be able to point to a specific Asteroids object from the array. You can do this by adding the Asteroids object as a parameter of the function or by making the function a property of the Asteroids class and using this. I did the latter in the example below. var astArray = []; var manageAsteroidFrequency = 2000; var Asteroids { X: //something Y://something speed:1 move: function() { this.X -= speed; } } var mainRenderingFunction = setInterval( function() { for (var i = astArray.length-1 ; i > -1; i --){ if(astArray[i].Y < 0){ astArray.splice(i, 1) }else{ astArray[i].move; } } }, 40); var manageAsteroids = setInterval( function () { if (astArray.length < 4){ var tmpAst = new Asteroids(); astArray.push(tmpAst); } manageAsteroidFrequency = Math.floor(Math.random()*10000); }, manageAsteroidFrequency);
How to move image on canvas using arrow keys in javascript
I've tried a few different ways that I have seen on here, but I can't quite get my image to move. Whenever I try adapting code for arrow key presses, it just seems to make my canvas shrink and my player model (spaceperson) disappear. here is the "drawing board" I keep returning to, and what I have so far. // Get the canvas and context var canvas = document.getElementById("space"); var ctx = canvas.getContext("2d"); canvas.width = 1920; canvas.height = 700; // Create the image object var spaceperson = new Image(); // Add onload event handler spaceperson.onload = function () { // Done loading, now we can use the image ctx.drawImage(spaceperson, 280, 300); }; // artwork by Harrison Marley (using make8bitart.com) spaceperson.src = "http://i.imgur.com/Eh9Dpq2.png";` I am quite new to javascript, and I am just trying to work out how I can move the specperson image using arrow keys. I was trying to make a class for space person to access their x,y values, but I can't seem to draw the image without using .onload
here a more complete example: //just a utility function image(url, callback){ var img = new Image(); if(typeof callback === "function"){ img.onload = function(){ //just to ensure that the callback is executed async setTimeout(function(){ callback(img, url) }, 0) } } img.src = url; return img; } //a utility to keep a value constrained between a min and a max function clamp(v, min, max){ return v > min? v < max? v: max: min; } //returns a function that can be called with a keyCode or one of the known aliases //and returns true||false wether the button is down var isKeyDown = (function(aliases){ for(var i=256, keyDown=Array(i); i--; )keyDown[i]=false; var handler = function(e){ keyDown[e.keyCode] = e.type === "keydown"; e.preventDefault(); //scrolling; if you have to suppress it }; addEventListener("keydown", handler, false); addEventListener("keyup", handler, false); return function(key){ return(true === keyDown[ key in aliases? aliases[ key ]: key ]) } })({ //some aliases, to be extended up: 38, down: 40, left: 37, right: 39 }); // Get the canvas and context var canvas = document.getElementById("space"); canvas.width = 1920; canvas.height = 700; var ctx = canvas.getContext("2d"); //the acutal image is just a little-part of what defines your figue var spaceperson = { image: image("//i.imgur.com/Eh9Dpq2.png", function(img){ spaceperson.width = img.naturalWidth; spaceperson.height = img.naturalHeight; //start the rendering by calling update update(); }), //position x: 60, y: 310, width: 0, height: 0, speed: 200 // 200px/s }; var lastCall = 0; //to calculate the (real) time between two update-calls //the render-fucntion function update(){ //taking account for (sometimes changing) framerates var now = Date.now(), time = lastCall|0 && (now-lastCall)/1000; lastCall = now; requestAnimationFrame(update); var sp = spaceperson, speed = sp.speed; //checking the pressed buttons and calculates the direction //two opposite buttons cancel out each other, like left and right var dx = (isKeyDown('right') - isKeyDown('left')) * time, dy = (isKeyDown('down') - isKeyDown('up')) * time; //fix the speed for diagonals if(dx && dy) speed *= 0.7071067811865475; // * 1 / Math.sqrt(2) if(dx) { //there is some movement on the x-axes sp.x = clamp( //calculate the new x-Position //currentPos + direction * speed sp.x + dx * sp.speed, //restraining the result to the bounds of the map 0, canvas.width - sp.width ); } //same for y if(dy) sp.y = clamp(sp.y + dy * sp.speed, 0, canvas.height - sp.height); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(sp.image, sp.x, sp.y); } Edit: A quick question (I hope); if I was to later add other objects, would I check for collisions in update()? This is still just a very basic example. The main purpose of the update()-function should be to work as the main event-loop. To trigger all Events that have to happen each frame in the order they have to happen. var lastCall = 0; function update(){ //I always want a next frame requestAnimationFrame(update); //handle timing var now = Date.now(), //time since the last call in seconds //cause usually it's easier for us to think in //tems like 50px/s than 0.05px/ms or 0.8333px/frame time = lastCall|0 && (now-lastCall) / 1000; lastCall = now; movePlayer(time); moveEnemies(time); moveBullets(time); collisionDetection(); render(); } function render(){ ctx.clear(0, 0, canvas.width, canvas.height); drawBackground(ctx); for(var i=0; i<enemies.length; ++i) enemies[i].render(ctx); player.render(ctx); } Not saying that you have to implement all these functions now, but to give you an idea of a possible structure. Don't be scared to break big tasks (functions) up into subtasks. And it might make sense to give each enemy a move()-function so you can implement different movement-patterns per enemy, or you say that the pattern is (and will be) all the same for each enemy, parameterized at the best, then you can handle that in a loop. Same thing for rendering, as I'm showing in the last part of code.
Here's some slightly modified code from a game I was noodling around with a while back. If you want to see more code, check out the complete JS on GitHub. The game is incomplete but you should gather some helpful clues as to how to move an image around the canvas. var spaceperson = { speed: 256, other_stuff: '' }, keysDown = [], update, main; addEventListener("keydown", function (e) { keysDown[e.keyCode] = true; }, false); update = function (modifier) { if (38 in keysDown && spaceperson.y > 0) { // UP spaceperson.y -= spaceperson.speed * modifier; } if (40 in keysDown && spaceperson.y < CANVAS_HEIGHT - SPACEPERSON_HEIGHT) { // DOWN spaceperson.y += spaceperson.speed * modifier; } if (37 in keysDown && spaceperson.x > 0) { // LEFT spaceperson.x -= spaceperson.speed * modifier; } if (39 in keysDown && spaceperson.x < CANVAS_WIDTH - SPACEPERSON_WIDTH) { // RIGHT spaceperson.x += spaceperson.speed * modifier; } }
I'm not sure but i think this can help. // Get the canvas and context var canvas = document.getElementById("space"); var ctx = canvas.getContext("2d"); canvas.width = 1920; canvas.height = 700; var x = 280; var y = 300; // Create the image object var spaceperson = new Image(); spaceperson.addEventListener("keypress", press); // Add onload event handler spaceperson.onload = function () { // Done loading, now we can use the image ctx.drawImage(spaceperson, x, y); }; function press(event) { if(event.keyCode == 37) {//LEFT x = x - 1; } else if(event.keyCode == 38) {//UP y = y - 1; } else if(event.keyCode ==39) {//RIGHT x = x + 1; } else if(event.keyCode == 40) {//DOWN y = y + 1; } draw(); } function draw(){ ctx.drawImage(spaceperson,x,y); } // artwork by Harrison Marley (using make8bitart.com) spaceperson.src = "http://i.imgur.com/Eh9Dpq2.png";
I found a solution! // Get the canvas and context var canvas = document.getElementById("space"); var ctx = canvas.getContext("2d"); canvas.width = 1920; canvas.height = 700; var xPos = 60; var yPos = 310; // Create the image object var spaceperson = new Image(); // Add onload event handler spaceperson.onload = function () { // Done loading, now we can use the image ctx.drawImage(spaceperson, xPos, yPos); }; function move(e){ if(e.keyCode==39){ xPos+=10; } if(e.keyCode==37){ xPos-=10; } if(e.keyCode==38){ yPos-=10; } if(e.keyCode==40){ yPos+=10; } canvas.width=canvas.width; ctx.drawImage(spaceperson, xPos, yPos); } document.onkeydown = move; // artwork by Harrison Marley spaceperson.src = "http://i.imgur.com/Eh9Dpq2.png";
How to make javascript canvas draw faster?
I have the following code to display an ECG. I use the canvas to draw the graph background (each grid of 2.5 mm dimension). Later I'm taking the y coordinates from an array array_1 (x coordinates are calculated within the program). The problem with this approach is it will take around 40 seconds to plot the entire graph since there are 1250 values within array array_1. What I could do is I could do the plotting part within a loop in which case, the entire graph is plotted as soon as the page is loaded. But, I need the plotting to happen over the course of 5 seconds. Not more. Not less. How would I alter the code to do this? Please help. <!DOCTYPE html> <html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <canvas id="canvas" width="1350" height="1300" style="background-color: white;"></canvas> <script type='text/javascript'> var canvas = document.getElementById("canvas"); var ctxt = canvas.getContext("2d"); var n1 = 1; var n1_x=49; //Graph x coordinate starting pixel. var n1_y=72;//Graph y coordinate starting pixel. var array_1 = []// array from which y coordinates are taken. Has 1250 elements var ctx = canvas.getContext("2d"); var x=0; var y=0; var Line_position=-1; while(x<=1350)//graph width { ctxt.lineWidth = "0.5"; Line_position=Line_position+1; if(Line_position%5==0) { ctxt.lineWidth = "1.5"; } ctxt.strokeStyle = "black"; ctxt.beginPath(); ctxt.moveTo(x, 0); ctxt.lineTo(x, 1300); ctxt.stroke(); x=x+9.43; } Line_position=-1; while(y<=1300)//graph height { ctxt.lineWidth = "0.5"; Line_position=Line_position+1; if(Line_position%5==0) { ctxt.lineWidth = "1.5"; } ctxt.strokeStyle = "black"; ctxt.beginPath(); ctxt.moveTo(0, y); ctxt.lineTo(1350,y); ctxt.stroke(); y=y+9.43; } drawWave(); function drawWave() { requestAnimationFrame(drawWave); ctx.lineWidth = "1"; ctx.strokeStyle = 'blue'; ctx.beginPath(); ctx.moveTo(n1_x- 1, n1_y+array_1[n1-1]);//move to the pixel position ctx.lineTo(n1_x, n1_y+array_1[n1]);//Draw to the pixel position ctx.stroke(); n1_x=n1_x+0.374;//Incrementing pixel so as to traverse x axis. n1++; } </script> </body> </html> Here is the array: array_1 = [69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,72,72,72,72,72,72,72,73,73,74,74,74,74,74,74,74,73,73,73,73,74,74,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,74,74,74,73,73,73,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,71,72,72,72,73,73,73,72,72,72,73,73,73,74,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,72,73,73,73,72,72,72,71,101,71,70,70,70,69,68,68,67,67,66,66,67,67,69,70,72,72,72,73,73,74,73,73,73,73,73,73,73,73,73,74,76,77,76,70,57,40,22,11,11,22,40,57,69,73,73,71,71,71,72,72,73,73,74,74,74,73,72,72,72,72,72,72,72,72,72,72,72,72,71,71,70,70,71,71,71,71,70,70,69,69,69,69,69,69,69,68,68,68,67,67,66,66,65,65,64,63,63,62,62,62,62,62,62,62,62,63,63,64,65,66,67,68,68,69,70,71,72,72,72,73,73,73,73,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,73,73,73,73,72,73,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,73,73,74,74,74,74,74,74,73,73,72,73,73,73,74,73,73,72,72,72,73,73,73,72,72,73,73,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,71,70,70,70,70,70,69,69,68,67,67,67,67,68,69,71,72,72,73,73,73,73,74,74,74,74,74,73,73,73,73,75,77,78,76,67,53,35,18,8,10,23,41,58,69,73,72,71,70,71,72,73,73,73,73,73,73,73,73,72,72,73,73,73,73,72,71,71,70,70,71,71,71,71,71,71,71,71,70,70,69,69,69,69,68,68,67,67,67,67,67,66,65,65,65,64,63,62,61,61,61,60,60,60,59,60,60,60,61,62,63,65,66,66,67,68,69,70,71,72,72,72,72,73,73,73,72,72,72,72,72,72,72,73,73,73,73,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,71,71,72,72,73,73,73,72,72,72,72,72,72,73,73,73,73,73,73,73,73,73,72,73,73,73,73,73,73,72,73,73,73,73,73,73,73,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,71,71,70,70,69,69,69,68,67,67,66,65,66,66,68,69,70,71,72,72,73,73,73,73,73,73,74,74,74,74,74,74,76,78,78,74,64,48,29,13,5,10,26,45,62,71,73,72,71,71,72,73,73,73,73,73,74,74,74,73,72,72,72,73,73,73,73,73,73,73,72,72,72,72,71,71,71,71,71,71,71,71,71,70,70,69,69,69,69,68,67,66,66,66,66,65,65,64,63,62,62,61,61,60,60,60,60,61,62,62,63,64,65,66,67,68,70,71,72,72,72,72,72,72,73,73,73,73,73,73,73,74,74,75,75,74,74,74,73,73,73,74,73,73,73,73,73,74,74,74,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,73,73,74,74,74,73,73,73,73,73,73,73,73,73,73,72,72,72,72,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,73,73,72,72,71,70,70,70,69,69,68,68,67,67,66,67,67,68,69,70,71,72,73,73,74,74,73,73,73,74,75,75,74,73,73,74,76,78,75,67,52,32,15,5,8,22,41,59,69,73,72,71,70,71,72,72,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,72,72,72,72,71,71,71,70,70,70,70,70,70,70,69,69,69,69,68,68,68,68,67,67,66,65,65,64,64,64,63,62,61,60,60,60,60,60,61,61,62,62,63,64,65,65,66,67,68,69,70,71,71,71,71,71,71,72,72,73,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,72,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,72,71,71,71,72,72,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,73,73,74,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,72,71,71,71,70,70,70,70,69,69,68,67,67,68,69,71,72,73,73,73,73,73,73,73,73,74,75,75,75,74,74,74,75,77,77,75,67,52,34,18,10,12,26,45,62,71,74,73,72,72,72,73,74,74,74,75,75,74,74,74,74,74,74,74,74,74,73,73,73,73,74,74,73,73,73,73,73,73,73,72,72,71,71,71,71,71,70,70,70,69,69,69,68,68,68,68,67,66,65,64,63,63,62,62,62,63,63,63,63,64,65,66,67,69,69,70,71,72,72,73,73,74,74,74,74,75,75,76,76,74,72,70,70,69,69 ];
I'd probably go about the task something like this. As mentioned in a comment, we need to draw a number of the data-points per-frame. How many we draw depends on the speed that the browser is able to supply an animation frame. I've hard-coded the value to 4, since that seems to work on my machine, but with not much more work you can probably make the code time itself and adjust this value on the fly so that your animation runs for as close as possible to the target time. I had a quick go, but the results were awful, I'll leave that as an exercise in research or thought for the reader. By keeping track of how many frames we've already drawn for the current 'refresh-cycle', we know how far to index into the array for the first point to be drawn for each frame. I've tried to parameterize the code as much as possible, but it's late and I'm tired, I may have overlooked something somewhere. <!doctype html> <html> <head> <script> function byId(id,parent){return (parent == undefined ? document : parent).getElementById(id);} window.addEventListener('load', onDocLoaded, false); function onDocLoaded(evt) { drawBkg(byId('canvas'), 9.43, 5, "0.5", "1.5", "black"); drawCurFrame(); } var dataSamples = [69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,72,72,72,72,72,72,72,73,73,74,74,74,74,74,74,74,73,73,73,73,74,74,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,74,74,74,73,73,73,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,71,72,72,72,73,73,73,72,72,72,73,73,73,74,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,72,73,73,73,72,72,72,71,101,71,70,70,70,69,68,68,67,67,66,66,67,67,69,70,72,72,72,73,73,74,73,73,73,73,73,73,73,73,73,74,76,77,76,70,57,40,22,11,11,22,40,57,69,73,73,71,71,71,72,72,73,73,74,74,74,73,72,72,72,72,72,72,72,72,72,72,72,72,71,71,70,70,71,71,71,71,70,70,69,69,69,69,69,69,69,68,68,68,67,67,66,66,65,65,64,63,63,62,62,62,62,62,62,62,62,63,63,64,65,66,67,68,68,69,70,71,72,72,72,73,73,73,73,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,73,73,73,73,72,73,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,73,73,74,74,74,74,74,74,73,73,72,73,73,73,74,73,73,72,72,72,73,73,73,72,72,73,73,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,71,70,70,70,70,70,69,69,68,67,67,67,67,68,69,71,72,72,73,73,73,73,74,74,74,74,74,73,73,73,73,75,77,78,76,67,53,35,18,8,10,23,41,58,69,73,72,71,70,71,72,73,73,73,73,73,73,73,73,72,72,73,73,73,73,72,71,71,70,70,71,71,71,71,71,71,71,71,70,70,69,69,69,69,68,68,67,67,67,67,67,66,65,65,65,64,63,62,61,61,61,60,60,60,59,60,60,60,61,62,63,65,66,66,67,68,69,70,71,72,72,72,72,73,73,73,72,72,72,72,72,72,72,73,73,73,73,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,71,71,72,72,73,73,73,72,72,72,72,72,72,73,73,73,73,73,73,73,73,73,72,73,73,73,73,73,73,72,73,73,73,73,73,73,73,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,71,71,70,70,69,69,69,68,67,67,66,65,66,66,68,69,70,71,72,72,73,73,73,73,73,73,74,74,74,74,74,74,76,78,78,74,64,48,29,13,5,10,26,45,62,71,73,72,71,71,72,73,73,73,73,73,74,74,74,73,72,72,72,73,73,73,73,73,73,73,72,72,72,72,71,71,71,71,71,71,71,71,71,70,70,69,69,69,69,68,67,66,66,66,66,65,65,64,63,62,62,61,61,60,60,60,60,61,62,62,63,64,65,66,67,68,70,71,72,72,72,72,72,72,73,73,73,73,73,73,73,74,74,75,75,74,74,74,73,73,73,74,73,73,73,73,73,74,74,74,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,73,73,74,74,74,73,73,73,73,73,73,73,73,73,73,72,72,72,72,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,73,73,72,72,71,70,70,70,69,69,68,68,67,67,66,67,67,68,69,70,71,72,73,73,74,74,73,73,73,74,75,75,74,73,73,74,76,78,75,67,52,32,15,5,8,22,41,59,69,73,72,71,70,71,72,72,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,72,72,72,72,71,71,71,70,70,70,70,70,70,70,69,69,69,69,68,68,68,68,67,67,66,65,65,64,64,64,63,62,61,60,60,60,60,60,61,61,62,62,63,64,65,65,66,67,68,69,70,71,71,71,71,71,71,72,72,73,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,72,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,72,71,71,71,72,72,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,73,73,74,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,72,71,71,71,70,70,70,70,69,69,68,67,67,68,69,71,72,73,73,73,73,73,73,73,73,74,75,75,75,74,74,74,75,77,77,75,67,52,34,18,10,12,26,45,62,71,74,73,72,72,72,73,74,74,74,75,75,74,74,74,74,74,74,74,74,74,73,73,73,73,74,74,73,73,73,73,73,73,73,72,72,71,71,71,71,71,70,70,70,69,69,69,68,68,68,68,67,66,65,64,63,63,62,62,62,63,63,63,63,64,65,66,67,69,69,70,71,72,72,73,73,74,74,74,74,75,75,76,76,74,72,70,70,69,69 ]; function drawBkg(canvasElem, squareSize, numSquaresPerBlock, minorLineWidthStr, majorLineWidthStr, lineColStr) { var nLinesDone = 0; var i, curX, curY; var ctx = canvasElem.getContext('2d'); ctx.clearRect(0,0,canvasElem.width,canvasElem.height); // draw the vertical lines curX=0; ctx.strokeStyle = lineColStr; while (curX < canvasElem.width) { if (nLinesDone % numSquaresPerBlock == 0) ctx.lineWidth = majorLineWidthStr; else ctx.lineWidth = minorLineWidthStr; ctx.beginPath(); ctx.moveTo(curX, 0); ctx.lineTo(curX, canvasElem.height); ctx.stroke(); curX += squareSize; nLinesDone++; } // draw the horizontal lines curY=0; nLinesDone = 0; while (curY < canvasElem.height) { if (nLinesDone % numSquaresPerBlock == 0) ctx.lineWidth = majorLineWidthStr; else ctx.lineWidth = minorLineWidthStr; ctx.beginPath(); ctx.moveTo(0, curY); ctx.lineTo(canvasElem.width, curY); ctx.stroke(); curY += squareSize; nLinesDone++; } } // position that will be treated as 0,0 when drawing our points. var originX=49; var originY=72; function drawSamples(nSamplesToDraw, firstSample, lineWidthStr, lineColourStr) { var can = byId('canvas'); var ctx = can.getContext('2d'); ctx.strokeStyle = lineColourStr; ctx.lineWidth = lineWidthStr; console.log(firstSample); ctx.beginPath(); ctx.moveTo( originX+firstSample-1, dataSamples[firstSample-1]+originY ); for (var i=0; i<nSamplesToDraw; i++) { var curSample = dataSamples[i + firstSample]; ctx.lineTo( originX+firstSample+i, curSample+originY ); } ctx.stroke(); } var curFrame=0; var nPointsPerFrame = 4; function drawCurFrame() { if ((dataSamples.length - (nPointsPerFrame * curFrame)) < nPointsPerFrame) // will we over-run the end of the array of datapoints? { curFrame = 0; // if so, reset drawBkg(byId('canvas'), 9.43, 5, "0.5", "1.5", "black"); } drawSamples(nPointsPerFrame, nPointsPerFrame*curFrame, "1", "blue"); curFrame++; requestAnimationFrame( drawCurFrame ); } </script> <style> #canvas { border: solid 1px black; background-color: #FFFFFF; } </style> </head> <body> <div id='txt'></div> <canvas id="canvas" width="1350" height="1300"></canvas> </body> </html>
Update Now that I see you have provided some more info I get what you want. The problem is you need to draw a fixed number of line segments within time t. As you do not know how long each frame could take you can not rely on a fixed frame rate. The alternative it to just use the current time and save the end time. Get the start time and then each frame draw all the should be drawn until the current time. As the line segments being drawn will not be displayed until the next screen refresh the time you get will be approx 16ms behind so will need to adjust for that. What I have done is keep track of the average frame time and used half that time to estimate when the new canvas update will be displayed. Its a bit pedantic but might as well show how to get a required time as close as possible. If you dont care its a few ms out then just remove the average frame time stuff. You will be at most 30ms off on a slow machine. var canvas; // canvas var ctx; function getCanvas () { // to do // get canvas and context } function drawGrid () { // to do // draw the grid } function drawTimedLine(){ if(canvas === undefined){ // if the canvas not available get it getCanvas(); } // clear the canvas is repeating animation ctx.clearRect(0, 0, canvas.width, canvas.height); drawGrid(); var array_1 = ; // your data // All stuff needed for timed animation. // The frame you render will not be displayed untill the next // vertical refresh which is unknown, Assume it is one frame. var startDelay = 1000; // if Needed time in milliseconds to delay start var endTime; // hold the time that the animation has to end var lastDataPoint; // holds the last point drawn to var timeToDraw = 5 * 1000; // how long the animation should last var repeatAfter = 1 *1000; // if you want to repeat the animatoin var frameCount = 0; // count the frames to get average frame time var startTime; //the start time; var numberPoints = array_1.length; // number of points; var startX = 49; // line starts at var yOffset = 72; // line Y offset var endX = 512; // line ends at. var width = endX - startX; // width var xStep = width / numberPoints; // X step per point var pointsPerMS = numberPoints / timeToDraw; // get how many points per ms should be drawn // function to draw function drawWave() { // variable needed var averageframeTime, timeLeft, i, currentTime; currentTime = new Date().valueOf(); // gets the time in millisecond; if (startTime === undefined) { // Is this the first frame startTime = currentTime; // save the start time; endTime = currentTime + timeToDraw; // workout when the end time is; lastDataPoint = 0; // set the data position to the start; averageframeTime = 0; // no frames counted so frame time is zero } else { frameCount += 1; // count the frames // get the average frame time averageframeTime = (currentTime - startTime) / frameCount; } // get the time this frame // will most likely be displayed // then calculate how long // till the end timeLeft = endTime - Math.min(endTime, currentTime + averageframeTime / 2); // now get where we should // be when the frame is presented pointPos = Math.floor(pointsPerMS * (timeToDraw - timeLeft)); // now draw the points from where we last left of // till the new pos; ctx.lineWidth = 4; ctx.strokeStyle = 'blue'; ctx.beginPath(); ctx.moveTo( // move to first point lastDataPoint * xStep + startX, array_1[lastDataPoint] + yOffset ); // draw each line from the last drawn to the new position for (i = lastDataPoint + 1; i <= pointPos && i < numberPoints; i++) { // Add the line segment ctx.lineTo( i * xStep + startX, array_1[i] + yOffset ); } ctx.stroke(); // execute the render commands lastDataPoint = pointPos; // update the last point if (pointPos < numberPoints) { // are we there yet??? requestAnimationFrame(drawWave); // no so request another frame }else{ // if you want to repeat the animation setTimeout(drawTimedLine , repeatAfter ); } } // start the line animation with delay if needed setTimeout(drawWave,startDelay); } // use this if you want it to start as soon as page is ready. document.addEventListener("DOMContentLoaded",drawTimedLine); // or use if you want it to start when page has images loaded and is ready // document.addEventListener("load",drawTimedLine); I have also added the ability to repeat the animation. If not needed just remove that code My original answer Dont know what the problem is with speed as it runs quite well on my machine. To set up a better start use function startFunction(){ // your code } document.addEventListener("DOMContentLoaded",startFunction); This will wait until the page has loaded and parsed the page. Images and other media may not have loaded but the page is ready to be manipulated. Not sure what you mean with 5 seconds. Assuming you may want the thing to sart in 5 seconds. The following will do that. document.addEventListener("DOMContentLoaded",function() {setTimeout(startFunction,5000);}); I would ask why plot the graph one entry at a time with requestAnimationFrame 1250 is not that many lines to draw. If you add ctx.beginPath() ctx.moveTo(/*first point*/) then loop all points with ctx.moveTo(/*points*/) then ctx.stroke() will run realtime on but the slowest of devices. BTW ctx.lineWidth is a Number not a string. Also you have two context? Use the one context for the canvas. Remove ctxt and just use ctx and finally you don't need to add type='text/javascript' to the script tag as Javascript is the default.
1) It cannot take that long to draw 1000 lines, even 100000 lines won't take more than 10 ms on any decent Browser. Look else where the time is lost. 2) The core issue of your code is that it lacks modularity. Split your code into a few clear functions, group the parameters into a few objects only, name and indent things properly. Below an (incomplete but working) example of how this might look. var cv, ctx; var data = null; var debug = true; // --------------------------------------- // define here all graphic related parameters var gfxParams = { canvasWidth: 600, canvasHeight: 600, gridColor: '#A66', gridSpacing: 10, gridLineWidth: 0.5, gridStrongLinesEvery: 5, lineColor: '#AEB', lastLineColor: '#8A9' // , ... }; // define here all animation related parameters var animationParams = { duration: 5, startTime: -1 } // --------------------------------------- // main // --------------------------------------- window.onload = function() { data = getData(); setupCanvas(data); launchAnimation(); } // --------------------------------------- // function setupCanvas(data) { cv = document.getElementById('cv'); cv.width = gfxParams.canvasWidth; cv.height = gfxParams.canvasHeight; ctx = cv.getContext('2d'); // here you should translate and scale the context // so that it shows your data. } function drawGrid(ctx) { var i = 0, pos = 0, lw = gfxParams.gridLineWidth; ctx.fillStyle = gfxParams.gridColor; var vLineCount = gfxParams.canvasWidth / gfxParams.gridSpacing; for (i = 0; i < vLineCount; i++) { pos = i * gfxParams.gridSpacing; ctx.fillRect(pos, 0, lw, gfxParams.canvasHeight); } var hLineCount = gfxParams.canvasHeight / gfxParams.gridSpacing; for (i = 0; i < hLineCount; i++) { pos = i * gfxParams.gridSpacing; ctx.fillRect(0, pos, gfxParams.canvasWidth, lw); } } function animate() { requestAnimationFrame(animate); var now = Date.now(); // erase screen ctx.clearRect(0, 0, gfxParams.canvasWidth, gfxParams.canvasHeight); // draw grid drawGrid(ctx); // draw lines var lastIndex = getLastDrawnIndex(data, now - animationParams.startTime); drawLines(ctx, data, lastIndex); if (debug) { ctx.save(); ctx.fillStyle = '#000'; ctx.fillText(lastIndex + ' lines drawn. Time elapsed : ' + (now - animationParams.startTime), 10, 10); ctx.restore(); } } // comment function launchAnimation() { requestAnimationFrame(animate); animationParams.startTime = Date.now(); } // comment function getData() { var newData = []; for (var i = 0; i < 500; i++) { newData.push([Math.random() * 600, Math.random() * 600]); } return newData; } // comment function getLastDrawnIndex(data, timeElapsed_ms) { var timeElapsed = timeElapsed_ms / 1000; if (timeElapsed >= animationParams.duration) return data.length - 1; return Math.floor(data.length * timeElapsed / animationParams.duration); } function drawLines(ctx, data, lastIndex) { ctx.strokeStyle = gfxParams.lineColor; // other ctx setup here. for (var i = 0; i < lastIndex - 1; i++) { drawLine(ctx, data[i], data[i + 1]); } ctx.strokeStyle = gfxParams.lastLineColor; drawLine(ctx, data[lastIndex - 1], data[lastIndex]); } function drawLine(ctx, p1, p2) { ctx.beginPath(); ctx.moveTo(p1[0], p1[1]); ctx.lineTo(p2[0], p2[1]); ctx.stroke(); } <canvas id='cv'></canvas>