Resizing and Cropping Images with Canvas

Learn how to resize and crop images using JavaScript and the HTML5 Canvas element using controls, commonly seen in photo editing applications.

In this tutorial we’re going to learn how to resize and crop an image using the HTML5 <canvas> element, and while we’re at it, let’s create some fancy controls for resizing, commonly seen in photo editing applications.

In a real world example a website or app might use a technique like this to resize and frame a profile picture before uploading. Whilst we could do this on the server, it would require the transfer of a potentially large file, which is slow. Instead we can resize the image on the client side before uploading it, which is fast.

We do this by creating an HTML5 <canvas> element and drawing the image to the canvas at a particular size, then extracting the new image data from the canvas as a data URI. Most browsers have good support for these methods, so you can probably use this technique right now, however just be aware of some limitations unrelated to browser support such as quality and performance.

Resizing very large images can cause the browser to slow down or in some cases, even crash. It makes sense to set reasonable limits on the file size just as you would when uploading a file. If quality is important you may find the resized image looks undesirable due to how the browser resampled it. There are some techniques to improve the quality of images downscaled with canvas, but they are not covered in this tutorial.

Take a look at the final result in this demo or download the ZIP file.

With that in mind, let’s get started!

The Markup

In our demo we’re going to start with an existing image:

<img class="resize-image" src="image.jpg" alt="Image" />

That’s it! That’s all the HTML we need for this demo.

The CSS

The CSS is also very minimal. First, define the styles for the resize-container and the image.

.resize-container {
    position: relative;
    display: inline-block;
    cursor: move;
    margin: 0 auto;
}

.resize-container img {
    display: block
}

.resize-container:hover img,
.resize-container:active img {
    outline: 2px dashed rgba(222,60,80,.9);
}

Next, define the position and style for each of the ‘resize handles’. These are the little squares at each corner of the image that we drag to resize.

.resize-handle-ne,
.resize-handle-ne,
.resize-handle-se,
.resize-handle-nw,
.resize-handle-sw {
    position: absolute;
    display: block;
    width: 10px;
    height: 10px;
    background: rgba(222,60,80,.9);
    z-index: 999;
}

.resize-handle-nw {
    top: -5px;
    left: -5px;
    cursor: nw-resize;
}

.resize-handle-sw {
    bottom: -5px;
    left: -5px;
    cursor: sw-resize;
}

.resize-handle-ne {
    top: -5px;
    right: -5px;
    cursor: ne-resize;
}

.resize-handle-se {
    bottom: -5px;
    right: -5px;
    cursor: se-resize;
}

The JavaScript

With the JavaScript start by defining some of the variables and initializing the Canvas and the target image.

var resizeableImage = function(image_target) {
    var $container,
    orig_src = new Image(),
    image_target = $(image_target).get(0),
    event_state = {},
    constrain = false,
    min_width = 60,
    min_height = 60,
    max_width = 800,
    max_height = 900,
    resize_canvas = document.createElement('canvas');
});

resizeableImage($('.resize-image'));

Next, we create the init function that will be called immediately. This function wraps the image with a container, creates resize handles and makes a copy of the original image used for resizing. We also assign the jQuery object for the container element to a variable so we can refer to it later and add a mousedown event listener to detect when someone begins dragging one of the handles.

var resizeableImage = function(image_target) {

// ...
    init = function(){

        // Create a new image with a copy of the original src
        // When resizing, we will always use this original copy as the base
        orig_src.src=image_target.src;

        // Add resize handles
        $(image_target).wrap('<div class="resize-container"></div>')
        .before('<span class="resize-handle resize-handle-nw"></span>')
        .before('<span class="resize-handle resize-handle-ne"></span>')
        .after('<span class="resize-handle resize-handle-se"></span>')
        .after('<span class="resize-handle resize-handle-sw"></span>');

        // Get a variable for the container
        $container =  $(image_target).parent('.resize-container');

        // Add events
        $container.on('mousedown', '.resize-handle', startResize);
    };

//...

    init();
}

The startResize and endResize functions do very little other than tell the browser to start paying attention to where the mouse is moving and when to stop paying attention.

