matiasgali / guillotine

jQuery plugin to crop images within an area (fully responsive), allowing to drag (touch support), zoom and rotate.
http://guillotine.js.org
326 stars 100 forks source link

Canvas width set to 0% #3

Closed Pran54 closed 9 years ago

Pran54 commented 9 years ago

I was using the guillotine plugin along with images that were received from a server using ajax and php. For some reason, on the initial load, the 'guillotine-canvas' has a width set to 0%. Upon reloading the page, though, the width is set to 100%.

Any ideas? Thanks in advance. Great plugin by the way!

matiasgali commented 9 years ago

Hi, fortunately that has an easy fix.

Guillotine assumes that the target image is already loaded (or cached) before it gets called, it needs the full image to get its width and height.

In your case, the first time Guillotine is called before the image finishes loading but when you reload the page everything works because the image has already been cached by the browser.

I documented this on the source code but I've certainly forgotten to add it on the readme as well. Thanks for making me notice it! I'll add it as soon as possible.

So... how to fix it? You have to make sure that the image has been loaded before calling Guillotine:

var img = $('<img>');
img.on('load', function() { img.guillotine(); });
img.attr('src', 'image.png');

Checkout the source of the built in demo for a more complete example.

At some point I considered making Guillotine itself to check if the image is loaded but it wouldn't be nice to display an empty guillotine or nothing while the image loads, so I decided to let the developer handle what to do while the image loads.

Hope it helps! Ask away if it still doesn't work, good luck!

Pran54 commented 9 years ago

Works great! Thanks for the clarification!

Pran54 commented 9 years ago

Unfortunately, including the loading seemed to fix the issue in Firefox, but not in chrome.

I implemented the imagesLoaded (http://desandro.github.io/imagesloaded/) plugin to fix the issue in chrome, but no success. It still worked great in Firefox though.

Just to make sure that this wasn't a loading issue, I set a .setTimeout() before I call the plugin to see if the same issue occurs. In this case, I see the image loaded on my screen, and then as soon as the timeout finishes, the guillotine is set up, the canvas is set to width 0%, and the image is no longer visible. Going into the developer tools and changing the width to 100% makes the image visible again.

Perhaps this has something to do with the fact I'm using AJAX calls to get the images? Any suggestions would be great. Thanks again!

matiasgali commented 9 years ago

Hi again, nice to see that you're working hard with Guillotine.

First of all, try the demo in the browsers where you saw the issue to rule out if there's a problem in the plugin itself or if it's something specific with your use case.

And second, post some relevant code, a jsFiddle or a link to your implementation to allow debugging.

Pran54 commented 9 years ago

I have figured out the problem and it is something very local. The demo works great on chrome. My problem most likely has something to do with Chrome, image loading, and my server calls. I say this because everything works fine when placing static images as the src attributes and instantiating the plugin. When I add in my server calls though, that is when the canvas width is set to zero (even though the image has loaded after the .onload and .setTimeout delay).

I worked around this by using a static image while initializing the plugin, and then changing the src attribute to the images from the server that I want. This "placeholder method" works great! I'm working on figuring out exactly why Chrome gives me this problem though. Thanks for the help!

willrbc commented 9 years ago

I'm also having this issue, only my issue was not on Chrome but on Safari for Mac and iOS 8.1. Pran54's solution has partially solved it, by adding a placeholder image I am able to get this to work.

However, I am using this plugin for users to upload a passport style photo straight from their iPad camera or computer, I have no way to control what the new uploaded image might be, so when I change the source, guillotine stretches the image to fill the dimensions provided by the placeholder image. If I initialize guiilotine again on img.on('load') it goes to 0% height and width again and the image disappears. Any ideas on how to get around this?

Many thanks for this great plugin.

Will

matiasgali commented 9 years ago

Again, try the demo in the browser where you saw the issue. If it works then the problem is most likely in your implementation, not in the plugin and therefore it isn't an issue.

Why is the width or height of 0%

One of the first things Guillotine does when initialized is to get the image's absolute dimensions and convert them to relative measures to make the guillotine responsive.

If the image isn't already loaded or at least cached before initializing then the width and height the plugin gets form the image are 0, which translates to 0% making the guillotine collapse.

So as it says in the documentation (third step of the setup), make sure that the image is already loaded or cached before calling Guillotine.

Why you shouldn't swap images

As explained above, the plugin gets the absolute dimensions the first time and makes them relative after that. If you swap images that don't have the same dimensions then you'll get broken aspect ratios and other unexpected behaviors. If you change the src then you should re-initialize the plugin.

That's why Pran54's solution won't work in most cases, it's more of a patch than a solution.

Uploading images

You need to figure out a way to make the image that's being uploaded available in the DOM before calling Guillotine, otherwise you'll get width and height of 0%. Either upload the image to the server first or use HTML5 features to load the image on the fly.

You could try jQuery Presto, it displays images from file inputs before uploading using HTML5 when possible or uploading them asynchronously. It doesn't have documentation yet but the source code is short and well commented.

willrbc commented 9 years ago

Thanks for that Matias,

I understand why the canvas is being set to 0 which is why I initiate guillotine using img.on('load'), and this works for firefox, chrome and IE. It seems Safari can't get these dimensions however (for me at least), perhaps it's a glitch with Safari on iOS and OS X. I will look at Presto, for now I have got round the issue by standardising the aspect ratio of the input image. (Cropping the largest square out of the middle).

matiasgali commented 9 years ago

I don't know how you display or load the images to be uploaded so I can't help you there, but as I said before don't swap images, remove any existing instance of Guillotine, make sure the new image is completely loaded and then get a new instance of Guillotine over it. Also be careful with img.on('load'), it's a double-edged sword, if the image loads before you set it the event never gets caught.

In the end, if width or height are 0% is because the plugin attempted to get the dimensions before the image was completely loaded. The magic lines is L174, maybe for some reason that doesn't work in Safari, but if that were the case then the demo shouldn't work either.

andrisgazdag commented 9 years ago

Hi, I'm experiencing the same problem described above. I load images from server with AJAX + PHP. I use Guillotine as described: I initialise it only when the image is loaded. The method works fine in Chrome, Firefox, IE, but in Safari it fails for some reason... So I came up with a workaround. I added an extra option for Guillotine so now I'm able to initialise it with custom width and height parameters. I added a few lines exactly after L174, where Matias pointed the problem. I'm not familiar with coffee script so I edited the generated javascript. These lines go after line 162: right after _ref = [img.width, img.height], width = _ref[0], height = _ref[1];

if (width == 0 && this.op.img_width) {
    width = this.op.img_width;
}
if (height == 0 && this.op.img_height) {
    height = this.op.img_height;
}

With this modification it is possible now to initialise Guillotine this way:

....
 // Set actual image size parameters for safari
initObject["img_width"] = 1024;
initObject["img_height"] = 768;

// Initialise plugin
$newImage.guillotine(initObject);
...

So far this works fine for me. :)

marcelgruber commented 8 years ago

I was using this with a Knockout.js binding and was accidentally initializing it twice which also caused the width to be 0%, so be sure you're only initializing once!

milindk commented 6 years ago

I'm trying to use guillotine for the image drawn on canvas. In the demo application after applying the frame class to canvas where image is drawn guillotine fails to function. Any idea?

Thanks in advance.