Canvas apply different image patterns for differents drawings in a canvas? - javascript

I have 4 images to show in a canvas. I have 4 drawings in it. Each image for each drawing. But when i run the loop, it's applying last image for all drawing.
How i apply different image patterns to different drawings?
Thank you.
function draw(){
var ctx = $('#canvas')[0].getContext('2d');
var $width = $('#canvas').parent().width();
var $canvas_width = 380;
var $canvas_margin = 20;
var $canvas_height = 810;
var $total_draw = $width / Math.ceil($canvas_width);
var $start = 0;
for(var i = 1; i <= $total_draw + 1; i++){
var image = new Image();
image.onload = function(){
ctx.save();
ctx.beginPath();
ctx.moveTo($start,0);
ctx.lineTo($start + $canvas_width,0);
ctx.lineTo($start, $canvas_height);
ctx.lineTo(-380 + $start, $canvas_height);
ctx.lineTo($start,0);
ctx.fillStyle = ctx.createPattern(image, "no-repeat");
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();
$start = $start + ($canvas_width + $canvas_margin);
};
image.src = 'img/samples/b'+i+'.jpg';
}
}

Since you use the same variable (image), the image.src changes every time until you reach the last iteration.
The loop runs his 4 loops before any of your image is loaded, then the last image is loaded and image.onload is called and draw your stuff.
To fix this, take everything outside the for loop and use different variables name. Or you could probably do something like that :
var imgArray = new Array();
for (var i=1; i<=4; i++)
{
imgArray[i] = new Image();
imgArray[i].src = 'img/samples/b' + i + '.jpg';
imgArray[i].onload = function(){
ctx.save();
ctx.beginPath();
ctx.moveTo($start,0);
ctx.lineTo($start + $canvas_width,0);
ctx.lineTo($start, $canvas_height);
ctx.lineTo(-380 + $start, $canvas_height);
ctx.lineTo($start,0);
ctx.fillStyle = ctx.createPattern(image, "no-repeat");
ctx.fill();
ctx.stroke();
ctx.closePath();
ctx.restore();
$start = $start + ($canvas_width + $canvas_margin);
};
}
So the onload function applies to different objects.
UPDATE
Hope you'll be happy with that code :) Sorry, I did not found better images with a sequential number.
http://jsfiddle.net/r5QAF/2/ Just merge this code with yours and you'll probably reach the stuff you want
Here some reading about the problem you were facing (javascript closures in a loop) http://www.mennovanslooten.nl/blog/post/62

Related

how to draw multiple square using a loop to change the position only in java script?

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);
};

changing colour value of drawings after set amount of time with canvas and javascript