startResize = function(e){
    e.preventDefault();
    e.stopPropagation();
    saveEventState(e);
    $(document).on('mousemove', resizing);
    $(document).on('mouseup', endResize);
};

endResize = function(e){
    e.preventDefault();
    $(document).off('mouseup touchend', endResize);
    $(document).off('mousemove touchmove', resizing);
};

Before we start tracking the mouse position we want to take a snapshot of the container dimensions and other key data points. We store these in a variable named event_state and use them later as a point of reference while resizing to work out the change in height and width.

saveEventState = function(e){
  // Save the initial event details and container state
  event_state.container_width = $container.width();
  event_state.container_height = $container.height();
  event_state.container_left = $container.offset().left; 
  event_state.container_top = $container.offset().top;
  event_state.mouse_x = (e.clientX || e.pageX || e.originalEvent.touches[0].clientX) + $(window).scrollLeft(); 
  event_state.mouse_y = (e.clientY || e.pageY || e.originalEvent.touches[0].clientY) + $(window).scrollTop();

  // This is a fix for mobile safari
  // For some reason it does not allow a direct copy of the touches property
  if(typeof e.originalEvent.touches !== 'undefined'){
	event_state.touches = [];
	$.each(e.originalEvent.touches, function(i, ob){
	  event_state.touches[i] = {};
	  event_state.touches[i].clientX = 0+ob.clientX;
	  event_state.touches[i].clientY = 0+ob.clientY;
	});
  }
  event_state.evnt = e;
}

The resizing function is where most of the action happens. This function is constantly invoked while the user is dragging one of the resize handles. Every time this function is called we work out the new width and height by taking the current position of the mouse relative to the initial position of the corner we are dragging.

resizing = function(e){ 
    var mouse={},width,height,left,top,offset=$container.offset();
    mouse.x = (e.clientX || e.pageX || e.originalEvent.touches[0].clientX) + $(window).scrollLeft(); 
    mouse.y = (e.clientY || e.pageY || e.originalEvent.touches[0].clientY) + $(window).scrollTop();

    width = mouse.x - event_state.container_left;
    height = mouse.y  - event_state.container_top;
    left = event_state.container_left;
    top = event_state.container_top;

    if(constrain || e.shiftKey){
        height = width / orig_src.width * orig_src.height;
    }

    if(width > min_width && height > min_height && width < max_width && height < max_height){
      resizeImage(width, height);  
      // Without this Firefox will not re-calculate the the image dimensions until drag end
      $container.offset({'left': left, 'top': top});        
    }
}

Next we add the option to constrain the image dimensions when toggled using the shift key or a variable.

Finally, we resize the image, but only if the new width and height are not outside the bounds of the min and max variables we initially set.

Note: Because we’re actually resizing the image and not just changing the height and width attributes, you might consider limiting how often resizeImage is called to improve performance. This is called debouncing or throttling.

Actually resizing the image

Drawing an image to the Canvas is as easy as drawImage. We set the height and width of the canvas first and always use the original copy of the full-sized image. We then use toDataURL on the Canvas to get a Base64-encoded version of the newly resized image and place this on the page.

There is a full explanation for all the parameters that can be used with the drawImage method in the cropping section of this tutorial.

resizeImage = function(width, height){
    resize_canvas.width = width;
    resize_canvas.height = height;
    resize_canvas.getContext('2d').drawImage(orig_src, 0, 0, width, height);   
    $(image_target).attr('src', resize_canvas.toDataURL("image/png"));  
};

Too simple? There is one small proviso: the image must be on the same domain as the page or on a server with cross-origin resource sharing (CORS) enabled. If it’s not, you might run into problems with an error about a ‘tainted canvas’.

Resizing from different corners

You should now have a working demo. But it’s not complete. At the moment, no matter which corner of the image we resize, it behaves as though we are resizing it from the bottom right. We want to be able to resize the image from any corner. To do this we need to understand how it should behave.

When resizing, the corner we’re dragging as well as its adjacent edges should move, while the corner directly opposite and its adjacent edges should remain fixed.

Shows which corner remains fixed when resizing an image

