Create shapes by for loop in canvas - javascript

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...

Related

Game froze in <canvas> js

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

Drawing objects on canvas created by eventlisteners()

I am new to JavaScript and I am having a hard time understanding how to get the canvas to cooperate with stuff I want to do.
I am trying to have my HTML button outside the canvas create a rectangle on the canvas. Then I want the rectangle to fall. I have the canvas animated, I have the button set to create a rectangle at user inputted coordinates, but...the canvas won't animate it.
It won't fall like the statically created rectangles. I also am struggling with how to get my button to create a new rectangle each time instead of redrawing the same one? Hoping someone can explain more than just give me the code.
Thanks in advance.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
window.onload = addListeners;
function addListeners() {
document.getElementById('b1').addEventListener('click',btn1,false);
function btn1() {
var inputx = document.getElementById("work").value;
var inputy = document.getElementById("y").value;
var inputs = document.getElementById("s").value;
new Rectangle(inputx,inputy,inputs);
// rec.x = inputx;
//rec.y = inputy;
//rec.s = inputs;
}
}
var r2 = new Rectangle(500, 50, 50);
var rec = new Rectangle();
//animate
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0,0,1450,250);
r2.draw();
r2.update();
rec.draw();
rec.update();
}
code for rectangle:
function Rectangle(x,y,s){
this.x = x;
this.y = y;
this.s = s;
this.draw = function(){
//console.log('fuck');
ctx.fillStyle = "rgba(0,200,0,1)";
ctx.fillRect(this.x,this.y,this.s,this.s);
}
this.update = function(){
console.log(this.y);
if((this.y + this.s) <= 250){
this.y= this.y+2;
}
}
}
The button is not animating anything as the animate method was not being called. Also in your code when you called animate it ended in an infinite loop as requestAnimationFrame will continue to call animate repeatedly, so I've added a condition around requestAnimationFrame(animate) to check the squares height and stop running it when it reaches the bottom, similar to what you had in your update function.
To create a new square every time you click the button, I moved the creation of the Rectangles inside the btn1 function. If you want to keep the old squares while creating new ones, you will need to keep track of them outside the canvas and redraw them with everything else each animation.
I guessed what your html looks like, but you can run the code below it will drop two squares, but note that the stop condition is only on the hard coded one.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
document.getElementById("b1").addEventListener("click", btn1, false);
var r2;
var rec;
function btn1() {
var inputx = document.getElementById("work").value;
var inputy = document.getElementById("y").value;
var inputs = document.getElementById("s").value;
r2 = new Rectangle(500, 50, 50);
rec = new Rectangle(parseInt(inputx, 10), parseInt(inputy, 10), parseInt(inputs, 10));
animate();
}
//animate
function animate() {
if (r2.y + r2.s <= 400) {
requestAnimationFrame(animate);
}
ctx.clearRect(0,0,1450,400);
r2.draw();
r2.update();
rec.draw();
rec.update();
}
function Rectangle(x,y,s){
this.x = x;
this.y = y;
this.s = s;
this.draw = function(){
ctx.fillStyle = "rgba(0,200,0,1)";
ctx.fillRect(this.x,this.y,this.s,this.s);
}
this.update = function(){
this.y= this.y+2;
}
}
<div>
<input id='work'>
<input id='y'>
<input id='s'>
<button id="b1"> Create Sqaure</button>
</div>
<canvas id="myCanvas" width=600 height=400></canvas>

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>

HTML5 canvas blinks while refreshing

