Canvas to data url not working properly - javascript

I have a function that takes an img-element and returns a data url. This works like 7/10 times and returns a blank image 3/10 times. I view the created data url through my browser(chrome) and use the same images so I know that this function is returning broken images. Can anyone spot why?
function imgToDataURL(img) {
var canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
var c = canvas.getContext('2d');
c.drawImage(img, 0, 0);
dataurl = canvas.toDataURL();
return dataurl;
}
$(function() {
img = new Image();
img.crossOrigin = '';
img.onload = function() {
newimg = new Image();
newimg.onload = function() {
document.body.appendChild(newimg);
}
newimg.src = imgToDataURL(img);
}
img.src = 'https://somedomain.s3.amazonaws.com/someimg.png';
});
This example works most of the time but sometimes ends with a large white rectangle instead of the image.

You have to wait for images to load before drawing them using .drawImage function.
Only after the image is loaded by the browser you can call your imgToDataURL function.
Something like this:
var image_sources = ["img1.png", "img2.png", "..."];
for(var i=0; i<image_sources.length; i++) {
var new_img = document.createElement("img");
new_img.onload = function() {
imgToDataURL(this);
};
new_img.src = image_sources[i];
}

Related

Canvas drawImage is not accepting width and height

Image is getting created in full original size, even last two arguments 150, 150 are height and width context.drawImage(img, 0, 0, 150, 150); in the code below:
function (file) { //uploaded files are always images
var reader = new FileReader(); //FileReader for uploading files from local stroge.
reader.onload = function () {
var links = document.createElement('a'); //link when image is clicked
var img = document.createElement('img');
img.src = reader.result; //src = url from uploaded file
img.className = 'images'; //css -> .images { margin-top: 30px; padding: 30px; }
img.onload = function () { //repaint image to 150 - 150 size with canvas, because setting width and height on image itself would just resize the image but I want to create new image with new size
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0, 150, 150) //draw image with canvas
}
links.href = reader.result; // url from local storage needed when image is clicked -
links.target = "_blank"; // open new blank page with original image
links.appendChild(img); // image is appended to <a>
document.body.appendChild(links); // <a> is appended to body, that body contains image thumbnail with a link linked to the image source
}
if (file) {
reader.readAsDataURL(file); // read uploaded files url
}
}
img.onload does not making any sense here. result is the same even when I remove it.
You are not drawing back the cropped image to your <img> tag... you will have to create two image Objects, let's call the first originalImage, and the second one croppedImage.
The one you will append to the document is croppedImageand originalImage will just stay in the cache.
When originalImage has loaded, you will paint it to a canvas, and then set croppedImage to the result of the canvas' toDataURL() method.
var read = function() {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function() {
var links = document.createElement('a');
// this will be the appended image
var croppedImage = new Image();
// do your DOM stuff
croppedImage.className = 'images';
links.href = reader.result;
links.target = "_blank";
links.appendChild(croppedImage);
document.body.appendChild(links);
// create a buffer image object
var originalImage = new Image();
// set its load handler
originalImage.onload = function() {
// create a canvas
var canvas = document.createElement('canvas');
// set canvas width/height
canvas.width = canvas.height = 150;
var context = canvas.getContext('2d');
// draw the buffered image to the canvas at required dimension
context.drawImage(originalImage, 0, 0, 150, 150);
// set the appended to doc image's src to the result of the cropping operation
croppedImage.src = canvas.toDataURL();
}
originalImage.src = reader.result;
}
if (file) {
reader.readAsDataURL(file);
}
};
upload.onchange = read;
.images {
margin-top: 30px;
padding: 30px;
}
<input type="file" id="upload" />
You could also have used only a single image object, but this would have required to reset the onload event in the onload event, to avoid an infinite loop, which is a little bit less clear :
var read = function() {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function() {
var links = document.createElement('a');
var img = new Image();
img.className = 'images';
links.href = reader.result;
links.target = "_blank";
links.appendChild(img);
document.body.appendChild(links);
img.onload = function() {
//reset the onload event so it does fire in a loop
img.onload = function(){return;};
var canvas = document.createElement('canvas');
canvas.width = canvas.height = 150;
var context = canvas.getContext('2d');
context.drawImage(this, 0, 0, 150, 150);
this.src = canvas.toDataURL();
}
img.src = reader.result;
}
if (file) {
reader.readAsDataURL(file);
}
};
upload.onchange = read;
.images {
margin-top: 30px;
padding: 30px;
}
<input type="file" id="upload" />
var reader = new FileReader();
reader.onload = function () {
var links = document.createElement('a');
var img = new Image();
img.src = reader.result;
img.className = 'images';
img.onload = function () {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.drawImage(this, 0, 0, 150, 150);
this.src = canvas.toDataURL(); // convert the canvas back to the image
links.appendChild(this); // append the updated image to the document
}
links.href = reader.result;
links.target = "_blank";
document.body.appendChild(links);
}
if (file) {
reader.readAsDataURL(file); //reads the data as a URL
}