When we change the width and height of an image, the right and bottom edges move, while the top and left edges will stay the same. This means that by default, an image is resized from its bottom right corner.

We can’t change this default behavior, but when resizing from any corner other than the bottom right we can change the overall position of the image so that it appears as though the opposite corner and edges remain fixed. Let’s update our resizing function:

resizing = function(e){
  var mouse={},width,height,left,top,offset=$container.offset();
  mouse.x = (e.clientX || e.pageX || e.originalEvent.touches[0].clientX) + $(window).scrollLeft(); 
  mouse.y = (e.clientY || e.pageY || e.originalEvent.touches[0].clientY) + $(window).scrollTop();
  
  // Position image differently depending on the corner dragged and constraints
  if( $(event_state.evnt.target).hasClass('resize-handle-se') ){
    width = mouse.x - event_state.container_left;
    height = mouse.y  - event_state.container_top;
    left = event_state.container_left;
    top = event_state.container_top;
  } else if($(event_state.evnt.target).hasClass('resize-handle-sw') ){
    width = event_state.container_width - (mouse.x - event_state.container_left);
    height = mouse.y  - event_state.container_top;
    left = mouse.x;
    top = event_state.container_top;
  } else if($(event_state.evnt.target).hasClass('resize-handle-nw') ){
    width = event_state.container_width - (mouse.x - event_state.container_left);
    height = event_state.container_height - (mouse.y - event_state.container_top);
    left = mouse.x;
    top = mouse.y;
    if(constrain || e.shiftKey){
      top = mouse.y - ((width / orig_src.width * orig_src.height) - height);
    }
  } else if($(event_state.evnt.target).hasClass('resize-handle-ne') ){
    width = mouse.x - event_state.container_left;
    height = event_state.container_height - (mouse.y - event_state.container_top);
    left = event_state.container_left;
    top = mouse.y;
    if(constrain || e.shiftKey){
      top = mouse.y - ((width / orig_src.width * orig_src.height) - height);
    }
  }

  // Optionally maintain aspect ratio
  if(constrain || e.shiftKey){
    height = width / orig_src.width * orig_src.height;
  }

  if(width > min_width && height > min_height && width < max_width && height < max_height){
    // To improve performance you might limit how often resizeImage() is called
    resizeImage(width, height);  
    // Without this Firefox will not re-calculate the the image dimensions until drag end
    $container.offset({'left': left, 'top': top});
  }
}

We are now checking to see which resize-handle has been dragged and we’re moving the image while resizing it so that it appears as though the correct corner remains fixed.

Moving the image

Now that we can resize the image from any of its corners you might have noticed we can inadvertently change its position on the page. We need to give users the ability to move the image back into the center of frame. In the init function let’s add another event listener similar to the one we did earlier.

init = function(){

    //...

    $container.on('mousedown', 'img', startMoving);
}

We now add startMoving and endMoving functions similar to startResize and endResize.

startMoving = function(e){
    e.preventDefault();
    e.stopPropagation();
    saveEventState(e);
    $(document).on('mousemove', moving);
    $(document).on('mouseup', endMoving);
};

endMoving = function(e){
    e.preventDefault();
    $(document).off('mouseup', endMoving);
    $(document).off('mousemove', moving);
};

In the function moving we need to work out the new position of the top left edge of the container. This will be equal to the current position of the mouse, offset by the distance the mouse was from the top left corner when we started dragging the image.

moving = function(e){
    var  mouse={};
    e.preventDefault();
    e.stopPropagation();
    mouse.x = (e.clientX || e.pageX) + $(window).scrollLeft();
    mouse.y = (e.clientY || e.pageY) + $(window).scrollTop();
    $container.offset({
        'left': mouse.x - ( event_state.mouse_x - event_state.container_left ),
        'top': mouse.y - ( event_state.mouse_y - event_state.container_top ) 
    });
};

Cropping the image

Now that we can resize the image we might want to crop it as well. Rather than allowing users to crop the image to any size and shape, let’s create a frame that is the exact dimensions we require and ask users to position the image inside that frame. This gives them control over the zoom and framing, yet ensures the final image always the same size and shape.

