How to display several different images in canvas? - javascript
I get an issue with my javascript code... I'd like to display 5 lines of different pictures in my canvas but only one line is displayed and the LIFE function is only called 16 times (canvas.width / imgW).
Why the LIFE function can't be called after the 'while' loop ?
Thank you !
This is the result i get
<script type="text/javascript">
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var tabImages = ['css3.svg', 'adobe-1.svg', 'html-5.svg', 'jquery.svg'];
var canvasW = canvas.width;
var canvasH = canvas.height;
var imgW = 50;
var imgH = 50;
var x = 0, y = 0, i = 0, posY = 1, increImg = 0, posX = 0;
/* I want to display 5 lines of pictures but only one is displayed */
for(var lignes = 0 ; lignes < 5 ; lignes++) {
i = 0;
while(posX < canvasW) {
/* That function is only called 16 times but i know only that
way to display several differentes images in canvas */
(function(posX, posY) {
var img = new Image();
if(increImg === tabImages.length-1)
increImg = 0;
img.src = 'testanim/img/'+tabImages[increImg];
img.onload = function() {
ctx.drawImage(img, posX, posY, imgW, imgH);
}
})(posX, posY)
i++;
increImg++;
posX = imgW * i;
}
posY = lignes * imgH;
}
</script>
are you looking for something like à kind of gallery :
//our images
let imageArr = ["animage","another","another","another","another","another","another","another","another","another"];
//getting the context (mycanvas is the id of the canvas element)
let context = mycanvas.getContext("2d")
//checking the context
if(context)
// for all images in my array(can't do for in because of the calculation of position later on )
for(let i = 0;i<imageArr.length;i++)
{
//creation of an image html element
let imgel = document.createElement('img');
//addition of src tag
imgel.src=imageArr[i];
//drawing of the image (parameters:(image, xcoordinates,ycoordinates,width,height)
context.drawImage(imgel, (i%5)*100, (Math.floor(i/5))*100,100,100);
//drawing a rectangle around them
context.strokeRect((i%5)*100,(Math.floor(i/5))*100,100,100);
}
canvas{border:1px solid black;}
<canvas id="mycanvas" height="500" width="500"></canvas>
so in order to have several lines of your image in your code you have to use % operator:
for instance if you want 5 lines this gives you :
(i%canvasW)*imgW
what this does it simply stops after 5 images and if you have to go to the line(see below) it will start from 0 again : 0%5 = 0 1%5 = 1 , ... , 5%5 = 0 6%5 = 1
On the other side for the height if you want to go to the line you need to do some kind of oposite calculation which is / this gives you :
Math.floor(i/5)*imgH
This gives you the lines you are hoping for
so if i resume you will have :
ctx.drawImage(img, (i%canvasW)*imgW, Math.floor(i/5)*imgH, imgW, imgH);
and that's also why your while loop is not going through several lines, you have to calculate your posX differently like so :
posX = (i%canvasW)*imgW;
and throw in another variable for the height which will be the condition to stop your while loop
for instance let's say you want to stop once the last image of the last line is hit you would have to calculate this condition like so :
if(canv.getContext("2d"))
{
let max = canv.width*canv.height;
let current = 0;
let imgW=50;
let imgH=50;
let i = 0;
while(current<=max)
{
console.log("stuff");
current = (Math.floor(i/canv.height)*imgH)*((i%canv.width)*imgW);
i++;
}
}
<canvas id="canv" width="500" height="500"></canvas>
i strongly advise using for loops since you almost can calculate the number of steps for every problem :
for(let i = 0; i<(canvas.width*canvas.height);i++)
Related
How to check image data of image in javascript?
I want to make a minecraft sort of thing in javascript, and I have a textures image already (an image with all of the textures of the block). Is there a way to get the specific pixel data for said image without drawing it on a canvas? I don't really know how to upload files to stackoverflow, to make the images work, but I have tried anyways. I think I have done it using an image and canvas element, it works about three fourths of the time: //size of individual textures var textureWidth = 16; var textureHeight = 16; //get the image var image = document.getElementById("texturesImage"); console.log(image); //get the image canvas //or textures canvas or tcanvas var tcanvas = document.getElementById("textures"); var tctx = tcanvas.getContext("2d"); tcanvas.width = image.naturalWidth; tcanvas.height = image.naturalHeight; var imgWidth = image.naturalWidth; var imgHeight = image.naturalHeight; console.log(tcanvas.width); console.log(tcanvas.height); tctx.drawImage(image, 0, 0); //accepts a full integer between 0 and 15 function getTextureData(blockID) { if (!Number.isInteger(blockID) || blockID < 0 || blockID > 15) { console.log(blockID + " is not a valid ID."); return (false); } //get texture x and y var tx = blockID % (imgWidth / textureWidth) * textureWidth; var ty = Math.floor(blockID / (imgWidth / textureWidth)) * textureHeight; //console.log(tx,ty); //return image data return (tctx.getImageData(tx, ty, textureWidth, textureHeight)); } console.log(getTextureData(13)); #textures { background-color: orange; } Hello world <canvas id="canvas"></canvas> <canvas id="textures"></canvas> <image src="/BlockTextures.png" id="texturesImage"></image> <!--scripts--> <script src="engine/1setup.js"></script> <script src="engine/2getTextures.js"></script> <script src="script.js"></script>
Uncaught TypeError: Cannot set property 'onclick' of null. Tried window.onload
So, I'm relatively new to Javascrip. Though what I want to do is give a moving image on my canvas an id, so that I can ultimately use onclick to use the image as a clickable image, so I can redirect the user to another page, which is what would happen when the image is clicked. Here is my code so far. I need help. If you need any more clarification I will try to explain further. var ctx; var imgBg; var imgDrops; var x = 0; var y = 0; var noOfDrops = 50; var fallingDrops = []; function drawBackground(){ ctx.drawImage(imgBg, 0, 0); //Background } function draw() { drawBackground(); for (var i=0; i< noOfDrops; i++) { ctx.drawImage (fallingDrops[i].image, fallingDrops[i].x, fallingDrops[i].y); //The rain drop fallingDrops[i].y += fallingDrops[i].speed; //Set the falling speed if (fallingDrops[i].y > 1000) { //Repeat the raindrop when it falls out of view fallingDrops[i].y = -25 //Account for the image size fallingDrops[i].x = Math.random() * 10000; //Make it appear randomly along the width } } } function setup() { var canvas = document.getElementById('canvasRegn'); if (canvas.getContext) { ctx = canvas.getContext('2d'); imgBg = new Image(); imgBg.src = "http://images.susu.org/unionfilms/films/backgrounds/hd/space-jam.jpg"; setInterval(draw, 36); for (var i = 0; i < noOfDrops; i++) { // Charles Barkley var fallingDr = new Object(); fallingDr["image"] = new Image(); fallingDr.image.src = 'http://xenboards.ignimgs.com/external_data/attachments/8/8795-f09b907a01726a25ca2fbd2f588e3f0e.jpg'; fallingDr["x"] = Math.random() * 10000; fallingDr["y"] = Math.random() * 5; fallingDr["speed"] = 3 + Math.random() * 5; fallingDrops.push(fallingDr); // Bugs bunny var fallingDr2 = new Object(); fallingDr2["image"] = new Image(); fallingDr2.image.src = 'http://i.imgur.com/zN2CSAf.png' fallingDr2["x"] = Math.random() * 10000; fallingDr2["y"] = Math.random() * 5; fallingDr2["speed"] = 3 + Math.random() * 5; fallingDrops.push(fallingDr2); // Michael Jordan var fallingDr3 = new Object(); fallingDr3["image"] = new Image(); fallingDr3.image.src = 'http://i.imgur.com/XxvJiGg.png' fallingDr3["x"] = Math.random() * 10000; fallingDr3["y"] = Math.random() * 5; fallingDr3["speed"] = 3 + Math.random() * 5; fallingDrops.push(fallingDr3); // Daffy duck var fallingDr4 = new Object(); fallingDr4["image"] = new Image(); fallingDr4.image.src = 'http://i.imgur.com/QZogw2L.png' fallingDr4["x"] = Math.random() * 10000; fallingDr4["y"] = Math.random() * 5; fallingDr4["speed"] = 3 + Math.random() * 5; fallingDrops.push(fallingDr4); fallingDr4.image.id = "Daffy"; } } } setup(); window.onload = function(){ document.getElementById("Daffy").onclick=function(){ alert("Hello World"); } }
Try: fallingDr4.image.onclick=function(){ alert(this.id); } should alert "Duffy".
Your problem is that you're trying to catch the click event on an element that is not in the document and hence, not clickable by the user. When you call var img = new Image() a new <img> element is created, with all its properties that you can already modify in your javascript. But this element is only available to your scripts, and is not displayed into the page until you call document.anyElement.appendChild(img). So it's better to consider this as an imageObject more than to an element (even if it actually also is). What is in your document, and accessible to your user, is the <canvas> element. So if you want to know if the user has clicked, you will have to attach the eventListener to this canvasElement. But the canvasElement doesn't know what it does represent. When you call context.drawImage(), you're just applying the pixels from the imageSource to the ones of the canvas, and all reference to the original imageObject are lost. To workaround this, you'll then have to store the position of your drawn image into the canvas, and then check if the click event you caught was inside these positions. Click events' clientX and clientY properties of the Event passed as arguments of you handler will give you the position of the cursor when the event occurred. But the positions are relative to the top-left corner of the window. So you'll also need to make these relative to the top-left corner of your canvas, which can be done by calling the getBoundingClientRect() method of the canvasElement. Here is a simplified example : // this function will be called at image's load var init = function() { // a reference to our "in-screen" canvasElement var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); // "kitty" will be the js object that will help us know if we clicked on our image // "this" refers to the imageObject var kitty = { width: this.width, height: this.height, // random positions left: Math.random() * (canvas.width - this.width), top: Math.random() * (canvas.height - this.height), }; // draw our image at the random positions we created ctx.drawImage(this, kitty.left, kitty.top, kitty.width, kitty.height); // here we're listening to mousemove event, // but click event shares the same clientX & clientY properties var moveHandler = function(evt) { // in the "evt" object passed, we can get the x and y positions relative to the window // so we make these relatives to the canvas ones var canvasRect = canvas.getBoundingClientRect(); var x = evt.clientX - canvasRect.left; var y = evt.clientY - canvasRect.top; // now we've got our relative positions, we can check if we were inside the image if (x >= kitty.left && x <= (kitty.left + kitty.width) && y >= kitty.top && y <= (kitty.top + kitty.height) ) { // we are over the image, do something canvas.style.cursor = 'pointer'; document.body.style.backgroundColor = 'lightblue'; } else { canvas.style.cursor = 'default'; document.body.style.backgroundColor = 'transparent'; } }; // attach this event handler to the canvasElement canvas.addEventListener('mousemove', moveHandler); }; // this will create an imageObject, which will stay "off-screen" (never appendded to the document) var img = new Image(); // wait that the image has loaded before trying to make any magic img.onload = init; img.src = "http://lorempixel.com/200/70/cats"; body { width: 100vw; text-align: center; } canvas { margin: 0 auto; position: relative; } <canvas id="canvas" width="500" height="500"></canvas> (you may need to scroll to actually see the image, or go fullscreen)
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>
Blending two ImageData into one ImageData with an offset in Javascript
I'm trying to blend two ImageData objects into a single object in order to obtain result similar to the pictures shown in this link The following is the Javascript code that has the two ImageData var redImage = copy.getImageData((SCREEN_WIDTH - VIDEO_WIDTH)/2,(SCREEN_HEIGHT - VIDEO_HEIGHT)/2,VIDEO_WIDTH,VIDEO_HEIGHT); var bluImage = copy.getImageData((SCREEN_WIDTH - VIDEO_WIDTH)/2,(SCREEN_HEIGHT - VIDEO_HEIGHT)/2,VIDEO_WIDTH,VIDEO_HEIGHT); var redData = redImage.data; var blueData = blueImage.data; // Colorize red for(var i = 0; i < redData.length; i+=4) { redData[i] -= (redData[i] - 255); } redImage.data = redData; // Draw the pixels onto the visible canvas disp.putImageData(redImage,(SCREEN_WIDTH - VIDEO_WIDTH)/2 - 25,(SCREEN_HEIGHT - VIDEO_HEIGHT)/2); // Colorize cyan for(var i = 1; i < blueData.length; i+=4) { blueData[i] -= (blueData[i] - 255); blueData[i+1] -= (blueData[i+1] - 255); } blueImage.data = blueData; // Draw the pixels onto the visible canvas disp.putImageData(blueImage,(SCREEN_WIDTH - VIDEO_WIDTH)/2 + 25,(SCREEN_HEIGHT - VIDEO_HEIGHT)/2); How do i merge/blend the redData and blueData before putting it on the canvas ?
The formula you can use to mix two images is fairly simple: newPixel = imageMainPixel * mixFactor + imageSecPixel * (1 - mixFactor) Example assuming both buffers are of equal length: var mixFactor = 0.5; //main image is dominant //we're using the red buffer as main buffer for this example for(var i = 0; i < redData.length; i+=4) { redData[i] = redData[i] * mixFactor + blueData[i] * (1 - mixFactor); redData[i+1] = redData[i+1] * mixFactor + blueData[i+1] * (1 - mixFactor); redData[i+2] = redData[i+2] * mixFactor + blueData[i+2] * (1 - mixFactor); } Now your red buffer contains the mixed image. To add an offset you can simply redraw the images with an offset value, for example: var offset = 20; //pixels copy.drawImage(originalImage, -offset, 0); // <-- var redImage = copy.getImageData( /*...*/ ); copy.drawImage(originalImage, offset, 0); // --> var bluImage = copy.getImageData( /*...*/ );
If you have not onlyImageDataobjects, but also sourcecanvaselements, you can use this method. You can obtain base64-encoded image data by callingtoDataURLcanvas method. Then you can createImageelement from that data and then paste that image to destination canvas viadrawImage. Example code: function mergeImageData(callback, sources) { var canvas = document.createElement('canvas'), context, images = Array.prototype.slice.call(arguments, 1).map(function(canvas) { var img = new Image(); img.onload = onLoad; img.src = canvas.toDataURL(); return img; } ), imgCounter = 0, widths = [], heights = []; function onLoad() { widths.push(this.width); heights.push(this.height); if (++imgCounter == images.length) { merge(); }; }; function merge() { canvas.width = Math.max.apply(null, widths); canvas.height = Math.max.apply(null, heights); context = canvas.getContext('2d'); images.forEach(function(img) { context.drawImage(img, 0, 0, img.width, img.height); } ); callback(context.getImageData(0, 0, canvas.width, canvas.height)); }; };
what about functions of setting the transmission format 3d - from format full side by side to anaglyph, alternating rows, alternating columns, chessboard, original side by side and 2d from 3d ?
Javascript Pixel Manipulation: These aren't my colors
I know questions like this have been asked several time, but I have yet to find just what I'm looking for. I am reading an image into a canvas object (in javascript) and trying to manipulate some specific pixels. For example, I am looking for the color RGB: 224 64 102, and trying to change this to a different color. I can apply greyscale to the image, so I know the manipulation works, but the code is not finding any pixels with this color (that Adobe Illustrator said was the RGB color). I'm hoping I'm just missing a small detail. The code is below, hopefully someone will see it. Thanks! var canvas = document.getElementById("testcanvas"); var canvasContext = canvas.getContext('2d'); imgObj = new Image(); imgObj.src = "ss.jpg"; //imgObj.width = 200; //imgObj.height = 200; var imgW = imgObj.width; var imgH = imgObj.height; canvas.width = imgW; canvas.height = imgH; canvasContext.drawImage(imgObj, 0, 0); var imgPixels = canvasContext.getImageData(0, 0, imgW, imgH); //hash_table = {}; for (var x = 0; x < imgPixels.width; x++) { for (var y = 0; y < imgPixels.height; y++) { var i = (y * imgPixels.width + x) * 4; //Want to go from: //E04066 //224 64 102 -> to //134 135 185 if(imgPixels.data[i] == 224 && imgPixels.data[i+1] == 64 && imgPixels.data[i+2] == 102) { imgPixels.data[i] = 134; imgPixels.data[i+1] = 135; imgPixels.data[i+2] = 185; } //To greyscale: /* var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3; imgPixels.data[i] = avg; imgPixels.data[i + 1] = avg; imgPixels.data[i + 2] = avg; imgPixels.data[i + 3] = 255; */ } } canvasContext.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height); //color_count = 0; //for(key in hash_table) { // color_count++; //} //console.log(color_count); //console.log(hash_table); return canvas.toDataURL(); }); }); </script> </head> <body> <canvas id="testcanvas"></canvas> <img src="ss.jpg" id="testimage"/>
You are probably unable to get image data from canvas because the canvas has been tainted by cross-origin data. If that file, ss.jpg is local then it won't work. I imagine that's the case. Search for canvas cross-origin on SO or Google for more information on that. There's a lot out there. Here's a bit of an explanation: http://simonsarris.com/blog/480-understanding-the-html5-canvas-image-security-rules Here's a site about enabling it on your server: http://enable-cors.org/ Otherwise, your code works. Here is the same code converting a tiny red dot into a tiny green dot: http://jsfiddle.net/RBaxt/
Canvas really don't work with .JPG format. You have to convert your image into .PNG using any picture editing tool like Photoshop. Your code works well.
I think you are loading an Image that is not ready to be painted. Below I have updated your code above, though I have not test it but I feel it could lead you somewhere var canvas = document.getElementById("testcanvas"); var canvasContext = canvas.getContext('2d'); imgObj = new Image(); imgObj.src = "ss.jpg"; //imgObj.width = 200; //imgObj.height = 200; var imgW = imgObj.width; var imgH = imgObj.height; imgObj.onload = function(){ //Put the pixel manipulation code here; // This ensures the image has been loaded before it is accessed. }