I need to refresh an HTML5 canvas every two or three seconds.
setInterval(writeCanvas, 2000);
This canvas is filled with points and lines. Each abscissa and ordinate is stored in an XML file. So before updating the canvas I do an async request to the file on the server.
The problem is that the canvas blinks. I guess it disappears while the async request is running.
How could I get around this issue?
Here is the code of writeCanvas:
function drawLines(ctx, back, front, width, xArray, yArray) {
ctx.strokeStyle = back;
ctx.fillStyle = front;
ctx.lineWidth = width;
ctx.beginPath();
ctx.moveTo(xArray[0], yArray[0]);
for (var i=1; i<xArray.length; i++) {
ctx.lineTo(xArray[i],yArray[i]);
}
ctx.fill();
ctx.stroke();
ctx.closePath();
}
function drawPoint(ctx, back, front, x, y, radius, startAngle, endAngle) {
ctx.strokeStyle = back;
ctx.fillStyle = front;
ctx.beginPath();
ctx.arc(x,y,radius,startAngle,endAngle,endAngle);
ctx.fill();
ctx.stroke();
ctx.closePath();
}
function writeLabel(ctx, color, font, x, y, text) {
ctx.fillStyle = color;
ctx.font = font;
ctx.beginPath();
if(x < 0) {
x = 0;
}
ctx.fillText(text, x, y);
ctx.fill();
ctx.closePath();
}
function writeCanvas()
{
var elem = document.getElementById('profileCanvas');
if (!elem || !elem.getContext) {
return;
}
var ctx = elem.getContext('2d');
if (!ctx) {
return;
}
// apply the final size to the canvas
elem.setAttribute('width', canvasWidth);
elem.setAttribute('height', canvasHeight);
$.get('profileStatus.xml', function(xml) {
if(xml) {
var testPoints = new Array();
$(xml).find('TP').each(function() {
var selected = $(this).find('SELECTED:first').text();
if(selected == "YES") {
var name = $(this).find('MODULE_NAME:first').text();
var state = $(this).find('STATE:first').text();
var tp = new ProfileTp(name, state, selected);
testPoints.push(tp);
}
});
$.get('profile.xml', function(data) {
if(data) {
profileWidth = parseFloat($(data).find('MAIN > PROFILE > DIM_W').first().text());
profileHeight = parseFloat($(data).find('MAIN > PROFILE > DIM_H').first().text());
var backgroundColor = '#ddd';
var color = '#323232';
ctx.translate(0,canvasHeight);
var xArray = new Array();
var yArray = new Array();
$(data).find('PROFILE > POINT > X').each(function(){
var x=parseFloat($(this).text());
xArray.push(x);
});
$(data).find('PROFILE > POINT > Y').each(function(){
var y=parseFloat($(this).text());
yArray.push(y);
});
drawLines(ctx, backgroundColor, color, 2, xArray, yArray);
var finalArray = new Array();
$(data).find('TESTPOINTS > TP').each(function() {
var labelName = $(this).find('MODULE_NAME:first').text();
var tp = $.grep(testPoints, function(obj){ return obj.NAME == labelName; });
if(tp.length == 1) {
$(this).find('IHM').each(function(){
tp[0].LABEL_X = parseFloat($(this).find('LABEL > X:first').text());
tp[0].LABEL_Y = parseFloat($(this).find('LABEL > Y:first').text());
tp[0].MARKER_X = parseFloat($(this).find('MARKER > X:first').text());
tp[0].MARKER_Y = parseFloat($(this).find('MARKER >Y:first').text());
});
finalArray.push(tp[0]);
}
});
for(var i=0; i<finalArray.length; i++) {
writeLabel(ctx, color, fontSize+"px Arial",(finalArray[i].MARKER_X+finalArray[i].LABEL_X),(finalArray[i].MARKER_Y+finalArray[i].LABEL_Y), finalArray[i].NAME);
drawPoint(ctx, backgroundColor, color, finalArray[i].MARKER_X, finalArray[i].MARKER_Y, 8, 0, 2*Math.PI);
}
} else {
console.error('No XML test points returned');
}
});
}
});
}
There are two XML files. One contains all the points, lines and labels. The second contains only the points and labels that have to be displayed.
Setting a canvas' dimensions clears it entirely, so the lines :
elem.setAttribute('width', canvasWidth);
elem.setAttribute('height', canvasHeight);
are likely to make your canvas 'blink'. GET requests are asynchronous so the canvas is cleared way before points data are computed and drawn.
To fix this, change the dimensions inside your requests callbacks, right before drawing.
Crogo already mention the probable cause in the answer, but as a work around you could do:
if (elem.width !== canvasWidth || elem.height !== canvasHeight) {
// apply the final size to the canvas
elem.setAttribute('width', canvasWidth);
elem.setAttribute('height', canvasHeight);
}
The canvas size is only set if the size change.
You should also try to avoid using setInterval (what if client is on a slow/unstable connection that makes it take longer than 2 second to load data..). If the download is still in progress and setInterval triggers you will initiate another download while the first one is still downloading. You will also risk get "double drawings" to the canvas as these calls stack up in the event queue:
Rather trigger an setTimeout from inside your writeCanvas():
function writeCanvas()
{
//... load and draw
setTimeout(writeCanvas, 1500); //compensate for time
}
Of course, if the data MUST be loaded every two seconds this would be inaccurate (not that setInterval is.. they both give an estimate only).