To do this we need to add a the following HTML:

<div class="overlay">
    <div class="overlay-inner">
    </div>
</div>
<button class="btn-crop js-crop">Crop</button>

The styles for the overlay box are important, particularly it’s position, width and height as they are used to determine what part of the image is cropped. It’s also important to remember that the frame should always be visible on any background color. That is why I used a semi transparent white outline around the main box in my example.

.overlay {
    position: absolute;
    left: 50%;
    top: 50%;
    margin-left: -100px;
    margin-top: -100px;
    z-index: 999;
    width: 200px;
    height: 200px;
    border: solid 2px rgba(222,60,80,.9);
    box-sizing: content-box;
    pointer-events: none;
}

.overlay:after,
.overlay:before {
    content: '';
    position: absolute;
    display: block;
    width: 204px;
    height: 40px;
    border-left: dashed 2px rgba(222,60,80,.9);
    border-right: dashed 2px rgba(222,60,80,.9);
}

.overlay:before {
    top: 0;
    margin-left: -2px;
    margin-top: -40px;
}

.overlay:after {
    bottom: 0;
    margin-left: -2px;
    margin-bottom: -40px;
}

.overlay-inner:after,
.overlay-inner:before {
    content: '';
    position: absolute;
    display: block;
    width: 40px;
    height: 204px;
    border-top: dashed 2px rgba(222,60,80,.9);
    border-bottom: dashed 2px rgba(222,60,80,.9);
}

.overlay-inner:before {
    left: 0;
    margin-left: -40px;
    margin-top: -2px;
}

.overlay-inner:after {
    right: 0;
    margin-right: -40px;
    margin-top: -2px;
}

.btn-crop {
    position: absolute;
    vertical-align: bottom;
    right: 5px;
    bottom: 5px;
    padding: 6px 10px;
    z-index: 999;
    background-color: rgb(222,60,80);
    border: none;
    border-radius: 5px;
    color: #FFF;
}

Update the JavaScript with the following function and event listener:

init = function(){

    //...

    $('.js-crop').on('click', crop);
  
};

crop = function(){
    var crop_canvas,
        left = $('.overlay').offset().left - $container.offset().left,
        top =  $('.overlay').offset().top - $container.offset().top,
        width = $('.overlay').width(),
        height = $('.overlay').height();
        
    crop_canvas = document.createElement('canvas');
    crop_canvas.width = width;
    crop_canvas.height = height;
    
    crop_canvas.getContext('2d').drawImage(image_target, left, top, width, height, 0, 0, width, height);
    window.open(crop_canvas.toDataURL("image/png"));
}

The crop function is similar to the resizeImage function however instead of passing it height and width values we get the height and width from the overlay element.

For cropping, the canvas drawImage method requires nine parameters. The first parameter is the source image. The next four parameters indicate what part of the source image is used (the clipping box). The final four parameters indicate where on the canvas to start drawing the image and at what size.

Adding touch events and gesture detection

We’ve added mouse events, now let’s add support for touch enabled devices.

For mousedown and mouseup there are equivalent touch events, touchstart and touchend and for mousemove there is the equivalent touchmove. Someone obviously lacks a sense of humor because these events could have easily been named “touchdown” and “touchup”.

Let’s add touchstart and touchend everywhere we have a mousedown and mouseup event listener and touchmove everywhere we have mousemove.

// In init()...
$container.on('mousedown touchstart', '.resize-handle', startResize);
$container.on('mousedown touchstart', 'img', startMoving);

//In startResize() ...
$(document).on('mousemove touchmove', moving);
$(document).on('mouseup touchend', endMoving);

//In endResize()...
$(document).off('mouseup touchend', endMoving);
$(document).off('mousemove touchmove', moving);

//In  startMoving()...
$(document).on('mousemove touchmove', moving);
$(document).on('mouseup touchend', endMoving);

//In endMoving()...
$(document).off('mouseup touchend', endMoving);
$(document).off('mousemove touchmove', moving);