How can I load an array of images to be faded?

I have the following code where I am loading only one image. But I want to load multiple images from e folder and then to fade them in and out. But what I am interesting in, is how can I load all images from a folder without to repeat the code, as src="frames/frame_1" src="frames/frame_2" i want something soft as src="frames/frame_" + i + ".jpg" where i is the number of the frame
this is how I load only one image now
var img = new Image();
var div = document.getElementById('foo');
img.onload = function() {
div.appendChild(img);
};
img.src = 'frames/frame_1.jpg';
It's relatively simple. You basically put the code you already had in a loop:
var img, i,
imageCount = 5,
div = document.getElementById('foo');
for(i = 0; i < imageCount; i++){
img = new Image();
img.onload = function() {
div.appendChild(img);
};
img.src = 'frames/frame_' + i + '.jpg';
}
You can use a loop for :
var img,
div = document.getElementById('foo');
for (var i = 0, nbImg = 5; i < nbImg; i++) {
img = new Image();
img.onload = function() {
div.appendChild(img);
};
img.src = 'frames/frame_'+i+'.jpg';
}

javascript drawImage() issues

Currently having some issues with drawImage();. Namely it wont actually draw. I tried it out with fillRect(); and it worked aswell as putting the drawImage(); inside the the onload function aswell (which worked).
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 640;
canvas.height = 400;
document.body.appendChild(canvas);
var tileArray = [
[0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,1,1,1,1,0,0,1,0,0,0],
[0,0,0,1,1,1,1,0,1,1,1,0,0],
[0,0,0,1,1,1,0,0,0,0,1,1,0],
[0,0,0,1,1,1,0,0,0,0,0,0,0],
[0,0,0,1,1,1,0,0,0,0,0,0,0],
[0,0,0,0,1,1,1,0,0,0,0,0,0],
[0,0,0,0,1,1,1,0,0,0,0,0,0],
[0,0,0,0,0,1,1,1,0,0,0,0,0],
[0,0,0,0,0,0,1,1,0,0,0,0,0]
];
var grassReady = false;
var grass = new Image();
grass.onload = function() {
grassReady = true;
};
grass.src = "images/grass.png";
var sandReady = false;
var sand = new Image();
sand.onload = function() {
sandReady = true;
};
sand.src = "images/sand.png";
var posX = 0;
var posY = 0;
if(grassReady) {
ctx.drawImage(grass, posX, posY);
}
Any pointers as to why this is would be greatly appreciated and I appologize in advance if messed up the code section in anyway. I went through other similar posts and coulden't find a solution that seemed to work.
As #Suman Bogati correctly says, you must wait for your images to load before using them in drawImage.
A Demo: http://jsfiddle.net/m1erickson/jGPGj/
Here's an image loader that preloads all images and then calls the start() function where you can use drawImage because all the images are fully loaded.
var imageURLs=[]; // put the paths to your images here
var imagesOK=0;
var imgs=[];
imageURLs.push("images/grass.png");
imageURLs.push("images/sand.png");
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
}
}
function start(){
// the imgs[] array holds fully loaded images
// the imgs[] are in the same order as imageURLs[]
// grass.png is in imgs[0]
// sand.png is in imgs[1]
}
This statement ctx.drawImage(); should be inside the grass.onload = function() {} function, something like
grass.onload = function() {
ctx.drawImage(grass, posX, posY);
}
If you define drawImage() outside the grass.onload() function, then that statment would executed first, so at that point grassReady is false, So the condition is not satisfied.
Bascially it's related to asynchronous concept.
Your code is running into order
1) First
var grassReady = false;
if(grassReady) {
//grassReady is false, this condition is not satisfied
ctx.drawImage(grass, posX, posY);
}
2) Second
grass.onload = function() {
grassReady = true;
};

How do I load a local image using JS?