I've made an attempt at creating a simple audio visualizer for an mp3 track using canvas to draw/ animate circles in sync with the audio track using the web audio API.
What i've done so far:
What I want to do basically now is change the colours of the circles at a certain amount of time (eg. at a different part of the track - the drop etc). How would I go about doing this? setTimeout? I had a search but could not find anything (and I'm still quite new to JavaScript).
Here is the full code.
// Estabilish all variables tht analyser will use
var analyser, canvas, ctx, random = Math.random, circles = [];
// begins once all the page resources are loaded.
window.onload = function() {
canvas = document.createElement('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
document.body.appendChild(canvas);
ctx = canvas.getContext('2d', {alpha: false});
setupWebAudio(); //loads audio track
for (var i = 0; i < 20; i++) { // loop runs 50 times - creating 49 circles
circles[i] = new Circle();
circles[i].draw();
}
draw();
};
function setupWebAudio() {
var audio = document.createElement('audio');
audio.src = 'rustie.mp3';
audio.controls = true;
document.body.appendChild(audio);
var audioContext = new AudioContext(); // AudioContext object instance (WEB AUDIO API)
//contains an audio signal graph representing connections betweens AudioNodes
analyser = audioContext.createAnalyser(); // Analyser method
var source = audioContext.createMediaElementSource(audio);
// Re-route audio playback into the processing graph of the AudioContext
source.connect(analyser);
analyser.connect(audioContext.destination);
audio.play();
}
function draw() {
requestAnimationFrame(draw);
var freqByteData = new Uint8Array(analyser.frequencyBinCount); //Array of the frequency data from the audio track (representation of the sound frequency)
analyser.getByteFrequencyData(freqByteData); //take the analyser variable run it through the getByteFrequencyData method - passing through the array
ctx.fillStyle = "#ff00ed";
ctx.fillRect(0, 0, canvas.width, canvas.height); //fill the canvas with colour
for (var i = 1; i < circles.length; i++) {
circles[i].radius = freqByteData[i] * 1;
circles[i].y = circles[i].y > canvas.height ? 0 : circles[i].y + 1;
circles[i].draw();
}
}
function Circle() {
this.x = random() * canvas.width; // creates random placement of circle on canvas
this.y = random() * canvas.height;
this.radius = random() * 20 + 20; //creates random radius of circle
this.color = 'rgb(6,237,235)'; //colour of circles
}
Circle.prototype.draw = function() { //Drawing path
var that = this;
ctx.save();
ctx.beginPath();
ctx.globalAlpha = 0.75; //Transparency level
ctx.arc(that.x, that.y, that.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
ctx.restore();
}
Another thing to add also - where in the code is it setting the movement/path of the circles? As they are going from the top to the bottom of the canvas. Wanted to know if I could change this.
Question 1
Instead of hard coding the circle color to this.color = 'rgb(6,237,235)';, you can use a global variable to hold the hue var hue = 0; and then use that in your Circle.draw() like this: ctx.fillStyle = 'hsla(' + hue + ', 50%, 50%, 0.75)';
Note 1: by defining the alpha in the color, you no longer need to set ctx.globalAlpha.
Note 2: you don't need this.color any more.
As you say, you can use setTimeout to change the hue variable at a certain point in time. Or you can use the data in freqByteData to change the hue variable continuously.
More info about hsl color: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hsl()_and_hsla()
Question 2
You are updating the y coordinate of each circle with this line:
circles[i].y = circles[i].y > canvas.height ? 0 : circles[i].y + 1;
Which means: if current y position is greater than the canvas height: set the new value to zero, otherwise increment by one.

Why does canvas shape only render on the last canvas out of 3?

As seen on my jsfiddle, i have 3 canvases that is rendering one shape however it only renders on the last canvas. I guess i might be missing something however i have looped over the canvas elements so i thought it would just work on all of them. I am new to Javascript so sorry if its silly thinking.
var c = document.getElementsByClassName("myCanvas");
for (var canvas of c) {
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.rect(150, 0, 3, 75);
var img = new Image();
img.src = "https://assets.servedby-buysellads.com/p/manage/asset/id/26625";
img.onload = function() {
var pattern = ctx.createPattern(this,"repeat");
ctx.fillStyle = pattern;
ctx.fill();
};
ctx.closePath();
}
My Fiddle:
Here is my JSFiddle
What you get from getElementsByClassName is an HTMLCollection and not an object, so you loop through them in the same way you would loop through an array.
You where trying to loop through it using for (var canvas of c) which should be for (var canvas in c) if it was an object. But as mentioned before, what you have is not an object.
Then, you need to consider that the onload function on an image is async. Since you are using the same image for all of them, it would be ok to load the image once and then paint all canvases after image is loaded.
var c = document.getElementsByClassName("myCanvas");
// First load the image
var img = new Image();
img.onload = function() {
// Once the image is ready, loop through the HTMLCollection
for (var i = 0; i < c.length; i++) {
var canvas = c[i];
var ctx = canvas.getContext("2d");
var pattern = ctx.createPattern(img, "repeat");
ctx.beginPath();
ctx.rect(150, 0, 3, 75);
ctx.fillStyle = pattern;
ctx.fill();
}
};
img.src = "https://assets.servedby-buysellads.com/p/manage/asset/id/26625";

How to rendering Socket data in canvas html5?

1) How to rendering Socket data in canvas html5?
Using constant data i rending canvas.
When i am getting data from socket that is not rending like above one
1) socket data coming like same above example every 10 seconds
I think; your data array should be outside of 'drawWave' method as your fiddle ex and you should call 'drawWave' method just once in 'socket.on'. After a call of 'drawWave'. you should change your data array at 'socket.on' , not at 'drawWave'.
Could you try that?
var data = [];
var drawWaveCaught=false;
socket.on('canvasData', function (incomeData) {
console.log(incomeData);
data = JSON.parse("[" + incomeData + "]");
if(!drawWaveCaught){
drawWaveCaught = true;
that.drawWave();
}
});
function drawWave() {
requestAnimFrame(drawWave);
var n = 0;
var canvas = document.getElementById('canvas');
//alert(id);
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#dbbd7a";
ctx.fill();
ctx.lineWidth = "2";
ctx.strokeStyle = 'green';
// Drawing code goes here
for(n=1;n<=data.length;n++)
{
ctx.beginPath();
ctx.moveTo(n - 1, data[n - 1] * 2);
ctx.lineTo(n, data[n] * 2);
ctx.stroke();
ctx.clearRect(n + 1, 0, 10, canvas.height);
}
}

Need to create a loop using HTML5 canvas and JavaScript

I am using a jQuery carousel to display 38 different magnifications/positions of a large SVG image. I would ideally like to use some sort of loop to go through all the different sizes, draw to an individual canvas and place one in each of the li's in my carousel. Can anyone help me achieve this. Here's what I tried:
function drawSlides() {
for (var i = 1; i <= 38; i++) {
var currentCanvas = 'myCanvas_' + slideNumber;
// initialise canvas element
var canvas_i = document.getElementById('' + currentCanvas + '');
var context = canvas_i.getContext('2d');
// position of SVG – these measurements are subject to change!
var destX_i = -6940;
var destY_i = -29240;
var destWidth_i = 9373;
var destHeight_i = 30000;
context.drawImage('/path/image.svg',
destX_i, destY_i, destWidth_i, destHeight_i);
// white rectangle background – these are constant
var topLeftCornerX_i = 453;
var topLeftCornerY_i = -10;
var width_i = 370;
var height_i = 480;
context.beginPath();
context.rect(topLeftCornerX_i, topLeftCornerY_i, width_i, height_i);
context.fillStyle = "rgba(255, 255, 255, 1)";
context.fill();
// orange vertical line – these elements are constant
context.moveTo(453, 0);
context.lineTo(453, 460);
context.lineWidth = 2;
context.strokeStyle = "#f5d7cb";
context.stroke();
//orange ball – these are constant
var centerX_ball_i = 453;
var centerY_ball_i = 323;
var radius = 99;
context.beginPath();
context.arc(centerX_ball_i, centerY_ball_i, radius, 0, 2 * Math.PI, false);
var grd_ball_i = context.createLinearGradient(224, 354, 422, 552);
grd_ball_i.addColorStop(0, "#f5d7cb"); // light orange
grd_ball_i.addColorStop(1, "#ff4900"); // dark orange
context.fillStyle = grd_ball_i;
context.fill();
}
};
drawSlides();
This should get you moving:
var numCarouselItems = 38;
var myUL = document.getElementById('carousel');
var items = myUL.childNodes;
var img = new Image;
img.onload = function(){
for (var i=0;i<numCarouselItems;++i){
// Find the nth li, or create it
var li = items[i] || myUL.appendChild(document.createElement('li'));
// Find the nth canvas, or create it
var canvas = li.getElementsByTagName('canvas')[0] ||
li.appendChild(document.createElement('canvas'));
canvas.width = 1; // Erase the canvas, in case it existed
canvas.width = 320; // Set the width and height as desired
canvas.height = 240;
var ctx = canvas.getContext('2d');
// Use your actual calculations for the SVG size/position here
ctx.drawImage( img, 0, 0 );
}
}
// Be sure to set your image source after your load handler is in place
img.src = "foo.svg";

Categories