Cropping images in the browser BEFORE the upload

JavascriptHtmlHtml5 Canvas

Javascript Problem Overview


Many libraries I have found, like Jcrop, do not actually do the cropping, it only creates an image cropping UI. It then depends on the server doing the actual cropping.

How can I make the image cropping client-side by using some HTML5 feature without using any server-side code.

If yes, are there some examples or hints?

Javascript Solutions


Solution 1 - Javascript

Yes, it can be done.
It is based on the new html5 "download" attribute of anchor tags.
The flow should be something like this :

  1. load the image
  2. draw the image into a canvas with the crop boundaries specified
  3. get the image data from the canvas and make it a href attribute for an anchor tag in the dom
  4. add the download attribute (download="desired-file-name") to that a element That's it. all the user has to do is click your "download link" and the image will be downloaded to his pc.

I'll come back with a demo when I get the chance.

Update
Here's http://jsfiddle.net/gion_13/MmFkL/show/">the live demo as I promised. It takes the http://jsfiddle.net/img/logo.png">jsfiddle logo and crops 5px of each margin.
The code looks like this :

var img = new Image();
img.onload = function(){
    var cropMarginWidth = 5,
        canvas = $('<canvas/>')
                    .attr({
                         width: img.width - 2 * cropMarginWidth,
                         height: img.height - 2 * cropMarginWidth
                     })
                    .hide()
                    .appendTo('body'),
        ctx = canvas.get(0).getContext('2d'),
        a = $('<a download="cropped-image" title="click to download the image" />'),
        cropCoords = {
            topLeft : {
                x : cropMarginWidth,
                y : cropMarginWidth 
            },
            bottomRight :{
                x : img.width - cropMarginWidth,
                y : img.height - cropMarginWidth
            }
        };
    
    ctx.drawImage(img, cropCoords.topLeft.x, cropCoords.topLeft.y, cropCoords.bottomRight.x, cropCoords.bottomRight.y, 0, 0, img.width, img.height);
    var base64ImageData = canvas.get(0).toDataURL();
    
    
    a
        .attr('href', base64ImageData)
        .text('cropped image')
        .appendTo('body');
    
    a
        .clone()
        .attr('href', img.src)
        .text('original image')
        .attr('download','original-image')
        .appendTo('body');
    
    canvas.remove();
}
img.src = 'some-image-src';

Update II
Forgot to mention : of course there is a downside :(.
Because of the same-origin policy that is applied to images too, if you want to access an image's data (through the canvas method toDataUrl).
So you would still need a server-side proxy that would serve your image as if it were hosted on your domain.

Update III Although I can't provide a live demo for this (for security reasons), here is a php sample code that solves the same-origin policy :

file proxy.php :

$imgData = getimagesize($_GET['img']);
header("Content-type: " . $imgData['mime']);
echo file_get_contents($_GET['img']);  

This way, instead of loading the external image direct from it's origin :

img.src = 'http://some-domain.com/imagefile.png';

You can load it through your proxy :

img.src = 'proxy.php?img=' + encodeURIComponent('http://some-domain.com/imagefile.png');  

And here's a sample php code for saving the image data (base64) into an actual image :

file save-image.php :

$data = preg_replace('/data:image\/(png|jpg|jpeg|gif|bmp);base64/','',$_POST['data']);
$data = base64_decode($data);
$img = imagecreatefromstring($data);

$path = 'path-to-saved-images/';
// generate random name
$name  = substr(md5(time()),10);
$ext = 'png';
$imageName = $path.$name.'.'.$ext;

// write the image to disk
imagepng($img,  $imageName);
imagedestroy($img);
// return the image path
echo $imageName;

All you have to do then is post the image data to this file and it will save the image to disc and return you the existing image filename.

Of course all this might feel a bit complicated, but I wanted to show you that what you're trying to achieve is possible.

Solution 2 - Javascript

The Pixastic library does exactly what you want. However, it will only work on browsers that have canvas support. For those older browsers, you'll either need to:

  1. supply a server-side fallback, or
  2. tell the user that you're very sorry, but he'll need to get a more modern browser.

Of course, option #2 isn't very user-friendly. However, if your intent is to provide a pure client-only tool and/or you can't support a fallback back-end cropper (e.g. maybe you're writing a browser extension or offline Chrome app, or maybe you can't afford a decent hosting provider that provides image manipulation libraries), then it's probably fair to limit your user base to modern browsers.

EDIT: If you don't want to learn Pixastic, I have added a very simple cropper on jsFiddle here. It should be possible to modify and integrate and use the drawCroppedImage function with Jcrop.

Solution 3 - Javascript

#change-avatar-file is a file input #change-avatar-file is a img tag (the target of jcrop) The "key" is FR.onloadend Event https://developer.mozilla.org/en-US/docs/Web/API/FileReader

$('#change-avatar-file').change(function(){
        var currentImg;
        if ( this.files && this.files[0] ) {
            var FR= new FileReader();
            FR.onload = function(e) {
                $('#avatar-change-img').attr( "src", e.target.result );
                currentImg = e.target.result;
            };
            FR.readAsDataURL( this.files[0] );
            FR.onloadend = function(e){
                //console.log( $('#avatar-change-img').attr( "src"));
                var jcrop_api;

                $('#avatar-change-img').Jcrop({
                    bgFade:     true,
                    bgOpacity: .2,
                    setSelect: [ 60, 70, 540, 330 ]
                },function(){
                    jcrop_api = this;
                });
            }
        }
    });

Solution 4 - Javascript

If you will still use JCrop, you will need only this php functions to crop the file:

$img_src = imagecreatefromjpeg($src);
$img_dest = imagecreatetruecolor($new_w,$new_h);
imagecopyresampled($img_dest,$img_src,0,0,$x,$y,$new_w,$new_h,$w,$h);
imagejpeg($img_dest,$dest);

client side:

jQuery(function($){

	$('#target').Jcrop({
	onChange:   showCoords,
	onSelect:   showCoords,
	onRelease:  clearCoords
	});

});

var x,y,w,h; //these variables are necessary to crop
function showCoords(c)
{
	x = c.x;
	y = c.y;
	w = c.w;
    h = c.h;
};
function clearCoords()
{
	x=y=w=h=0;
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLorraine BernardView Question on Stackoverflow
Solution 1 - Javascriptgion_13View Answer on Stackoverflow
Solution 2 - JavascriptapsillersView Answer on Stackoverflow
Solution 3 - Javascriptuser1925970View Answer on Stackoverflow
Solution 4 - Javascriptuser669677View Answer on Stackoverflow