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.
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 trying to display particles on my screen, and then animate them in a way. Like just moving around and boucing on the screen. I cannot find the error which does not allow me to display these particles.
Best regards,
Tar2ed
// Initializing the canvas
var canvas = document.getElementById("canvas");
var c = canvas.getContext('2d');
// Setting the positition in the middle of the canvas
var posX = 512,
posY = 384;
// Creation of an array of particles
var particles = [];
for (var i = 0; i < 50; i++) {
particles.push(new Particle());
}
// Creation of a fucntion which will help us create multiple particles
function Particle() {
// Randomizing the position on the canvas
this.posX = Math.random() * canvas.width;
this.posY = Math.random() * canvas.height;
}
// Creating a draw function
function draw() {
// Painting the canvas in black
c.fillStyle = "black";
c.fillRect(0, 0, canvas.width, canvas.height);
for (var d = 0; d < Particle.length; d++) {
var p = particles[d];
// Creating the particle
c.beginPath();
c.fillStyle = "white";
c.arc(p.posX, p.posY, 5, Math.PI * 2, false);
c.fill();
// Incrementing the X and Y postition
p.posX++;
p.posY++;
};
}
// Drawing the particle
setInterval(draw, 33);
<canvas id="canvas"></canvas>
In the for-loop: Particle.length should be particles.length.
The arguments for c.arc are wrong. They should be: c.arc(p.posX, p.posY, 5, 0, Math.PI * 2);
You should consider utilizing window.requestAnimationFrame() for calling the draw-loop.
Demo from your example:
// Initializing the canvas
var canvas = document.getElementById("canvas");
var c = canvas.getContext('2d');
// Setting the positition in the middle of the canvas
var posX = 512,
posY = 384;
// Creation of an array of particles
var particles = [];
for (var i = 0; i < 50; i++ ){
particles.push(new Particle());
}
// Creation of a fucntion which will help us create multiple particles
function Particle() {
// Randomizing the position on the canvas
this.posX = Math.random() * canvas.width;
this.posY = Math.random() * canvas.height;
}
// Creating a draw function
function draw() {
// Painting the canvas in black
c.fillStyle = "black";
c.fillRect(0, 0, canvas.width, canvas.height);
for (var d = 0; d < particles.length; d++) {
var p = particles[d];
// Creating the particle
c.beginPath();
c.fillStyle = "white";
c.arc(p.posX, p.posY, 5, 0, Math.PI * 2);
c.fill();
// Incrementing the X and Y postition
p.posX++;
p.posY++;
}
}
// Drawing the particle
setInterval(draw, 33);
<canvas id="canvas" width="310" height="160">
how can I fill the "new" canvas circle that appears next to the older one.
There is no problem with rectangle for example:
**
ctx.fillStyle = 'rgba('+quadratto.r+','+quadratto.g+','+quadratto.b+',1)';
quadratto.x += quadratto.speedX;
quadratto.y += quadratto.speedY;
quadratto.speedY += quadratto.speedY*(-0.15);
ctx.fillRect(quadratto.x-quadratto.h/4, quadratto.y-quadratto.h/2, 2, 2);**
What I want to do?
I'm creating animation in canvas where random-sized-color circle will appear and
it will move in a specified direction. The new canvas layaer will appear in the next frame (fps) with a new(old) circle.
var myCanvasPattern = document.createElement('canvas');
myCanvasPattern.width = window.innerWidth;
myCanvasPattern.height = window.innerHeight;
document.body.appendChild(myCanvasPattern);
var ryC = myCanvasPattern.getContext('2d');
function lottery(min, max) {
return Math.floor(Math.random()*(max-min+1))+min;
}
var allQuadro = [];
var fps = 50;
var lastTime = 0;
animationLoop();
function animationLoop(time){
requestAnimationFrame( animationLoop );
if(time-lastTime>=1000/fps){
lastTime = time;
for(var i=0;i<10;i++){
allQuadro.push({
r : lottery(0, 240),
g : lottery(0, 240),
b : lottery(0, 240),
circleR : lottery(10, 30),
x : myCanvasPattern.width/2,
y : myCanvasPattern.height/2,
speedX : lottery(-1000,1000)/100,
speedY : lottery(-1000,1000)/100
})
}
ryC.fillStyle = 'rgba(255,255,255,0.2)';
ryC.fill(0,0,myCanvasPattern.width, myCanvasPattern.height);
for(var i=0; i<allQuadro.length;i++){
var circle = allQuadro[i];
ryC.fillStyle = 'rgba('+circle.r+','+circle.g+','+circle.b+',1)';
circle.x += circle.speedX;
circle.y += circle.speedY;
//HERE's THE PROBLEM BELOW. HOW TO CREATE NEW ONE THAT APPEARS NEXT TO PREVIOUS ONE WITH NEW RANDOM COLOR
ryC.arc(circle.x-circle.circleR/2, circle.y-circle.circleR/2, circleR, 0, 2 * Math.PI);
//ryC.fill();
}
// ryC.fillStyle = 'rgba('+r+','+g+','+b+',1)';
//ryC.arc(x+speedX, y+speedY, circleR, 0, 2 * Math.PI);
//ryC.fill();
}
}
body {
margin: 0;
padding: 0;
overflow: hidden;
}
The fillRect() will fill directly to the canvas without going via a path (versus for example rect()).
The arc() on the other hand will add to a path which needs to be filled later. It also require the path to be cleared in-between the calls using beginPath().
A simple way to think about it is to wrap the necessary code into a function that acts like fillRect():
function fillArc() {
ctx.beginPath(); // clear current path
ctx.arc.apply(ctx, arguments); // apply arguments to arc() call
ctx.fill();
// notice that the arc still exist on the path after this call
// so to "truly" behave like fillRect() you could consider an
// additional beginPath() here.. it will depend on your code
}
In action:
var ctx = c.getContext("2d");
ctx.fillStyle = "#09f";
fillArc(70, 70, 70, 0, 6.28);
ctx.fillStyle = "#0a9";
fillArc(220, 70, 70, 0, 6.28);
function fillArc() {
ctx.beginPath();
ctx.arc.apply(ctx, arguments);
ctx.fill();
}
<canvas id=c></canvas>
If you are bold you can also add the method to the context itself before calling getContext():
CanvasRenderingContext2D.prototype.fillArc = function() {
this.beginPath();
this.arc.apply(this, arguments);
this.fill();
}
The use it like any other method:
ctx.fillArc( ... );
CanvasRenderingContext2D.prototype.fillArc = function() {
this.beginPath();
this.arc.apply(this, arguments);
this.fill();
}
var ctx = c.getContext("2d");
ctx.fillStyle = "#09f";
ctx.fillArc(70, 70, 70, 0, 6.28);
ctx.fillStyle = "#0a9";
ctx.fillArc(220, 70, 70, 0, 6.28);
<canvas id=c></canvas>
The dots are arranged so they have a regular distance between them.
they are drawn at (x%4==0) and (y%4==0), they take time to be drawn in a brute force way:
for (var x = 0; x < width; x+=4) {
for (var y = 0; y < height; y+=4) {
draw(x, y, 1, 1);
}
}
how to do it in a better way?
You can use createPattern() by first creating an offscreen-canvas, draw a single dot into it, then use that canvas as a image source for createPattern(). Set pattern as fillStyle and fill.
var ctx = c.getContext("2d");
var pattern = ctx.createPattern(createPatternImage(), "repeat");
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, c.width, c.height);
function createPatternImage() {
var ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = ctx.canvas.height = 4; // = size of pattern base
ctx.fillStyle = "#fff";
ctx.fillRect(0,0,1,1);
return ctx.canvas; // canvas can be used as image source
}
<canvas id=c style="background:#c00"></canvas>
If this is not an option you can always optimize the code you have now by not filling each dot, but adding to the path and fill once:
var ctx = c.getContext("2d"); // obtain only one time.
var width = c.width, height = c.height;
ctx.beginPath(); // clear path if it has been used previously
ctx.fillStyle = "#fff";
for (var x = 0; x < width; x+=4) {
for (var y = 0; y < height; y+=4) {
draw(x, y, 1, 1);
}
}
// modify method to add to path instead
function draw(x,y,width,height) {
ctx.rect(x,y,width,height);
}
// when done, fill once
ctx.fill();
<canvas id=c style="background:#c00"></canvas>
$(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.