Reputation: 576
I'm stuck trying to understand why the same JavaScript function in my code is producing different results when called inside a function rather than when called through an HTML button click.
Basically, I'm trying to upload an image and convert it to grayscale using Duke University's Simple Image library. These images are then displayed on two <canvas>
elements.
This is my code (apologies for not producing a fiddle as the library I'm using cannot be included there. Here's the link to my codepen) -
HTML -
<script src="http://www.dukelearntoprogram.com/course1/common/js/image/SimpleImage.js"></script>
<h1>Upload and Display an Image</h1>
<canvas id="canvas"></canvas>
<canvas id="canvas2"></canvas>
<p>Filename:
<input type="file" multiple="false" accept="image/*" id="fileInput" onchange="upload()">
</p>
<p><input type="button" value="Make Grayscale" onclick="makeGray()"></p>
CSS -
html {
margin: 10px;
}
h1 {
font-family: helvetica;
}
canvas {
width: 500px;
height: 250px;
border-style: solid;
}
#textInput {
font-size: 20px;
}
JavaScript -
var img;
function upload() {
img = new SimpleImage(fileInput);
var canvas = document.getElementById("canvas");
img.drawTo(canvas);
}
function makeGray() {
for(var pixel of img.values()) {
var avg = (pixel.getRed() + pixel.getGreen() + pixel.getBlue())/3;
pixel.setRed(avg);
pixel.setGreen(avg);
pixel.setBlue(avg);
}
var canvas = document.getElementById("canvas2");
img.drawTo(canvas);
}
This works as I can click on the Make Grayscale
button and display the grayscale image on the second canvas after uploading it on the first canvas. However, when I get rid of the Make Grayscale
button and call the makeGray()
function inside of the upload()
function, instead of posting a grayscale image it just posts the normal image on the second canvas.
I'm not sure why this is happening as there are no errors on the console. Any information on this would be great. Thanks!
Upvotes: 0
Views: 332
Reputation: 54089
Wait till the image has loaded
The current answer is incorrect. Though it will work, that is only luck, and if the image takes longer than 1000 ms to load it will fail. You need to wait for an image to load before you can draw it, you can not guess how long this will take.
Bad lib because of bad documentation.
Unfortunately the documentation for the lib you are using is very sparse. Going through the source code there is a provision for a onload callback to be called when the image has loaded but I can not work out which of the arguments it is as there are no named arguments. Nor is the FileReader object it uses exposed for you to overwrite the onload event.
You have two options.
The second option
The Lib internal comments say that when the image is ready then img.imageData !== null
so the best you can do is to poll the image and wait till it is ready to render.
The function polls the SimpleImage object img
waiting for img.imageData !== null
and then call the function callback
. The poll interval is pollTime that defaults to 250ms
if not supplied.
function pollImage(img,callback,pollTime){
function isReady(){
if(img.dataURL === null){ // not ready
setTimeout(isReady,pollTime); // try again soon
}else{
callback(); // yes loaded call the callback
}
}
if(img === null || img === undefined){
throw new ReferenceError("No image object given!");
}
if(typeof callback !== "function"){
throw new ReferenceError("No callback function given!");
}
pollTime = pollTime === undefined ? 250 : pollTime; // default to 1/4 second
setTimeout(isReady,0); // Start as soon as possible
}
You then change your code to
function upload() {
img = new SimpleImage(fileInput);
pollImage(
img,
function(){ // callback function that is run when image is ready
var canvas = document.getElementById("canvas");
makeGray();
},
200
);
}
Third option.
Personally I find that a website promoting the teaching of subject (x) yet hiding the core concepts behind a sparsely documented interface is doing nobody any good. Don`t use SimpleImage. Learn to use the DOM directly, it is very well documented, and very simple to use, and will not leave you guessing when the inevitable complications arise.
Upvotes: 1