I want to load two separate images in my script. I've accomplished it using:
<img src="iphone4.png" id="img1">
<img src="screenshot.png" id="img2">
<script>
window.onload = function () {
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');
</script>
Problem here though is that the images should not be visible on the page but are when loaded using markup. I simply want to load them through the script without first having to add them in the markup. I realize this is an extremely trivial problem, but searching for a solution has given me nothing.
I tried this approach:
window.onload = function () {
var img1 = "iphone4.png";
var img2 = "screenshot.png";
But this did not work.
Can someone with some common JS sense please give me some input on this issue.
EDIT :
So this is how the markup/JS looks now, the images are still displayed and the final merge of the images won't show. The error I get is:
IndexSizeError: Index or size is negative or greater than the allowed amount
[Stanna vid fel]
var image1 = context.getImageData(0, 0, width, height);
And this is the syntax:
<body>
<img src="" id="img1">
<img src="" id="img2">
<p>Blended image<br><canvas id="canvas"></canvas></p>
<script>
window.onload = function () {
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');
img1.src = "iphone4.png";
img2.src = "screenshot.png";
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = img1.width;
var height = img1.height;
canvas.width = width;
canvas.height = height;
var pixels = 4 * width * height;
context.drawImage(img1, 0, 0);
var image1 = context.getImageData(0, 0, width, height);
var imageData1 = image1.data;
context.drawImage(img2, 73, 265);
var image2 = context.getImageData(0, 0, width, height);
var imageData2 = image2.data;
while (pixels--) {
imageData1[pixels] = imageData1[pixels] * 0 + imageData2[pixels] * 1;
}
image1.data = imageData1;
context.putImageData(image1, 0, 0);
};
</script>
You can create an Image without having the actual tag in the markup:
var img = new Image();
img.src = 'iphone4.png';
//use img however you want
Hope this helps.
window.onload = function () {
var img1 = new Image();
var img2 = new Image();
//EDIT2 you can hide img, or simply not add them to the DOM...
img1.style.display = "none";
img2.style.display = "none";
img1.src = "iphone4.png";
img2.src = "screenshot.png";
EDIT: DO NOT DO THAT and your images won't be displayed
document.body.append(img1);
OR
document.getElementById("myID").append(img2);
"What I'm doing is merging two images using JS"
Your problem is probably due to the fact that you are trying to draw images that have not been loaded yet. To circumvent this issue, you could create the images dynamically and set their src attribute to start loading the image and listen to the image's load event to know when they are fully loaded so that you can perform the merge safely.
I have not tested the code, but it should give you the idea.
var images = [
'iphone4.png',
'screenshot.png'
],
len = images.length,
i = 0,
loadedCount = 0,
img;
for (; i < len; i++) {
img = document.createElement('img');
//listener has to be added before setting the src attribute in case the image is cached
img.addEventListener('load', imgLoadHandler);
img.src = images[i];
images[i] = img;
}
function mergeImages() {
var img1 = images[0],
img2 = images[1];
//do the merging stuff
}
function imgLoadHandler() {
if (++loadedCount === len) {
mergeImages();
}
}
There is a way with HTML5, but it would still require the user to have dropped the file into a drop target or use a box.
Using the File API you can read files, and potentially decode them.
Actually reading the file blob and displaying it locally may be tricky though. You may be able to use the FileReader.readAsDataURL method to set the content as a data: URL for the image tag.
example:
$('#f').on('change', function(ev) {
var f = ev.target.files[0];
var fr = new FileReader();
fr.onload = function(ev2) {
console.dir(ev2);
$('#i').attr('src', ev2.target.result);
};
fr.readAsDataURL(f);
});​
see the working fiddle here :
http://jsfiddle.net/alnitak/Qszjg/
using jquery:
$('#my_image').attr('src','image.jpg');
using javasript:
document.getElementById("my_image").src="image.jpg";
just check path to your image
Write the below code in head block
<script>
window.onload = function () {
document.getElementById("img1").src="iphone4.png";
document.getElementById("img2").src="screenshot.png";
}
</script>
This will work
Thanks

Unable to load image in canvas( using Excanvas in ie8)

Im using excanvas to use canvas element in ie8 but i can't able to load image in canvas.
my code is
var el = document.getElementById('cavasid');
G_vmlCanvasManager.initElement(el);
var context = el.getContext('2d');
var img = new Image();
img.onload = function() {
context.drawImage(img, 0,0);
};
img.src = "jj.png";
Make sure you have added excanvas.js in the head section and then try the below code
var el = document.getElementById('cavasid');
if (typeof G_vmlCanvasManager != 'undefined') {
el = G_vmlCanvasManager.initElement(el);
}
if (el.getContext) {
var context = el.getContext('2d');
var img = new Image();
img.onload = function () {
context.drawImage(img, 0, 0);
};
img.src = "jj.png";
}
also give some width and height to your canvas like
<canvas id="cavasid" width="200" height="200"/>

Categories