0

I am building a client side map-projection transmuter. I must ask the client to select an image, create an image object, get its size, resize a canvas and paint the image on the canvas. I am stuck on step one.

A priority in this project is legibility so it can be improved by other archivists. I am forbidden from using recursion, self-reference, functions that are called during creation, variable names that are identical to classes of ANY kind, and arrow functions.

This ought to work. It does not. There is no usable feedback at the console.

function imagetocanvas(element) {
  let file = element.files[0];
  let reader = new FileReader();
  reader.onloadend = function() { 
    const bob = reader.result;
    var inmap = new Image();
    inmap.src = bob;
    alert(bob.width); // UNDEFINED?...WHY??
  }
  reader.readAsDataURL(file); 
}   
<input type="file" accept="image/*" id="inputtag" onchange="imagetocanvas(inputtag)" >
<canvas id="primary" width="" height="" >
D M
  • 5,769
  • 4
  • 12
  • 27
Molly
  • 59
  • 8
  • `bob.width` should be `inmap.width` (the `FileReader.result` doesn't have a `width` property) and you aren't waiting for the `Image` to load before trying to access its width. Waiting for the file result doesn't also wait for the `Image` constructor. There are many questions about this: https://www.google.com/search?q=js+image+width+0+site:stackoverflow.com – D M Apr 25 '22 at 20:36
  • You cannot set the `src` attribute the `reader.result` variable, you should use `URL.createObjectURL(reader.result)`. – user17517503 Apr 25 '22 at 20:38
  • Thank you DM. Yes, I am aware that there are many questions. Did I accidentally give you the impression that I didn't spend 3 hours searching for the answers? Please forgive me. I do not use javascript. before 9 days ago I had never seen javascript. Management gave us this project to make us fail. I am determined to find simple solutions so the whole department can learn from the code I can scratch together. Do you know the answer in a way I can understand? If there are many questions, that may be a sign there is a SERIOUS problem that no-one is addressing. Thx again DM – Molly Apr 25 '22 at 20:44
  • you both seem to be contradicting each other. learning is accomplished by seeing a corrected form. I see jargon and descriptions of grammar. so one of you says insert a "wait" statement without saying where or how it is spelled. interesting. the other has gifted me with a fully spelled magic word but no clue how to use it. so... random trial and error? think thats the best way to learn? me too. – Molly Apr 25 '22 at 21:01

1 Answers1

0

I believe the following fits into your restrictive criteria. It addresses both concerns that were raised in the comments; neither of which contradicts the other.

  1. You must wait for the Image to load before attempting to access its width.
  2. You should use URL.createObjectURL instead of a FileReader if all you're doing is showing the file. See How to work with the url of a fileReader object?

I've even added the rendering of the image to the canvas. See How to add image to canvas for more about that process.

const fileInput = document.getElementById('inputtag');
const currentImage = new Image();
const primaryCanvas = document.getElementById('primary');
const context = primaryCanvas.getContext('2d');

// Handle changes to the selected file.
fileInput.addEventListener('change', function (changeEvent) {
  
  // Guard against invalid input (no files).
  if (!changeEvent?.target?.files?.length) return;
  
  // Read the file as a blob.
  const firstFile = changeEvent.target.files[0];
  const fileUrl = URL.createObjectURL(firstFile);
  
  // Update the image.
  currentImage.src = fileUrl;
});

// Wait for the image to load before accessing its
// current width or drawing it to the canvas.
currentImage.addEventListener('load', function () {
  console.log(currentImage.width);
  context.drawImage(currentImage, 0, 0);
});
<input type="file" accept="image/*" id="inputtag">
<canvas id="primary" width="" height="">

I did use optional chaining (?.) to check that the files array existed and had at least one item in it. If you're not allowed to use it, an equivalent check would be:

// Guard against invalid input (no files).
if (!changeEvent
    || !changeEvent.target
    || !changeEvent.target.files
    || !changeEvent.target.files.length) {
  return;
}

The check against length serves to check for undefined, null, and length == 0 because all are falsy. This is not a JS-specific concept, but may be unfamiliar depending on the language features you're used to.

For reference, here is the Blob documentation and the URL.createObjectURL documentation which together cover how files are read into memory and accessed.

D M
  • 5,769
  • 4
  • 12
  • 27
  • Thank you. Once I get a template functioning, each member will be able to add the maths in simple for loops and conditionals that we do understand. It's this document.thing.format.grammar(thats.tripping.us.all).up=> – Molly Apr 25 '22 at 21:48
  • bugger... const context = primaryCanvas.getContext('2d'); // Uncaught TypeError: primaryCanvas is null – Molly Apr 25 '22 at 22:10
  • Is the `id` for your canvas the same as in your question? That’d be the simplest answer. Otherwise, if the canvas comes after your ` – D M Apr 26 '22 at 00:37
  • Your code was clear enough that I solved it by putting the script in the header inside a function. Then I called the function in another script within the body onload = myheaderfunction. That got it working however... document.getElementById("primary").width=currentImage.width; Doesn't change the width of the canvas tag. i was so sure that would logically be the thing to do. AAAAHHH!!! I JUST FIGURED IT OUT !!!! you have to set the element BEFORE drawImage Success. Thank you for code I can shuffle around and learn from. I will be posting this code as the answer after I add co- – Molly Apr 26 '22 at 02:41