each time when I am reviewing and trying to re-write my code I always got errors and some unexpected issues. I am making a function object called draw() and trying to push it 20 times into an empty array for the animate purpose. the code is as follows:
function draw(){
var r = 20;
var x = Math.random()*canvas.width - r;
var y = Math.random()*canvas.height - r;
ctx.beginPath();
ctx.arc(x,y,r,Math.PI*2,false);
ctx.fillStyle='yellow';
ctx.fill();
}
myArray = [];
for(i=0;i<20;i++){
myArray.push(draw());
}
function animate(){
ctx.clearRect(0,0,canvas.width,canvas.height);
requestAnimationFrame(animate);
for(i=0;i<myArray.length;i++){
myArray[i];
}
}
animate();
When I am checking myArrayobject in the console, it just popped out 20 objects with undefined value, which means there is totally empty inside myArrayobject. I am stuck with this for 2 hours could someone could tell me where I go wrong with this? Thanks in advance.
myArray.push(draw()) calls the draw() function immediately and puts its return value (undefined) into the array. You probably want to push a reference to the function, so omit the ():
myArray.push(draw);
Then, in your for loop, to call the functions from the array you need parentheses:
myArray[i]();
Pushing the same function seems a bit pointless, because you could just directly call the function from your loop without using the array, but I assume you want to set up for the possibility of running some predefined sequence of several different functions that aren't shown.
Anyway, the following demo shows the above changes working in the context of your full code:
var canvas = document.querySelector("canvas")
var ctx = canvas.getContext("2d");
function draw() {
var r = 20;
var x = Math.random() * canvas.width - r;
var y = Math.random() * canvas.height - r;
ctx.beginPath();
ctx.arc(x, y, r, Math.PI * 2, false);
ctx.fillStyle = 'yellow';
ctx.fill();
}
myArray = [];
for (i = 0; i < 20; i++) {
myArray.push(draw); // this line changed
}
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
requestAnimationFrame(animate);
for (i = 0; i < myArray.length; i++) {
myArray[i](); // this line changed
}
}
animate();
<canvas></canvas>
Related
I am trying to use a button that can build a small square every time I click it with changing only position x
this is the code but it doesn't draw a square instead it's drawing a long rectangle
let addBlock = document.getElementById('addBlock')
var c = document.getElementById('canvas1');
var ctx = c.getContext("2d");
let s =0;
for(let i=0; i<=100; i=i+5 ){
addBlock.addEventListener('click', function(){
ctx.beginPath();
ctx.rect(10+i, 20,9, 100);
ctx.fill()
s=s+i; })}
rect(x,y,height,width)
I think you just need to change the width from 100 to 9.
ctx.rect(10+i, 20, 9, 9);
if i understood the purpose of your application correctly then you want to change the position on each click. Your code doesnt work because the foorloop finishes before you even get to click once. My solution is the following:
let addBlock = document.getElementById('addBlock');
var c = document.getElementById('canvas1');
var ctx = c.getContext('2d');
let i = 0;
let fn = function() {
ctx.clearRect(0, 0, c.width, c.height);
ctx.beginPath();
ctx.rect(20 + i, 20, 100, 100);
ctx.fill();
i += 5;
addBlock.addEventListener('click', fn);
};
addBlock.addEventListener('click', fn);
See my Stackblitz link for a working example:
https://stackblitz.com/edit/web-platform-4dhxpt?file=script.js
EDIT:
changed the code to run a maximum of 20 times:
let fn = function() {
if (i >= 100) {
return;
}
ctx.beginPath();
ctx.rect(20 + i, 20, 100, 100);
ctx.fill();
i += 5;
addBlock.addEventListener('click', fn);
};
I am figuring this one out an unhealthy amount of time for now and I did not found any note for this bug. I started to build a simple HTML Canvas animation in JavaScript. For now I expect the small squares to move. Here is the code (I am also using babeljs):
class Pod {
constructor(x,y) {
this.posX = x;
this.posY = y;
this.velX = getRandomNumber(-5,5);
this.velY = getRandomNumber(-5,5);
}
getPos() {
return ([this.posX,this.posY]);
}
move() {
this.posX += this.velX;
this.posY += this.velY;
}
render() {
ctx.save();
ctx.rect(this.posX,this.posY,3,3);
ctx.fillStyle = "#ffffff";
ctx.fill();
ctx.restore();
}
}
/*the classes end here*/
var canvas = document.getElementById('canvas')
var ctx = canvas.getContext('2d');
var elementsNum = 10;
const stack = new Array(elementsNum);
for(var i = 0; i < elementsNum; i++) {
stack[i] = new Pod(getRandomNumber(0,500),getRandomNumber(0,500));
}
function run() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#000000";
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx = canvas.getContext('2d');
for(var i = 0; i < elementsNum; i++) {
stack[i].move();
stack[i].render();
}
//window.requestAnimationFrame(run);
}
/*helper functions*/
function getRandomNumber(min, max) {
return Math.random() * (max - min) + min;
}
After running a cycle of the function run(), the small squares (I called them Pods) are rendered. Next cycle starts with clearing the canvas with ctx.clearRect... I am resetting the context and start moving and then drawing the Pods from the stack. When I draw the first Pod, it will draw all of them and also the previous frame.
Here is the codepen for this particular script: http://codepen.io/erikputz/pen/YNNXqX
(I knowingly commented the requestAnimationFrame, so you need to use the console to call the run() function)
Thank you forward for your help.
http://codepen.io/zfrisch/pen/bgazyO
This should solve your issue:
render() {
ctx.beginPath();
ctx.rect(this.posX,this.posY,3,3);
ctx.fillStyle = "#ffffff";
ctx.fill();
ctx.closePath();
}
With canvas you need to identify individual shapes through code by using the beginPath and closePath methods. In certain methods this is innate, like in fillRect. Hence the above code could be simplified even more to:
render() {
ctx.fillStyle = "#ffffff";
ctx.fillRect(this.posX,this.posY,3,3);
}
When you're just declaring a shape (rect) you need to specify when the path begins and when it is closed, otherwise it will most likely cause issues like the shape-bleeding you had in your original code.
Also, as a tip, you don't need to save state.save() / .restore() unless you're translating/scaling/rotating/or moving on the canvas element. Filling shapes doesn't apply.
I am trying to create a template for initiating as many waterfall objects as I wish without having to create a new canvas for each of them. I want two waterfalls with different colors but it doesn't work. I can't figure out why and I'm on it since a few hours. How can I make both red and blue waterfalls appear where the first has a lower z index than the last instantiation?
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var w = canvas.width = window.innerWidth;
var h = canvas.height = window.innerHeight;
function waterfall(color) {
var self = this;
this.color = color;
this.water = [];
this.Construct = function(y, vel, acc) {
this.y = y;
this.vel = vel;
this.acc = acc;
}
for(var i = 0; i < 1000; i++) {
this.water.push(new this.Construct(Math.random() * 65, 0.1 + Math.random() * 4.3, 0));
}
this.flow = function(color) {
ctx.clearRect(0, 0, w, h);
for(var i = 0; i < this.water.length; i++) {
this.water[i].vel += this.water[i].acc;
this.water[i].y += this.water[i].vel;
ctx.beginPath();
ctx.arc(0 + i * 0.5, this.water[i].y, 2, 0, Math.PI * 2, false);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
for(var i = 0; i < this.water.length; i++) {
if(this.water[i].y > window.innerHeight) {
this.water[i].y = 0;
}
}
requestAnimationFrame(function() {
self.flow.call(self);
});
}
this.flow(this.color)
}
new waterfall("blue");
new waterfall("red");
Here's my working code: https://jsfiddle.net/testopia/d9jb08xb/5/
and here again my intention to create two separate waterfalls but this time with the prototype inheritance:
https://jsfiddle.net/testopia/d9jb08xb/8/
I do prefer the latter but I just cant get either working.
The problem is that you are clearing the canvas in each waterfall. One is overpainting the other. You can immediately see that by commenting out the line
ctx.clearRect(0, 0, w, h);
Of course the water smears that way.
You have to manage your waterfalls in a way that in each animation frame you first clear the canvas then let them paint all.
Here is a quick attempt using a master flow_all() function:
https://jsfiddle.net/kpomzs83/
Simply move this line...
ctx.clearRect(0, 0, w, h);
... to here...
requestAnimationFrame(function() {
ctx.clearRect(0, 0, w, h); // ensure that w and h are available here.
self.flow.call(self);
});
This ensures that you do not clear the canvas before the 2nd waterfall is drawn. This clears the canvas, then draws the two waterfalls. Make sure you've added them to your water array, of course.
$(document).ready(function () {
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
function deseneazaGrafic(valori) {
var h = canvas.height;
var w = canvas.width / valori.length;
context.fillStyle = "#DEDEDE";
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "red";
context.strokeStyle = "black ";
context.lineWidth = 2;
var f = canvas.height * 0.9 / Math.max.apply(Math, valori);
for (var i = 0; i < valori.length; i++) {
context.beginPath();
context.rect((i + 0.1) * w, h - valori[i] * f, 0.8 * w, valori[i] * f);
context.fill();
context.stroke();
}
}
$("#btnGrafic").click(function () {
deseneazaGrafic(eval("[" + $("#valori").val() + "]"));
});
$("#scrie").click(function(){
});
});
This is my java script. This makes a graph from some written values. How can i change the "valori" to be jsonObject.values?Thanks a lot
As a formal answer (see question comments for details), the function deseneazaGrafic accepts a simple javascript array. Right now it's evaling (ew... bad) the value of a textarea (#valori). You can use any javascript array. When you get the values from your JSON file, just pass it to deseneazaGrafic as its parameter. For example:
var jsonObject = JSON.parse(myJSONString); // the JSON object contains an array called "values"
deseneazaGrafic(jsonObject.values);
That should render the graph just fine assuming the array is parsed correctly.
i am trying to animate 3 different shapes with setTimeout , my question is how can i use multiple setTimeout to animate 3 different shapes also is there a better way to do this maybe using setInterval
window.onload = draw;
var x = 5;
var y = 5;
radius = 50;
var x2 = 50;
var y2 = 120;
var x3 = 53;
var y3 = 230;
var context;
var loopTimer;
function draw(){
var canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
context.save();
context.clearRect(0,0,720,550);
rectangle(x,y);
circle(x2,y2);
circle2(x3,y3);
}
function rectangle(x,y){
//drawing a rectangle
context.fillStyle = "rgba(93,119,188,237)";
context.clearRect(0,0,720,550);
context.rect(x, y, 50, 50);
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'yellow';
context.stroke();
x += 1;
loopTimer = setTimeout('rectangle('+x+','+y+')',50);
}
function circle(x2,y2){
//darwong a circle
context.beginPath();
context.clearRect(0,0,720,550);
context.fillStyle = "#0000ff";
//Draw a circle of radius 20 at the current coordinates on the canvas.
context.arc(x2, y2, radius, 0, Math.PI*2, true);
context.closePath();
context.fill();
x2 += 1;
loopTimer = setTimeout('circle('+x2+','+y2+')',20);
}
function circle2(x3,y3){
//drawing a second circle
context.beginPath();
context.clearRect(0,0,720,550);
context.fillStyle = 'green';
context.arc(x3, y3, radius, 0, Math.PI*2, true);
context.closePath();
context.fill();
context.lineWidth = 5;//border around the circle
context.strokeStyle = 'red';
context.stroke();
x3 += 1;
loopTimer = setTimeout('circle2('+x3+','+y3+')',20);
}
Animating objects
When doing digital animation there is never need for more than one single timer.
The key is to bind properties to the objects being animation such as its position, speed (or steps), color, shape and so forth.
The logic step therefor is to create custom objects that we can collect this information and use an update function to do all the updates for us in a single step within the loop.
Example
ONLINE DEMO HERE
Lets create an object collection where we store all our objects:
var animObjects = [];
Nothing fancy about that - just an array.
A single loop
To show how simple this can get I will show the loop itself first, then dissect the object. The loop simply iterates through our collection and calls the update method on each object:
function loop() {
/// clear canvas before redrawing all objects
ctx.clearRect(0, 0, demo.width, demo.height);
/// loop through the object collection and update each object
for(var i = 0, animObject; animObject = animObjects[i]; i++) {
animObject.update();
}
/// use this instead of setTimeout/setInterval (!)
requestAnimationFrame(loop);
}
Now, you noticed probably that we used requestAnimationFrame (rAF) here instead of setTimeout or setInterval. rAF allows us to synchronize to the monitor's update frequency whereas setTimout/setInterval cannot. In addition rAF works more efficient than the other two which will benefit us if we need to animate a lot of stuff.
The animation object
Now lets take a look at the object, how come we only need to call update and things animate?
As we saw earlier we create a animated circle object simply by calling:
var animObject = new animCircle(context, x, y, radius, color, stepX, stepY);
This allows us to set which context we want to use (in case we use several layers of canvas), the start position, color and number of steps per frame. Note that these properties can be changed during the animation (e.g. change radius and color).
The object itself looks like this -
function animCircle(ctx, x, y, r, color, stepX, stepY) {
/// keep a reference to 'this' context for external calls
var me = this;
/// make the parameters public so we can alter them
this.x = x;
this.y = y;
this.radius = r;
this.color = color;
this.stepX = stepX;
this.stepY = stepY;
/// this will update the object by incrementing x and y
this.update = function() {
me.x += me.stepX;
me.y += me.stepY;
/// additional updates can be inserted here
render();
}
/// and this takes care of drawing the object with current settings
function render() {
ctx.beginPath();
ctx.arc(me.x, me.y, me.radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = me.color;
ctx.fill();
}
return this;
}
That's all there is to it!
The objects update() method will do all the calculations for us as well as call the internal render() method.
You can create many of these objects at various positions and speeds and animate all of them from a single loop.
I only created an object for the circle to keep things simple. You should be able to create an object for rectangle and what have you by using this as a base. You can of course extent the object to keep track of other things as well such as stroke color, angles and so forth.
I also made the objects bounce off the canvas' edges for the sake of demo. Please adjust as needed.
If i get the question right... why you just dont create three different functions like drawCircle? drawWhatever and create three setTimeouts? or something like that... ?
why you use so manny ' ? there is no reason to do that in:
var loopTimer = setTimeout('draw('+x+','+y+')',20);