clear Rect doesn't work [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I would like to add to a drawing program buttons with different functions. But I got problems with the first one, of course. I'm trying to have a button to clear the entire canvas. But somehow it doesn't work.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var cb_canvas = null;
var cb_ctx = null;
var cb_lastPoints = null;
var cb_easing = 0.4;
// Setup event handlers
window.onload = init;
function init(e) {
cb_canvas = document.getElementById("cbook");
cb_lastPoints = Array();
if (cb_canvas.getContext) {
cb_ctx = cb_canvas.getContext('2d');
cb_ctx.lineWidth = 2;
cb_ctx.strokeStyle = "rgb(0, 0, 0)";
cb_ctx.beginPath();
cb_canvas.onmousedown = startDraw;
cb_canvas.onmouseup = stopDraw;
cb_canvas.ontouchstart = startDraw;
cb_canvas.ontouchstop = stopDraw;
cb_canvas.ontouchmove = drawMouse;
}
}
function startDraw(e) {
if (e.touches) {
// Touch event
for (var i = 1; i <= e.touches.length; i++) {
cb_lastPoints[i] = getCoords(e.touches[i - 1]); // Get info for
finger #1
}
}
else {
// Mouse event
cb_lastPoints[0] = getCoords(e);
cb_canvas.onmousemove = drawMouse;
}
return false;
}
// Called whenever cursor position changes after drawing has started
function stopDraw(e) {
e.preventDefault();
cb_canvas.onmousemove = null;
}
function drawMouse(e) {
if (e.touches) {
// Touch Enabled
for (var i = 1; i <= e.touches.length; i++) {
var p = getCoords(e.touches[i - 1]); // Get info for finger i
cb_lastPoints[i] = drawLine(cb_lastPoints[i].x, cb_lastPoints[i].y,
p.x, p.y);
}
}
else {
// Not touch enabled
var p = getCoords(e);
cb_lastPoints[0] = drawLine(cb_lastPoints[0].x, cb_lastPoints[0].y, p.x,
p.y);
}
cb_ctx.stroke();
cb_ctx.closePath();
cb_ctx.beginPath();
return false;
}
// Draw a line on the canvas from (s)tart to (e)nd
function drawLine(sX, sY, eX, eY) {
cb_ctx.moveTo(sX, sY);
cb_ctx.lineTo(eX, eY);
return { x: eX, y: eY };
}
// Get the coordinates for a mouse or touch event
function getCoords(e) {
if (e.offsetX) {
return { x: e.offsetX, y: e.offsetY };
}
else if (e.layerX) {
return { x: e.layerX, y: e.layerY };
}
else {
return { x: e.pageX - cb_canvas.offsetLeft, y: e.pageY -
cb_canvas.offsetTop };
}
}
$("clear").onclick=function(){clearAll()};
function clearAll() {
var canvas = $("#cbook");
var ctx = canvas.get(0).getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
});
</script>
</head>
<body>
<canvas id="cbook" width="500" height="333"></canvas>
<button id="clear">Clean Up</button>
</body>
</html>
You are pretty much right, there are a couple of problems...
$("clear").onclick=function(){clearAll()};
Should probably read
$("#clear").click(clearAll);
You needed to mark the selector as an id and also you were trying to put a native element event on a jQuery object.
The other problem is similar you are using the jQuery object containing a canvas instead of the native element, instead change...
var canvas = $("#cbook");
var ctx = canvas.get(0).getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
To...
var canvas = $("#cbook");
var ctx = canvas.get(0).getContext("2d");
ctx.clearRect(0, 0, canvas.get(0).width, canvas.get(0).height);
Or more simply...
var canvas = $("#cbook").get(0);
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
A fiddle...
http://jsfiddle.net/zZhfE/
Oh and totally forgot, the window.onload handler is unnecessary as you are running it within the document ready function of jQuery everything should be ready to go anyway. I just moved everything out of the function and removed the window.onload handler.
I can see three main issues with your code. First, you're mixing and matching Javascript and jQuery, and it's causing issues (when working with Canvas, try and stick to vanilla Javascript).
You're not getting the context in the clearAll() function because you're trying to use jQuery:
var canvas = $("#cbook");
When you should be using plain ol' JS, like in your drawing function:
var canvas = document.getElementById("cbook");
Secondly, you're calling the clear function like this:
$("clear").onclick=function(){clearAll()};
When it should be this:
$("#clear").on('click', function(){clearAll()});
(note the # before clear - you need to use this to refer to the ID of an element.)
Finally, you're looking for the first instance of a canvas inside a canvas, which won't work:
var ctx = canvas.get(0).getContext("2d");
All you need is:
var ctx = canvas.getContext("2d");
Here's a working JSFiddle:
http://jsfiddle.net/hGjDR/

Categories