user1775888 user1775888 - 1 month ago 19
Javascript Question

javascript crop image to canvas

I want to crop the image based on specific area, below example how to crop image so it becomes only the things (pixel, data...) inside blue line, and delete things (pixel, data...) outside the box?

Below code is what I tried, after canvas draw new image, not correct area what I want, do I miss something, how to fix it ?

Any suggestion will be appreciated.
Please don't provide plugin as an answer, and css crop is not what I'm looking for.

UPDATE

https://jsfiddle.net/xqpdtq76/
I update the fiddle and below code, the image naturalWidth is different with css width, so I need to convert the crop x,y,width,height (sx,sy,sw,sh) to fit the natural image size and crop the image.

the result it seems work to crop the correct area but image is not correct scale .. I don't get why



var crop = function() {

var transformMediaBlock = $('.mediaBlock');
var transformCropInner = $('.transformCropInner');
var transformCropLimit = $('.transformCropLimit');
var canvasContainer = $('.canvasContainer')

var canvasWidth = $(transformCropLimit).width();
var canvasHeight = $(transformCropLimit).height();
var canvas = $('<canvas/>',{'class':''}).width(canvasWidth).height(canvasHeight);
canvas = canvas.appendTo($(canvasContainer));
var ctx = canvas[0].getContext('2d');

var limitLeft = transformCropLimit.offset().left;
var limitTop = transformCropLimit.offset().top;
var limitRight = limitLeft + transformCropLimit.width();
var limitBottom = limitTop + transformCropLimit.height();
console.log('limitLeft:'+limitLeft)
console.log('limitRight:'+limitRight)
var imageLeft = transformMediaBlock.find('img').offset().left;
var imageTop = transformMediaBlock.find('img').offset().top;
var imageRight = imageLeft + transformMediaBlock.find('img').width();
var imageBottom = imageTop + transformMediaBlock.find('img').height();
console.log('imageLeft:'+imageLeft)
console.log('imageRight:'+imageRight)

if (limitLeft <= imageLeft) {
var sx = 0;
} else {
var sx = limitLeft - imageLeft;
}
console.log('sx:'+sx)

if (limitTop <= imageTop) {
var sy = 0;
} else {
var sy = limitTop - imageTop;
}
console.log('sy:'+sy)

if (limitLeft <= imageLeft) {
var l = imageLeft;
} else {
var l = limitLeft;
}
if (limitRight <= imageRight) {
var r = limitRight;
} else {
var r = imageRight;
}
var sw = r - l;
console.log('sw:'+sw)

if (limitTop <= imageTop) {
var t = imageTop;
} else {
var t = limitTop;
}
if (limitBottom <= imageBottom) {
var b = limitBottom;
} else {
var b = imageBottom;
}
var sh = b - t;
console.log('sh:'+sh);


var dx = 0;
var dy = 0;
var dw = sw;
var dh = sh;

console.log('naturalWidth:'+transformMediaBlock.find('img')[0].naturalWidth);
console.log('naturalHeight:'+transformMediaBlock.find('img')[0].naturalHeight);
console.log('cssWidth:'+transformMediaBlock.find('img').width());
console.log('cssHeight:'+transformMediaBlock.find('img').height());
var ratio = (transformMediaBlock.find('img')[0].naturalWidth / transformMediaBlock.find('img').width());
console.log('ratio:'+ratio);
sx = sx*ratio;
sy = sy*ratio;
sw = sw*ratio;
sh = sh*ratio;
console.log('sx*ratio:'+sx)
console.log('sy*ratio:'+sy)
console.log('sw*ratio:'+sw)
console.log('sh*ratio:'+sh)
ctx.drawImage(transformMediaBlock.find('img')[0],
sx, sy, sw, sh,
dx, dy, dw, dh
);

};


$('#container').on('click', '.action.crop', function (e) {
var transformMediaBlock = $('.mediaBlock');
transformMediaBlock.find('img').on('load', function() {
crop();
}).each(function() {
if(this.complete) $(this).load();
});
});

.mediaBlock {
position: relative;
display: block;
overflow: hidden;
}
.mediaBlock img {
max-width: 100%;
}

.transformCropLimit {
position: relative;
top: 20px;
left: 20px;
width: 200px;
height: 200px;
border: 1px solid blue;
}

.transformCropInner {
width: 300px;
cursor: pointer;
position: relative;
top: 10px;
left: 10px;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="content main">
<div class="transformCropLimit">
<div class="transformCropInner">
<div class="mediaBlock">
<img src="http://i.imgur.com/EdQs9jA.jpg">
</div>
</div>
</div>
<div class="action crop">Crop</div>
</div>
<div class="canvasContainer"></div>
</div>




Answer

To crop image

Create a canvas the size of the crop area, then draw the image on that canvas. For example if you have a 500 by 400 image and you want to crop it to the top left 100,100, and bottom right 200,200

var crop = {
   top : 100,
   left : 100,
   right : 200,
   bottom : 200,
}

Create a canvas the correct width and height

var canvas = document.createElement("canvas");
canvas.width = crop.right - crop.left;
canvas.height = crop.bottom - crop.top;

Then draw the image onto that canvas so that the top left 100,100 is at the canvas origin 0,0

var ctx = canvas.getContext("2d"); // so we can draw
ctx.drawImage(image, -crop.left, -crop.top);

And that is how you create a cropped image (Note canvas is a HTMLImageElement)

If you have scaled images.

var w = image.width;
var h = image.height;

and you draw it on a canvas at a scale

var myScale = 0.5; // half scale

and you want to crop it via the scaled canvas coordinates

var crop = {  // the canvas coordinate system
   top : 100,
   left : 100,
   right : 200,
   bottom : 200,
}

Your image scaled is in the canvas coordinate system with top left at (0,0) and its width and height are w * scale, h * scale To crop it just scale the image when you draw it.

Create the canvas.

var canvas = document.createElement("canvas");
canvas.width = crop.right - crop.left;
canvas.height = crop.bottom - crop.top;

draw the image scaled and offset in the image coordinates

var ctx = canvas.getContext("2d"); // so we can draw
ctx.scale(scale,scale);
ctx.drawImage(image, -crop.left / scale, -crop.top / scale); // convert offset to 
                                                             // image coordinate system