jCrop (jQuery) sometimes fails to load image/cropper area
Asked Answered
K

6

16

I've got a pretty simple problem, but I've become clueless on what is causing the problem. In one of my applications I'm using jCrop as a small add-on to crop images to fit in banners/headers etc. These steps will be taken:

1) Select an image (using CKFinder for this, CKFinder returns the image path to an input field)
2) Click a button to load the image
3) Crop the image
4) Save the image

in about 75% of the cases everything goes according to plan, however the in the other 25% of the cases jCrop fails to load the cropping area and leaves it blank. Here's the jQuery code I'm using:

jQuery('#selectimg').live('click', function(e) { 
  e.preventDefault();
  var newsrc = jQuery('#img2').val();
  jQuery('#cropbox').attr('src', newsrc);
  var jcrop_api = jQuery.Jcrop('#cropbox', {
    boxWidth: 700, 
    boxHeight: 700,
    onSelect: updateCoords,
    onChange: updateCoords
  });
  //Some other JS code come's here for buttons (they work all the time)
});

I noticed that when I left the part away where #cropbox is being transformd in a cropable area, that the image is loading just fine, so the mistake lies with the var = jcrop_api part, but I slowsly start to think that there is no solution for this...

This is what I've tried so far:

Making a div <div id="cropper-box"></div> and use jQuery('#cropper-box').append('<img src="" id="cropbox" />'); and afterwards set the value. I tried the same thing but setting the image src in 1 step instead of afterwards.

I tried to put a placeholder on the page <img src="placeholder.png" id="cropbox" /> and change the source upon clicking the button. This works, but the cropperarea stays the size of the image (300x180px or something) and doesn't get bigger as it should.

// Edit:

Trying some more showed me that the image source is being replaced properly(! using Firefox to show the source for the selected text), I double checked the URL but this was a correct URL and a working image.

At the place where the cropper should be, there's an about 10x10 pixel white spot where the cropper icon (a plus sign) is popping up.. but as said before: the image isn't shown.

// Edit 2:

So I've took the sources for both the 1st and the 2nd try for the same image. As told before the first try the image won't load properly and the 2nd try it does (only when the 2nd try is the same image(!!)).

The selected page source shows 1 difference which is, first try:

<img style="position: absolute; width: 0px; height: 0px;" src="http://95.142.175.17/uploads/files/Desert.jpg">

second try:

<img style="position: absolute; width: 700px; height: 525px;" src="http://95.142.175.17/uploads/files/Desert.jpg">

I guess this is the image that's being replace by jCrop, but it's a complete riddle why it puts 0 heigth/width in there the first and the proper sizes the second time.

Kallick answered 9/5, 2011 at 12:59 Comment(3)
... not sure if it would make a difference and/or help, but I broke donw the init process into separate steps: var jcrop_api = null; jcrop_api = $.Jcrop('#cropbox'); jcrop_api.setOptions({onChange: [showCoords, setEditDataOK( false ) ], onSelect: showCoords}); jcrop_api.setSelect( [ vx, vy, vx2, vy2 ] ) jcrop_api.focus();Insensibility
thanks, but didn't solve the problem. In case this might be a more common problem: at the first try for each DIFFERENT image, it fails. But if I reload the page, select the same file and try again, it works.Kallick
problem solved, I'll post the answer myself in case anyone needs it.Kallick
K
10

Okay guys, in case anyone else runs into this problem:

jCrop kinda gets messed up if the actions of loading an image and applying jCrop to it are queued too fast after eachother. I still find it strange that a second attempt works perfect, but I think that has something to do with cached image dimensions which are recognized by the DOM of the page or something.

The solution I came up with was by creating a function that converts the #cropbox into a jCrop area and then setting a 2 second interval, just to give jCrop some time to recognize the image and it's dimensions and then convert the element.

This is the part of html I used (with a preloader):

<div id="cropper-loading" style="display: none;"><img src="images/analytics/ajax-loader.gif" /></div>
<img id="cropbox" src="images/placeholder.png" style="display: none;" />

As you can see both the cropbox image and cropper-loading div are hidden as they are not needed instantly. You could display the placeholder if you wanted though.. Then this HTML form is used:

<input name="image2" id="img2" type="text" readonly="readonly" onclick="openKCFinder(this)" value="click here to select an image" style="width: 285px;" />  <button class="button button-blue" type="submit" name="load" id="selectimg">Load Image in cropper</button>

In my case I've been using KCFinder to load the images (it's part of CKEditor, really worth watching into!), KCFinder handles uploads, renaming etc and after choosing it returns the chosen image path (relative/absolute is configurable) to the input field.

Then when clicking #selectimg this code is called:

jQuery('#selectimg').click(function(e) { 
    e.preventDefault();
    jQuery('#cropper-loading').css('display', 'block');

    var newsrc = jQuery('#img2').val();

    jQuery('#cropbox').attr('src', newsrc);
    jQuery('#img').val(newsrc);

    function createJcropArea() {
        jQuery('#cropper-loading').css('display', 'none');                                      
        jQuery('#cropbox').css('display', 'block');
        var jcrop_api = jQuery.Jcrop('#cropbox', {
            boxWidth: 700, 
            boxHeight: 700,
            onSelect: updateCoords,
            onChange: updateCoords
        });
        clearInterval(interval);
    }
    var interval = setInterval(createJcropArea, 2000);
});

