
HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .  HTML5,     ,      ,   . 
 
       -    .      ,           .   ,          . 
  
      canvas ,     ,    .     ,       ,       . 
 
            .          .     ,     ,     .            ,       . 
 
      ! 
 
  
          : 
 <img class="resize-image" src="image.jpg" alt="Image" /> 
 
 !  HTML   . 
 
 CSS 
 CSS-     .    resize-container   . 
 
 .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); } 
 
        'resize handles'.   ,    ,       . 
 
 .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; } 
 
 JavaScript 
        Canvas. 
 
 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')); 
 
    ,    .     ,    ,        .     jQuery  ,               . 
 
 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(); } 
 
  startResize  endResize   ,        ,     . 
 
 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); }; 
 
            .      event_state     . 
 
 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; } 
 
  resizing -  .     .             . 
 
 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}); } } 
 
           Shift  . 
 
           . 
 
   :      ,        ,         resizeImage     . 
 
    
    Canvas   ,   drawImage .      ,    .   toDataURL   Base64-encoded   . 
 
         . 
 
 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")); }; 
 
  ?   :          ,        CORS .    ,       'tainted canvas'. 
 
     
 
       .     .           ,     .    ,   . 
 
    ,       ,        . 
 
  
 
 
     ,         .    resizing : 
 
 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}); } } 
 
   ,   resize-handle  ,    . 
 
   
 ,      , , , ,    "".        .      . 
 
 init = function(){ //... $container.on('mousedown', 'img', startMoving); } 
 
     startMoving  endMoving ,   startResize  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); }; 
 
   moving       .        ,     . 
 
 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 ) }); }; 
 
   
 ,     ,    .  ,        ,    ,      ,      .       ,  , ,    ,      . 
 
      HTML : 
 
 <div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button> 
 
  ,          ,    . 
 
 .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; } 
 
   JavaScript : 
 
 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")); } 
 
  crop   resizeImage .    ,          . 
 
        drawImage  canvas.  -  .   - ,   .   -  ,       canvas,     . 
 
      
    .        . 
 
  mousedown  mouseup    - touchstart  touchend ,   mousemove  touchmove .   ,      touchup  touchdown (     ). 
 
   touchstart  touchend ,     mousedown ,  mouseup   touchmove ,   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); 
 
      ,       ""     .        Hammer,    . ,      ,       . 
 
 , , ,   saveEventState      .    . 
 
     " "    .     ,        .   moving : 
 
 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); } }; 
 
                . 
 
  
 
 
   .         . 
 
    Chrome        "",  Firefox    . 
 
 ,    .HTML5, , , .
- . , . , .
canvas , , . , , .
. . , , . , .
!
:
<img class="resize-image" src="image.jpg" alt="Image" />
! HTML .
CSS
CSS- . resize-container .
.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); }
'resize handles'. , , .
.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; }
JavaScript
Canvas.
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'));
, . , , . jQuery , .
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(); }
startResize endResize , , .
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); };
. event_state .
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; }
resizing - . . .
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}); } }
Shift .
.
: , , resizeImage .
Canvas , drawImage . , . toDataURL Base64-encoded .
.
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")); };
? : , CORS . , 'tainted canvas'.
. . , . , .
, , .

, . resizing :
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}); } }
, resize-handle , .
, , , , , "". . .
init = function(){ //... $container.on('mousedown', 'img', startMoving); }
startMoving endMoving , startResize 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); };
moving . , .
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 ) }); };
, , . , , , , . , , , , .
HTML :
<div class="overlay"> <div class="overlay-inner"> </div> </div> <button class="btn-crop js-crop">Crop</button>
, , .
.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; }
JavaScript :
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")); }
crop resizeImage . , .
drawImage canvas. - . - , . - , canvas, .
. .
mousedown mouseup - touchstart touchend , mousemove touchmove . , touchup touchdown ( 
touchstart touchend , mousedown , mouseup touchmove , 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);
, "" . Hammer, . , , .
, , , saveEventState . .
" " . , . moving :
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); } };
.

. .
Chrome "", Firefox .
, .Source: https://habr.com/ru/post/229809/
All Articles