Since we’re resizing an image it might be reasonable to expect that some users will try common gestures like pinch zoom. There is a library called Hammer that provides a lot of convenience when working with gestures. But since we only need pinch zoom, in our case, it might be a more like a sledge hammer. Let me show you how easy it is to detect pinch zoom without any library.

You might have notice that in the saveEventState function we already stored the initial touch data; this will come in handy now.

First we check if the event contains two “touches” and measure the distance between them. We note this as the initial distance and then constantly measure how much this distance changes while moving. Let’s update the moving function:

moving = function(e){
  var  mouse={}, touches;
  e.preventDefault();
  e.stopPropagation();
  
  touches = e.originalEvent.touches;
  mouse.x = (e.clientX || e.pageX || touches[0].clientX) + $(window).scrollLeft(); 
  mouse.y = (e.clientY || e.pageY || touches[0].clientY) + $(window).scrollTop();
  $container.offset({
    'left': mouse.x - ( event_state.mouse_x - event_state.container_left ),
    'top': mouse.y - ( event_state.mouse_y - event_state.container_top ) 
  });
  // Watch for pinch zoom gesture while moving
  if(event_state.touches && event_state.touches.length > 1 && touches.length > 1){
    var width = event_state.container_width, height = event_state.container_height;
    var a = event_state.touches[0].clientX - event_state.touches[1].clientX;
    a = a * a; 
    var b = event_state.touches[0].clientY - event_state.touches[1].clientY;
    b = b * b; 
    var dist1 = Math.sqrt( a + b );
    
    a = e.originalEvent.touches[0].clientX - touches[1].clientX;
    a = a * a; 
    b = e.originalEvent.touches[0].clientY - touches[1].clientY;
    b = b * b; 
    var dist2 = Math.sqrt( a + b );

    var ratio = dist2 /dist1;

    width = width * ratio;
    height = height * ratio;
    // To improve performance you might limit how often resizeImage() is called
    resizeImage(width, height);
  }
};

We divide the current distance by initial distance to get the ratio and how much to scale the image. We work out the new width and height and then resize the image.

ImageResizingCroppingCanvas01

And that’s it. Take a look at the demo or download the ZIP file.

In my testing, Chrome prevented the default browser response for pinch zoom, which is to change the page zoom, but Firefox didn’t.

I hope you found this tutorial useful. I suggest further reading on drag and drop and file upload techniques and look forward to see how people might combine these techniques to create some beautiful user interfaces.

Tagged with:

Mike Riethmuller

Mike is web developer living in Merimbula on the south east coast of Australia. He loves the web and sometimes writes about it. He is honored that you are reading his bio.

Stay up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 108

