The squares still get drawn even tough they are deleted from the array that is drawn. Shouldn't they be deleted when they are being deleted from the Array. Does the array not update inside the go function?
Javascript:
var canvas;
var ctx;
$(document).ready(function(){
$("#thebutton").on('click',function(){
thearray.pop();
})
canvas = $('#canvas').get(0);
ctx =canvas.getContext("2d");
})
class Square{
constructor(p1,p2,p3,p4){
this.p1=p1;
this.p2=p2;
this.p3=p3;
this.p4=p4;
}
start(){
var that = this;
setTimeout(function(){
that.go();
that.start();
console.log("timeout running");
},1000);
}
go(){
for(let i = 0; i<thearray.length;i++){
console.log("loop running:"+i);
if(true){
ctx.clearRect(0,0,500,500);
console.log("clearing rect");
}
ctx.rect(thearray[i].p1, thearray[i].p2, thearray[i].p3, thearray[i].p4);
ctx.stroke();
}
}
}
var thearray=[];
var thesquare1 = new Square(20,20,150,100);
var thesquare2 = new Square(100,100,200,200);
var thesquare3 = new Square(200,200,300,300);
thearray.push(thesquare1);
thearray.push(thesquare2);
thearray.push(thesquare3);
thesquare1.start();
HTML:
<canvas id="canvas" height="500" width="500"></canvas>
<button type="button" name="button" id="thebutton">Pop Array</button>
Spent almost an hour on debugging your code!
This led to the finding that if fillRect() is used instead of rect() the code works well...
and then finally found this..
(I too didn't know it before ><)
Have a look at this: link
In short, just call beginPath() after clearRect() to start a new path instead of using old path stack!!!
go(){
ctx.clearRect(0,0,500,500);
ctx.beginPath(); //This line saved the day :)))
console.log("clearing rect");
for(let i = 0; i<thearray.length;i++) {
console.log("loop running:"+i);
ctx.rect(thearray[i].p1, thearray[i].p2, thearray[i].p3, thearray[i].p4);
ctx.stroke();
}
}
using this the code works for rect() as intended :)
Note: also you have to move that clearRect() call outside the for loop otherwise it will clear the canvas after every single iteration of the loop which results in showing only the 3rd rectangle on the canvas.
Also, that if(true){} is not at all necessary.
Update: Also checkout this thread for some other alternatives to beginPath() to handle such scenario
Related
I am attempting to use a callback to create an asynchronous function that does calculations behind a draw loop without slowing it down. I have read many callback examples and am clearly doing something wrong.
I have created a simplified version of what I would like to do. When you click your mouse it should do the math without hanging up the draw loop. Right now it causes a hangup:
var nPoints = 1;
var sumDone = false;
var sum = 0;
function setup() {
var myCanvas = createCanvas(200, 200);
myCanvas.parent("p5");
}
function draw(){
nPoints = nPoints+1
stroke(0,0,0);
background(245,245,245);
noFill();
rect(1,1,198,198);
textAlign(CENTER);
fill(0,0,0);
textSize(20);
if(sumDone){
text(sum,100,20);
}else{
text("not done",100,20);
}
noStroke();
push();
translate(100,100);
rotate(nPoints/50);
rect(-50,-10,100,20);
pop();
}
function mouseClicked(){
if(sumDone){
sumDone = false;
sum=0;
}else{
doMath(function (){
sumDone = true;
});
}
}
function doMath(callback){
for(var i=0;i<10000000;i++){
sum = sum + i;
}
callback();
}
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.16/p5.js"></script>
<body>
<div id="p5" align="center">
</div>
<script>
<!-- JAVASCRIPT CODE GOES HERE -->
</script>
</body>
As you can see, the math still completely hangs up the draw loop. Is it possible to do this in a way where the draw loop is not effected?
JavaScript is single-threaded. Moving the work to a callback function just ties up the thread that's used to run draw() and every other JavaScript function.
The only way to offload work like this is to do it on the server and use AJAX to query for the result.
Other than that, perhaps try breaking the calculation down into smaller chunks. Can you do 10% of the calculation per frame or something?
Edit: These threads mention the concept of web workers which you might look into.
I'm working on a Paint App using Processing.js. Basically, when the mouse is dragged, mouseX and mouseY are saved in an array of objects called data[]. Afterwards the paint() function will run a loop that accesses every object of the data[] array and draws a line of color(data[i].R,data[i].G,data[i].B) and thickness data[i].T between the corresponding data[i].mouseX and data[i].mouseY coordinates. The problem is that the array keeps getting bigger the more you draw and in my case, when the length of the data[] array reaches ~800 elements it will start to lag, and keeps getting worse the more I keep drawing. Is there any tweak that will fix the lag or do I have to completely rethink the program?
<!DOCTYPE HTML>
<html>
<head>
<script src="https://github.com/downloads/processing-js/processing-js/processing-1.4.1.min.js"></script>
<script type="text/processing" data-processing-target="targetcanvas">
void setup() {
size(649, 600);
}
background(255,255,255);
var r=0;
var g=0;
var b=0;
var data = [];
var mousex;
var mousey;
var thickness=31;
var painting = false;
var counter=0;
var x;
var paint = function() {
background(255, 255, 255);
for(var i=1;i<data.length;i++){
if (data[i-1].mousex && data[i].mousex) {
strokeWeight(data[i].T);
stroke(data[i].R, data[i].G, data[i].B);
line(data[i].mousex,data[i].mousey,data[i-1].mousex,data[i-1].mousey);
fill(0,0,0);
text(data.length,10,10);
}
};
};
mouseDragged = function(){
painting = true;
data.push({mousex: mouseX, mousey: mouseY, R:r, G:g, B:b, T:thickness});
paint();
counter++;
};
mouseReleased = function() {
x=counter;
counter=0;
if(painting) {
data.push({mousex: 0, mousey: 0});
}
painting = false;
};
mouseOut = function() {
data.push({mousex: 0, mousey: 0});
}
</script>
<center>
<canvas id="targetcanvas"width="649" height="600" " style="border: 3px solid black; margin-top=100px;"></canvas>
</center>
</body>
</html>
Is this Processing.js or P5.js? Either way, the answer is the same.
You basically have a spectrum of options:
No data structures, no undo: Instead of storing your shapes and whatnot in data structures, just draw them directly to the canvas whenever the user does something. You can use a PGraphics canvas, or you can just draw directly to the screen, if you get rid of the call to background(). Then you don't need any data structures. The downside of this is you won't be able to remove shapes once they're drawn.
Some data structures, some undo: If you needed to be able to undo some of the shapes, but not all of them, you could do a mix of the above approach and your current approach. Instead of storing everything in data structures, you would only store the last 1-10 or so shapes. The rest of the shapes would be drawn directly to the PGraphics buffer.
Lots of data structure, lots of undo: If you really needed to be able to undo all of the shapes, then you could still use the PGraphics approach, but only redraw all of the shapes when something was removed.
I'm trying to build a very simple animation function. I'm using this tutorial to build my project:
https://www.youtube.com/watch?v=hUCT4b4wa-8
The result after the button is clicked should be a green box moving across the page from left to right. When the button is clicked, nothing happens and I don't get any console errors.
Here's my fiddle:
https://jsfiddle.net/xkhpmrtu/7/
And here's a snippet of my code:
<!DOCTYPE html>
<head>
<meta charset="utf-8" />
<style type="text/css">
canvas {
border: 1px solid #666;
}
</style>
<script type="application/javascript" language="javascript">
function anim(x,y) {
var canvas = document.getElementById('canvas');//reference to canvas element on page
var ctx = canvas.getContext('2d');//establish a 2d context for the canvas element
ctx.save();//save canvas state if required (not required for the tutoriral anaimation, but doesn't hurt the script so it stays for now)
ctx.clearRect(0, 0, 550, 400);//clears the canvas for redrawing the scene.
ctx.fillStyle = "rgba(0,200,0,1)";//coloring the rectangle
ctx.fillRect = (x, 20, 50, 50);//drawing the rectangle
ctx.restore();//this restores the canvas to it's original state when we saved it on (at the time) line 18
x += 5; //increment the x position by some numeric value
var loopTimer = setTimeout('draw('+x+','+y+')', 2000);// setTimeout is a function that
</script>
</head>
<body>
<button onclick="animate(0,0)">Draw</button>
<canvas id="canvas" width="550" height="400"></canvas>
</body>
Any idea what I'm doing wrong?
I just had a look at the tutorial link. I will give if a major thumbs down as it demonstrates how not to animate and how not to do many other things in Javascript.
First the script tag and what is wrong with it
// type and language default to the correct setting for javascrip
// <script type="application/javascript" language="javascript">
<script>
function anim(x,y) {
// get the canvas once. Getting the canvas for each frame of an
// animation will slow everything down. Same for ctx though will not
// create as much of a slowdown it is not needed for each frame
// var canvas = document.getElementById('canvas');
// var ctx = canvas.getContext('2d');
// Dont use save unless you have to. It is not ok to add it if not needed
// ctx.save();
// dont use literal values, canvas may change size
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "rgba(0,200,0,1)";
// this line is wrong should be ctx.fillRect(x, 20, 50, 50). It is correct in the video
ctx.fillRect = (x, 20, 50, 50);//drawing the rectangle
// restore not needed
//ctx.restore();
x += 5; //increment the x position by some numeric value
// creating a string for a timer is bad. It invokes the parser and is slooowwwwww...
// For animations you should avoid setTimeout altogether and use
// requestAnimationFrame
// var loopTimer = setTimeout('draw('+x+','+y+')', 2000);
requestAnimationFrame(draw);
// you were missing the closing curly.
}
</script>
There is lots more wrong with the tut. It can be excused due to it being near 5 years old. You should look for more up todate tutorials as 5 years is forever in computer technology.
Here is how to do it correctly.
// This script should be at the bottom of the page just befor the closing body tag
// If not you need to use the onload event to start the script.
// define a function that starts the animation
function startAnimation() {
animating = true; // flag we are now animating
x = 10;
y = 10;
// animation will start at next frame or restart at next frame if already running
}
// define the animation function
function anim() {
if (animating) { // only draw if animating
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "red"; //coloring the rectangle
ctx.fillRect(x, y, 50, 50); //drawing the rectangle
x += xSpeed;
}
// set animation timer for next frame
requestAnimationFrame(anim);
}
// add a click listener to the start button. It calls the supplied function every time you click the button
startAnimButton.addEventListener("click", startAnimation);
const ctx = canvas.getContext('2d'); // get the 2d rendering context
// set up global variables to do the animation
var x, y, animating;
animating = false; // flag we are not animating
const xSpeed = 50 / 60; // Speed is 50 pixels per second at 60fps
// dont slow the animation down via frame rate
// slow it down by reducing speed.
// You only slow frame rate if the machine
// can not handle the load.
// start the animation loop
requestAnimationFrame(anim);
canvas {
border: 1px solid #666;
}
<!-- don't add events inline -->
<button id="startAnimButton">Draw</button>
<canvas id="canvas" width="512" height="128"></canvas>
I am using html5 and javascript for rendering a text. I want to update content of text drawn in canvas. I use the following code
var drawArea = document.getElementById('drawingPlane');
var ctx = drawArea.getContext("2d");
ctx.font = "60px Arial";
var counter = 0;
function update() {
counter++;
ctx.strokeText(counter , 50 , 30);
}
setInterval(update , 1000);
The problem is text is not cleared before writing the next value of counter. It is rendering above the previous value of counter. How can I solve this??
Thanks in advance.
You can use the clearRect method to clear the canvas like this:
<script>
var drawArea = document.getElementById('drawingPlane');
var ctx = drawArea.getContext("2d");
ctx.font = "60px Arial";
var counter = 0;
function update() {
ctx.clearRect ( 0 , 0 , drawArea.width, drawArea.height ); // clear canvas
counter++;
ctx.strokeText(counter , 50 , 30);
}
setInterval(update , 1000);
</script>
In HTML5 Canvas shapes (including text) are immediately pixelated (rasterized) and cannot be edited once drawn. The only way to get rid of something you've drawn is clearing the area where it was drawn as suggested by MUG4N. If you want to edit shaped later you need to either use a HTML5 library such as KineticJS which has built a workaround for retaining shapes, or use SVG.
I'm trying to add cards to an array in a Deck object but for some reason push() is failing. I had this working earlier but after making some changes, I have effectively messed it up.
(The "testX" writes are there for debugging purposes)
<!DOCTYPE HTML>
<html>
<head>
<style>
#myCanvas {
border: 1px solid #9C9898;
}
body {
margin: 0px;
padding: 0px;
}
</style>
<script type="text/javascript">
<!--
//draws the game
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
//draws the rectangles that will be the card
var cardHeight = 125,
cardWidth = 80;
context.beginPath();
//draws the top row of 5 cards
for(var x=0;x<5;x++){
context.rect(10+(x*(cardWidth+10)),10,cardWidth,cardHeight);
}
//draws the bottom row of 5 cards
for(x=0;x<5;x++){
context.rect(10+(x*(cardWidth+10)),150,cardWidth,cardHeight);
}
//draws the deck
context.rect(10+5*cardWidth+65,(150-10)/2,cardWidth,cardHeight);
context.fillStyle = 'white';
context.fill();
context.lineWidth = 2;
context.strokeStyle = 'black';
context.stroke();
};
function Deck(){
//creates the unshuffled deck (loadedDeck) once to make the shuffling process faster
var loadedDeck = new Array(),
realDeck;
this.loadTheDeck = function(){ //method
for(x=1;x<=13;x++){
this.loadedDeck.push(x+" Spades"); //<---issue line (all 4 are failing though this is the first)
this.loadedDeck.push(x+" Clubs");
this.loadedDeck.push(x+" Hearts");
this.loadedDeck.push(x+" Diamonds");
}
document.write(this.loadedDeck);
};
this.loadTheDeck(); //creates the unshuffled deck when the Deck is instantiated
//resets the deck and randomizes
this.shuffle = function(){ //method
//creates the real deck
this.realDeck = this.loadedDeck;
//write shuffle function
};
this.shuffle(); //shuffles the Deck when instantiated
}
document.write("test-1");
var myDeck = new Deck();
document.write("test0");
document.write(this.realDeck);
document.write("test1");
-->
</script>
</head>
<body>
<canvas id="myCanvas" height="300" width="600"></canvas>
</body>
</html>
Here is a demo of the code: http://jsfiddle.net/NfmsR/2/
When you run this line:
this.loadedDeck.push(x+" Spades");
You are using the this.loadedDeck array. Have you defined it as part of the Deck object? Nope:
var loadedDeck = new Array(),
realDeck;
Change the declaration to this and it should work:
this.loadedDeck = []; // I'd use [] instead of new Array().
this.realDeck = [];
As #j08691 pointed out, you need to change realDeck to this.realDeck as well because you're calling it similarly here:
this.shuffle = function(){ //method
//creates the real deck
this.realDeck = this.loadedDeck;
//write shuffle function
};
You're referring to "loadedDeck" as if it's a property of your "Deck" object. It's not; it's just a local variable to the constructor closure.
Just call it "loadedDeck" and see if that helps. Same goes for "realDeck".
this.loadTheDeck = function(){ //method
for(var x=1;x<=13;x++){
loadedDeck.push(x+" Spades"); //<---issue line (all 4 are failing though this is the first)
loadedDeck.push(x+" Clubs");
loadedDeck.push(x+" Hearts");
loadedDeck.push(x+" Diamonds");
}
Also it'd be a good idea to get out of the habit of abusing document.write for debugging. Also don't forget to declare things like "x" with var!!
Your Deck object, which is accessed with this, has no "loadedDeck"-property. loadedDeck is a local variable. Just remove the "this.".