At first I prevent the link too be followed as it normally would (or button action) and after that the loading div is displayed (that's my reason for hiding the placeholder image, otherwise it would look messed up).

Then the image location is being loaded from the input field and copied into another (#img), this field is used to process the image afterwards (PHP uses the value of #img to load this image). Also simultaneously the #cropbox src is being set to the new image.

And here comes the part which solved my problem:

Instead of directly activating jCrop, I've made a function that:

1) hides the loading icon
2) displays the image
3) converts #cropbox into a jCrop area
4) clean the interval (otherwise it would loop un-ending)

And after this function you can see that, just to be save, I took 2 seconds delay before the jCrop area is being converted.

Hope it helps anyone in the future!

Cheers and thanks for thinking @vector and whoever else did ;-)

Kallick answered 9/5, 2011 at 14:26 Comment(5)
thanks, that might come in handy one day. info on working with jCrop can be a little sparse at times :-)Insensibility
Thanks @pendo. Had the same issue and your answer put me on the right path. I got mine to work slightly differently but same general idea. You can also stuff the init function to fire after the image is done loading by using .load() jquery function. $("image_cropper").attr("src",YOURSOURCE).load(function() {do_crop_init_here} ); You're forcing the image to load, and attaching a function to fire when done. Hopefully this might help folks.Revive
That's a nice addition indeed!Kallick
Hi @AlexeyGerasimov , I like your approach as it does not need wait instructions. However, it does not work in my case. It still remains empty... If I use the browser's reload function, the image is empty, if i use a propert GET request (e.g. setting the cursor in the URL field and hit ENTER), it works fine. Do you have full code example where it works for you?Promethium
None of the proposed solutions worked for me. Commenting here because it's ranked high on google. My problem was the Image width and height got changed from the original image size to the one displayed on page once the image was loaded and jCrop thought the dimensions of image were much smaller than the actual size. I solved it to set the css visibility to hidden, initialized jCrop on image load and after jCrop init removed the css visibility property.Purser
N
4

Creating an 'Image' object and setting up the 'src' attribute does not apply that you can treat the image like it had already been loaded. Also, giving any fixed timeout interval does not guaranty the image has already been loaded.

Instead, you should set up an 'onload' callback for the Image Object - which will then initialize the Jcrop Object:

var src = 'https://example.com/imgs/someimgtocrop.jpg'; 
var tmpImg = new Image();
tmpImg.onload = function() {
   //This is where you can safely create an image and a Jcrop Object
};
tmpImg.src = src; //Note that the 'src' attribute is only added to the Image Object after the 'onload' listener was defined
Nernst answered 18/10, 2013 at 21:1 Comment(0)
T
3

Try the edge library on the repo here: https://github.com/tapmodo/Jcrop

This should solve your problem. The lines that are changed to solve your problem:

// Fix size of crop image.
// Necessary when crop image is within a hidden element when page is loaded.
if ($origimg[0].width != 0 && $origimg[0].height != 0) {
  // Obtain dimensions from contained img element.
  $origimg.width($origimg[0].width);
  $origimg.height($origimg[0].height);
} else {
  // Obtain dimensions from temporary image in case the original is not loaded yet (e.g. IE 7.0).
  var tempImage = new Image();
  tempImage.src = $origimg[0].src;
  $origimg.width(tempImage.width);
  $origimg.height(tempImage.height);
}
Trachoma answered 15/12, 2011 at 16:51 Comment(1)
this doesn't seem to do it. Put alert($tempImage.width) or .height() at the end of else block. You will get 0. At least I do. The problem seems to be the same as before, the image is not done loading yet, so getting width/height on it returns 0. The extra code in the jcrop init doesn't help as what's needed is the delay between image load and the init itself.Revive
G
1

Don't call this function onChange : updateCoords

Try it without and it will run smooth on mobiles.

You can create base64 directly and show them as an image wherever you want.

Gershwin answered 12/4, 2014 at 12:37 Comment(0)
M
0

Here my weird but fantastic solution:

if (obj.tagName == 'IMG') {
  var tempImage = new Image();
    tempImage.src = $origimg[0].src;
    $origimg.width(tempImage.width);
    $origimg.height(tempImage.height);
  if ($origimg[0].width > 1 && $origimg[0].height > 1) {
    $origimg.width($origimg[0].width);
    $origimg.height($origimg[0].height);
  } else {
    var tempImage = new Image();
    tempImage.src = $origimg[0].src;
    $origimg.width(tempImage.width);
    $origimg.height(tempImage.height);
     //console.log('error'+$origimg[0].width + $origimg[0].height);
  } 
Maryalice answered 30/7, 2013 at 7:45 Comment(0)
A
0

I know this is old, but it was happening randomly to my install recently. Found that it was due to images not being full loaded before before jCrop intialized.

All it took to fix it was wrapping the jCrop initialization stuff inside of a

$(window).on("load", function () { //jcrop stuff here });

And it has been working well since.

Azaleah answered 2/4, 2016 at 19:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.