Search code examples
javascriptjquerycssfilereaderjcrop

Wrong width and height when reusing FileReader


I am currently developing a simple interface allowing users to upload a picture, have a preview of it, and then be able to crop it as a square.

I'm using FileReader to get the preview of the image, and jCrop to crop it.

Here is the code I'm using (a part of it):

    var jcrop = null;
    var reader = new FileReader();

    reader.onload = function (e) {

            $('#upload-picture-sample').attr('src', e.target.result).load(function(){
                var img = this;

                var imageWidth = img.width;
                var imageHeight = img.height;

                console.log(img.width+'-- WIDTH --'+$(this).width());
                console.log(img.height+'-- HEIGHT --'+$(this).height());

                if(imageWidth != imageHeight){

                    var selectArray = [];

                    if(imageWidth > imageHeight){
                        selectArray = [(imageWidth-imageHeight)/2, 0, (imageWidth-imageHeight)/2 + imageHeight, imageHeight];
                    }else{
                        selectArray = [0, (imageHeight - imageWidth)/2, imageWidth, (imageHeight - imageWidth)/2 + imageWidth];
                    }
                    if(!jcrop){
                        $('#upload-picture-sample').Jcrop({
                            aspectRatio: 1,
                            keySupport: false,
                            addClass: 'jcrop-centered',
                            setSelect: selectArray
                        }, function(){
                            jcrop = this;
                        });
                    }else{
                        jcrop.setImage(e.target.result);
                        jcrop.setSelect(selectArray);

                    }
                }
                $(this).unbind('load');
            });   
        }

    $('#upload-picture-file').change(function(){
        $('#upload-picture-send').removeClass('disabled');
        $('#upload-picture-error').hide();

        console.log('changed!');

        reader.readAsDataURL(this.files[0]);
    })

I think the only part that really matters is the part when I calculate the width and height.

To illustrate the problem, I'm uploading:

1/ Picture1 (600x450)

2/ Picture2 (94x125)

3/ Picture1 again (600x450)

enter image description here

The first upload is working fine, the second is working fine as well, but I guess it's more luck than something else, since the height is incorrectly calculated as 0. The third upload is not working (the size is not correctly set).

It means that the cropzone is not correctly displayed.

Regarding css, I have this:

#upload-picture-sample{
  display:block;
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 30px;
  max-height:125px;
  height:auto;
  width:auto;
  max-width:300px;
}

Do you have any idea of how I could solve my problem?

UPDATE: After having added setTimeout as recommended by @initialxy enter image description here

So it's much better, but still, the first image doesn't have the same dimensions than the last (and it should, since it's exactly the same image) Thanks.


Solution

  • All of your numbers look inconsistent. In first image, width end up being 167 with img.width but 167 with jQuery. One thing to note is that jQuery gets computed style while img.width and img.height are DOM properties that sets desired width and height. (FYI there's also naturalWidth and naturalHeight, which are read-only DOM properties that gets you the original dimensions of img.)

    it's more luck than something else

    If your code depends on luck, then we got a problem.

    It looks like browser emitted load event slightly too early, such that layout engine hasn't finished updating the DOM. load just means data is loaded, doesn't necessarily mean layout engine has to be completed. So try to queue your code block inside your load function later in the task queue. By which, I mean wrap all that in a setTimeout(..., 0); Watch out for this, as it has changed.

    eg.

    $('#upload-picture-sample').attr('src', e.target.result).load(function(){
        var img = this;
    
        setTimeout(function() {
            var imageWidth = img.width;
            var imageHeight = img.height;
    
            console.log(img.width+'-- WIDTH --'+$(img).width());
            console.log(img.height+'-- HEIGHT --'+$(img).height());
    
            // TODO: Crop
            ...
        }, 0);
    
        $(this).unbind('load');
    });
    

    EDIT: In response to @Vico's update. Looks like layout engine has settled this time. Let's make some observations on these numbers. The first image's dimensions were originally 600x450, but it ended up being 167x125. This makes sense, because it was resized by max-height: 125px; CSS property. The second image has both of its dimensions less than max-width and max-height so it didn't get resized. The third image has dimensions of 600x450, and it ended up having these dimensions, so at this point max-width and max-height is no longer taking effect. Why is that? Perhaps jcrop screwed around with style and overridden your style. Fire up your Chrome debugger, inspect your img element and use its JavaScript console to play around with it. (To give you a short answer, yes, jcrop does screw around with style and applies inline styles to the element, which overrides your style. But hey, it's more fun to play with debugger.) Also, I'm not sure why your dimensions on the right all ended up with 1440x514. You can find out by screwing around in debugger.