Comments are closed.
  1. Great stuff Mike. I think the image should scale with fixed proportions at default… and maybe be restricted to the images size, so you couldn’t make it smaller than the “canvas” of the exported image… Just some ideas. Like it a lot! 🙂

    • Thanks Chris, as always, you are exactly right. Using FileReader and drag and drop is a typical real world use of this example. I wanted to focus on the interface but I strongly suggest people take a look at FileReader examples like yous. Thank you!

  2. This is awesome ! It’s quite rare to see that kind of tutorial on codrops, but I really enjoyed it 🙂

  3. Well done!

    I’d like to propose a small change to scaling, however – the scaling is getting really slow on larger image size.
    I think it woulds make sense to resize the “current” state of the image (so just modify width/height of the image itself without adjusting the image source) and apply the resize (change the source via canvas manipulation) only after the user finishes the scaling itself.

    WDYT?

    • Yes, this is a valid suggestion. I left a comment in the code suggesting something similar. In a real word example to improve performance on all devices and images sizes you might want to limit how often the image is resized by the canvas.

      But be careful, if you were to use the “current state of the image” you might find that resizing from small to large looks ugly and you might also find that resizing with canvas looks slightly different to how it looks when changing the width and height attributes of the image.

    • … you might find that resizing from small to large looks ugly
      true, but only while the “drag” event is still in progress – as soon as you stop it, the image is recalculated using canvas.
      much better performance (actual and perceived) is a good trade here IMO 🙂

    • yes in here firefox doensn’t work too 😀 what should i change this issue, please answer if anyone knows

  4. It works reasonably well for me with Firefox and Windows 7. It’s not always snappy, but it is doing a lot. If performance is a concern, as Mike said you can always limit how often it gets resized.

    Thanks

  5. First: Very nice tutorial. But there are several problems:
    – The crop-function dosn’t work in IE11.
    – performance is really poor. If you use the workaround from Tomasz Stryjewski it runs smoother
    – If you dosn’t fill the whole overlay the crop-function crash

  6. Hey, great tutorial, i have one question, how move thumbnail for directory on javascript? This is correct implement other language(php)?

  7. Whoa!! Great stuff love your writing. Using Filereader API is another thing to rely on. But yes resizing so many images makes my browser hang up every now and then.
    Helpful article thanks to share Mike.

  8. Hmmm- I love this, it’s exactly what I was looking for- the only thing that disappoints me is that while the user is resizing the picture, the actual resizing does not get displayed until the user stops dragging the resize handle. It would be nice if the picture could resize as the handle was being dragged, so the user could get visual feedback during the resizing.

  9. Great article.
    When uploading a .png the background turns black. How would I go about changing this to white?
    Many thanks

  10. I am using HTML5 FileReader – Extracting the image into a DataURL and using canvas to crop the image. Once the crop done, I extract the image from canvas using toDataURL and put into the image tag. At this point the image quality drastically reduces. has anyone faced this?

  11. Great widget! Easy to customize to my needs (different style on overlay etc).

    My only real concern is the very low performance when zooming on a mobile device, so I’m wondering whether there’s a more efficient way than to convert the image to a base64 “blob” (if now that’s the main contributor). Save it as a binary file and refer to it as such? Not that Javascript is a fan of files.

    Regards,
    Anders

  12. Hi, excellent job! Though when image exceeds certain dimensions resize does not work any longer. I tried to edit max_width and max_height options but that didn’t solve the issue. Could you help me?
    Thanks

    • Hi, I faced same issue and made some changes on this part of the code helps me :

      if(width > min_width && height > min_height /*&& width < max_width && height < max_height*/){
      // To improve performance you might limit how often resizeImage() is called
      resizeImage(width, height);
      // Without this Firefox will not re-calculate the the image dimensions until drag end
      $container.offset({'left': left, 'top': top});
      }

      Hope this will help you

  13. Hi, very cool post.
    One question, when I load a png image resize is no longer possible, do I need to change or add something?
    thanks and happy new year.

  14. It’s working . But i want to know that how can i save it after crop? Does it need PHP for saving a crop image with your this image resizing technique?
    Thank you

  15. Hello, I am having difficulties with large images (resolution 8mp), can anyone help me with this? Anyone had the same problem?

  16. hi, why in the js in the variable image_target istanceof the n.fn.init[1] and no n.fn.init[0]?

  17. To save the image, I did this in the .js file:

    // resize from 128x128 to 64x64 crop_canvas.getContext('2d').drawImage(image_target, left, top, width, height, 0, 0, 64, 64); var Pic = crop_canvas.toDataURL("image/png"); // Generate the image data Pic = Pic.replace(/^data:image\/(png|jpg);base64,/, "") // make an AJAX call to move the file var url = '(place your url here)'; // Send the image data to Server $.ajax({ type: 'POST', url: url, dataType: 'text', data: { imageData : Pic }, success: function (msg) { } });

    And in the .php file, try this:

    if (array_key_exists('imageData',$_REQUEST)) { $imgData = base64_decode($_REQUEST['imageData']); // Path where the image is going to be saved $filePath = '(put your url + filename here)'; // Write $imgData into the image file $file = fopen($filePath, 'w'); fwrite($file, $imgData); fclose($file); }

    I had some problems with image quality after resizing from 128×128 to 64×64. Any clues? Thanks in advance.

    • OK, got it figured out. Make the changes below.

      – Be sure you use parseInt(left) and parseInt(top) to be sure you are resizing/cropping on the pixel!

      crop_canvas = document.createElement('canvas'); crop_canvas.width = 64; crop_canvas.height = 64; crop_canvas.getContext('2d').drawImage(image_target, parseInt(left), parseInt(top), width, height, 0, 0, 64, 64); var Pic = crop_canvas.toDataURL("image/png"); // Generate the image data Pic = Pic.replace(/^data:image\/(png|jpg);base64,/, "")

      Thanks for the great tutorial!!

      (By the way, I noticed the image gets cropped a pixel or two to the left of the box.)

    • Hello, i have been success insert it to database but hard to do update the images, did you have any idea how to update cause i use onclick to save it to database then after click it will write to folder adn insert, please guide me

  18. Great stuff! but I have an issue in Firefox, when the image is resized smaller then the bounding box, it fails to crop…
    need help on this….

  19. Hello,

    I really love this “pluggin” I made many changes on it I can updload, rename and tag the image and also save file.

    But there is one thing I am facing today.
    Maybe you facing same issue. When you move the image outside the global area (not the one inside the red square), you loose the image and there is no possibility to catch it again.

    How can we avoid this? Is there a possibility to stop moving when the mouse is close to the border – 10px (10 should be a parameter)?

  20. How I can do to re-center the image on a click button?
    I mean if I lost my image I want to reinit the position of the image if there a possibility to do that?
    thanks

  21. answer to my own question, maybe someone else needs this.
    Add the following function on a click (or whatever your want)

    recenter = function(e){
    $container.offset({
    'left': event_state.container_left,
    'top': event_state.container_top
    });
    }

  22. Hello, I have a wordpress website and want to implement this. Can you confirm how i would go about it ?

  23. Great Tutorial!Its working perfectly for small images but i am not able to crop large images(around 900 kb).I am however able to pan them.Can you please guide me what is causing the issue and what i need to change ?

    • hi,
      i have the same issue , i can’t resizing with big picture…

      i has try : img {max width:400px} and now i can resizing .. 🙂

      ….but now when i try to save, he save with the size of big picture so the result is not the same of the display .. any idea ? ( sorry for my english )

    • In the crop function, you should obtain a ratio of the original image width and its screen width. I assigned an id to the image in order to do this easily. Then apply the ratio to the left, top, width and height of the overlay variable. That should do it…looks like this:

      var myimg = document.getElementById(‘myimg’);
      var ratio = myimg.naturalWidth / myimg.width;
      var crop_canvas,
      left = ($(‘.overlay’).offset().left – $container.offset().left)*ratio,
      top = ($(‘.overlay’).offset().top – $container.offset().top)*ratio,
      width = ($(‘.overlay’).width())*ratio,
      height = ($(‘.overlay’).height())*ratio;

      I also needed a 16/9 crop, so I set the overlay’s width and height to 320/180. The :before and :after got fugly after that so I made an oldschool table and put the overlay in the center of it, three tr, and three td per row with left and right td of each row with the dashed border, so it looks decent. I used Jquery draggable for the table and Jquery resizable for the overlay with aspectRatio true so the 16/9 remains on resize. I then did this so all my pictures have the exact same dimension regardless of the original size…

      crop_canvas = document.createElement(‘canvas’);
      crop_canvas.width = 640;
      crop_canvas.height = 360;

      crop_canvas.getContext(‘2d’).drawImage(image_target, left, top, width, height, 0, 0, 640, 360);

      You can use regular CSS to size your image so long as it maintains its original aspect ratio, 4:3 or whatever. Thanks for the tutorial. I was attempting to do this with my own but I only had a resizable/draggable overlay, functional, but not pretty. Being able to resize and move the image is excellent. If you allow the image to be skewed, then you’ll need a separate ratio for left/width and for top/height

  24. Hi!
    Thanks for the great tutorial.

    I need to know how can hide the dotted line box and freeze the image once I done resizing and everything.

  25. Wow, this is plain amazing.

    Rather than display the cropped image though, I would like to save it. Another user posted some code but I don’t quite understand how he transitions the data from JavaScript into PHP. What method would YOU use to save the image rather than display it?

  26. Okay, so with saving the image is actually a pretty simple process, as outlined here: http://permadi.com/2010/10/html5-saving-canvas-image-data-using-php-and-ajax/

    First, you need to make a change in the component.js file, replacing the window.open() function with the code listed here to send the data to a php receiving file. In my case, I created a file called “savefile.php” and here is the code changes to the component.js file:

    *******************************************************************************
    crop = function(){
    //Find the part of the image that is inside the crop box
    var crop_canvas,
    left = $(‘.overlay’).offset().left – $container.offset().left,
    top = $(‘.overlay’).offset().top – $container.offset().top,
    width = $(‘.overlay’).width(),
    height = $(‘.overlay’).height();

    crop_canvas = document.createElement(‘canvas’);
    crop_canvas.width = width;
    crop_canvas.height = height;

    crop_canvas.getContext(‘2d’).drawImage(image_target, left, top, width, height, 0, 0, width, height);

    // The default operation will simply open a new tab showing the cropped image
    // window.open(crop_canvas.toDataURL(“image/png”));

    // The following code is taken from http://permadi.com/2010/10/html5-saving-canvas-image-data-using-php-and-ajax/
    // It shows how to post the image data as a POST variable into a url that points to a php file that receives the data…
    var canvasData = crop_canvas.toDataURL(“image/png”);
    var ajax = new XMLHttpRequest();
    ajax.open(“POST”,’savefile.php’,false);
    ajax.setRequestHeader(‘Content-Type’, ‘application/upload’);
    ajax.send(canvasData )
    }

    init();
    };
    *******************************************************************

    Then create the savefile.php file:
    ****************************************************************************************

    *****************************************************************************

    And that’s all there is to it. Hope this helps folks out there!

    • the code for the php didn’t show. here it is:

      // The file should be saved using the interpreter’s ID number.
      session_start();
      $imgFile = “http://www.ubpcorp.com/members/images/” . $_SESSION[‘agent’] . “.jpg”;

      if (isset($GLOBALS[“HTTP_RAW_POST_DATA”])) {
      // Get the data
      $imageData=$GLOBALS[‘HTTP_RAW_POST_DATA’];

      // Remove the headers (data:,) part.
      // A real application should use them according to needs such as to check image type
      $filteredData=substr($imageData, strpos($imageData, “,”)+1);

      // Need to decode before saving since the data we received is already base64 encoded
      $unencodedData=base64_decode($filteredData);

      //echo “unencodedData”.$unencodedData;

      // Save file.
      $fp = fopen( $imgFile, ‘wb’ );
      fwrite( $fp, $unencodedData);
      fclose( $fp );
      }

      header (‘Location: index.php’);

    • Victor – You solution is exactly what I need to do! I have been trying and trying to get your code to work. I appended the lines to crop = function in component.js (see below).
      I’ve read up on the savefile.php, read the original article you sourced and tried a few options found on there but to no luck. You would think saving a crop image wouldnt be so hard!

      Was there anything else you did in the code to get it to work? Your help greatly appreciated!

      crop = function(){
      //Find the part of the image that is inside the crop box
      var crop_canvas,
      left = $(‘.overlay’).offset().left – $container.offset().left,
      top = $(‘.overlay’).offset().top – $container.offset().top,
      width = $(‘.overlay’).width(),
      height = $(‘.overlay’).height();

      crop_canvas = document.createElement(‘canvas’);
      crop_canvas.width = width;
      crop_canvas.height = height;
      crop_canvas.getContext(‘2d’).drawImage(image_target, left, top, width, height, 0, 0, width, height);

      //=== set our canvas data
      var canvasData = crop_canvas.toDataURL(‘image/png’);

      //=== call ajax to fire php save function
      var ajax = new XMLHttpRequest();
      ajax.open(‘POST’,’savefile.php’,false);
      ajax.setRequestHeader(‘Content-Type’, ‘application/upload’);
      ajax.send(canvasData);
      }

  27. Hi,

    thanks for this, it’s very helpfull.
    But I have a problem. My website have to work on mobile devices. I changed the overlay size to crop a 390px * 390px picture. It works fine, but on smartphones, the screen’s size is 320px (well, it’s the size of my website on smartphone).

    How can I change on mobile the size of all (the picture, the crop zone, etc…) but get after crop my 390px * 390px picture ?