crop an image using canvas - result is small image - javascript

I'm trying to crop an image using canvas.
Original image is 2217 x 790.
Loading on page it is scaled to 1515 x 540
Canvas is 960 x 540.
Both image and canvas are on center of screen, so aligned horizontally.
I need to crop central area - 960 x 540.
var img = document.getElementById("imgt");
var canvas = document.getElementById("canvasa");
var ctx = canvas.getContext("2d");
var a = $('#imgt').width() - 960;
var a = a/2; // this is 277.7...
ctx.drawImage(img, a, 0, 960, 540, 0, 0, 960, 540);
//also tried:
ctx.drawImage(img, 0, 0, 960, 540, 0, 0, 960, 540);
var newimg = new Image();
newimg.src = canvas.toDataURL('image/jpeg');
var dl = document.createElement("a");
dl.href = canvas.toDataURL("image/jpeg");
dl.download = true;
document.body.appendChild(dl);
dl.click();
Downloading newimg what I see - it is 300 x 150 !

See my comment for differences between width attribute and width as style. They are not exactly the same. Besides, I just made a fiddle since you didn't and I don't get the same behavior! The downloaded image is 960 * 540
HERE - fires with delay
ONLOAD
setTimeout(function(){
var canvas = document.createElement("canvas");
canvas.width = "960";
canvas.height="540";
var ctx = canvas.getContext("2d");
ctx.drawImage(document.images[0], 0, 0, 960, 540, 0, 0, 960, 540);
var a = document.createElement("a");
a.download = "image.jpeg";
a.href = canvas.toDataURL("image/jpeg");
a.click();
},5000);

In general, if you want to crop an area from a source image, and draw it into a canvas without breaking the aspect ratio and not hardcoding the dimensions into the routine, you can do this:
const canvasAspectRatio = canvas.width / canvas.height;
const cropWidth = canvas.width;
const cropHeight = cropWidth / canvasAspectRatio;
const sx = img.width / 2 - cropWidth / 2;
const sy = img.height / 2 - cropHeight / 2;
ctx.drawImage(img, sx, sy, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const img = document.getElementById('img');
const canvasAspectRatio = canvas.width / canvas.height;
// Crop central canvas sized rectangle area into canvas
const cropWidth = canvas.width;
const cropHeight = cropWidth / canvasAspectRatio; // Here you should calculate the height based on aspect ratio instead of assuming it matches that of the canvas
const sx = img.width / 2 - cropWidth / 2;
const sy = img.height / 2 - cropHeight / 2;
ctx.drawImage(img, sx, sy, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
body {
background-color: black;
}
#img {
visibility: hidden;
}
#canvas {
border: 1px solid #f00;
}
<canvas id="canvas" width="960" height="540"></canvas>
<img id="img" src="http://via.placeholder.com/2217x790">

Related

How can I crop an area of an image using JavaScript?

How can I crop an area of an image using JavaScript? As I have read, I must use a canvas to project the image on it.
With the following code I am cutting an area of an image, but the size of the cut area is not the indicated one.
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
// draw cropped image
var sourceX = 0;
var sourceY = 0;
var sourceWidth = 500;
var sourceHeight = 150;
var destWidth = sourceWidth;
var destHeight = sourceHeight;
var destX = canvas.width / 2 - destWidth / 2;
var destY = canvas.height / 2 - destHeight / 2;
context.drawImage(imageObj, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
<canvas id="myCanvas" style="border:1px solid red"></canvas>
I am trying to represent in this image what I want to do.
my main problem is that the canvas does not adapt to the size of the cropped image and the final height (sourceHeight ) and width (sourceWidth ) They are not the ones I specified
How can i fix it?
The problem
Your source "width" is the same width as the image.
The solution
When using ctx.drawImage with 9 arguments, think of it like a cut and paste.
You want to "cut" the outline in red, and "paste" it to your new - centered - location.
You want to "cut" all the way up to the half way point of the source. So you need to select all the way up to the half way point of the image.
I also suggest maybe changing the variable name from "source" to "crop" (cropX, cropWidth, etc) to better reflect its purpose, as it is not really the width of the "source" anymore.
If you want the image to fill the entire canvas, "paste" it with the canvas' width and height at (0,0)
context.drawImage(imageObj, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, canvas.width, canvas.height);
The code
...
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
// crop from 0,0, to 250,150
var cropX = 0;
var cropY = 0;
var cropWidth = 250;
var cropHeight = 150;
//resize our canvas to match the size of the cropped area
canvas.style.width = cropWidth;
canvas.style.height = cropHeight;
//fill canvas with cropped image
context.drawImage(imageObj, cropX, cropY, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
...
You'll need to tell the canvas the size of the image you're trying to display to ensure the canvas has the desiredWith size;
However, the size of your example image is 438 × 300, which makes it hard to crop to 500px.
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.src = 'https://placehold.it/700x700';
imageObj.onload = function() {
const desiredWidth = 500;
const desiredHeight = 150;
canvas.width = desiredWidth;
canvas.height = desiredHeight;
context.drawImage(imageObj, 0, 0, desiredWidth, desiredHeight, 0, 0, desiredWidth, desiredHeight);
console.log(canvas.width, canvas.height);
};
<canvas id="myCanvas" style="border:1px solid red"></canvas>

Why is HTML5 Canvas blurry?

I have a canvas that is supposed to fill 100% of its parent div and scale height based on a ratio. The image that is placed in it is supposed to fill the canvas's height while scaling its width so it doesn't get distorted. That seems to be working but everything on the canvas is really blurry. Why is this happening and how might I display crisp images, shapes and text?
const photo = 'https://docplayer.net/docs-images/54/10835314/images/page_1.jpg';
const canvasRef = document.getElementById('canvas');
const mugRatioWidth = 2681;
const mugRatioHeight = 1110;
const canvasRatio = mugRatioHeight / mugRatioWidth;
const img = new Image();
const ctx = canvasRef.getContext("2d");
canvasRef.style.width = '100%';
img.src = photo;
img.onload = () => {
const hRatio = canvasRef.width / img.width ;
const vRatio = canvasRef.height / img.height ;
const ratio = Math.min ( hRatio, vRatio );
const centerShift_x = ( canvasRef.width - img.width*ratio ) / 2;
const centerShift_y = ( canvasRef.height - img.height*ratio ) / 2;
ctx.clearRect(0,0,canvasRef.width, canvasRef.height);
ctx.drawImage(img, 0,0, img.width, img.height, centerShift_x,centerShift_y,img.width*ratio, img.height*ratio);
ctx.beginPath();
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
ctx.stroke();
ctx.fillText("Text example!", 10, 90);
}
<canvas id='canvas'/>

Reduce the dimension of base64 image maintaining aspect ratio in javascript

I have a base64 string for an image, which i have to display as an icon. If the dimension of an image are bigger i have to display icon maintaining aspect ratio.
I have written a logic to identify if the image is landscape or portrait based on which height and width will be settled for canvas. But seems height and width of icons are not proper as i have hard coded it.
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var height, width;
if (this.height>this.width) {
height = 50;
width = 30;
} else if (this.height<this.width) {
height = 30;
width = 50;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(image,
0, 0, this.width, this.height,
0, 0, canvas.width, canvas.height
);
var scaledImage = new Image();
scaledImage.src = canvas.toDataURL();
Is there any way we can calculate it dynamically or any other way to preserve aspect ratio for icons.
Working Example can be found on https://codepen.io/imjaydeep/pen/ewBZRK
It will be fine if some space will be left on x-y axis.
You just need to calculate the scale or ratio and multiply both dimensions by that. Here's an example function, and here's your edited codepen.
Creates trimmed, scaled image:
function scaleDataURL(dataURL, maxWidth, maxHeight){
return new Promise(done=>{
var img = new Image;
img.onload = ()=>{
var scale, newWidth, newHeight, canvas, ctx;
if(img.width < maxWidth){
scale = maxWidth / img.width;
}else{
scale = maxHeight / img.height;
}
newWidth = img.width * scale;
newHeight = img.height * scale;
canvas = document.createElement('canvas');
canvas.height = newHeight;
canvas.width = newWidth;
ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, newWidth, newHeight);
done(canvas.toDataURL());
};
img.src = dataURL;
});
}
Or, if you want the image to always be the provided dimensions with empty surrounding area, you can use this: (codepen)
Creates scaled image of exact provided dimensions:
function scaleDataURL(dataURL, maxWidth, maxHeight){
return new Promise(done=>{
var img = new Image;
img.onload = ()=>{
var scale, newWidth, newHeight, canvas, ctx, dx, dy;
if(img.width < maxWidth){
scale = maxWidth / img.width;
}else{
scale = maxHeight / img.height;
}
newWidth = img.width * scale;
newHeight = img.height * scale;
canvas = document.createElement('canvas');
canvas.height = maxWidth;
canvas.width = maxHeight;
ctx = canvas.getContext('2d');
dx = (maxWidth - newWidth) / 2;
dy = (maxHeight - newHeight) / 2;
console.log(dx, dy);
ctx.drawImage(img, 0, 0, img.width, img.height, dx, dy, newWidth, newHeight);
done(canvas.toDataURL());
};
img.src = dataURL;
});
}
For someone who search for a solution when the image is bigger that you need.
function scaleDataURL(dataURL: string, maxWidth: number, maxHeight: number) {
return new Promise((done) => {
var img = new Image()
img.onload = () => {
var scale, newWidth, newHeight, canvas, ctx
if (img.width > maxWidth) {
scale = maxWidth / img.width
} else if (img.height > maxHeight) {
scale = maxHeight / img.height
} else {
scale = 1
}
newWidth = img.width * scale
newHeight = img.height * scale
canvas = document.createElement('canvas')
canvas.height = newHeight
canvas.width = newWidth
ctx = canvas.getContext('2d')
console.log('img', 'scale', scale, 0, 0, img.width, img.height, 0, 0, newWidth, newHeight)
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, newWidth, newHeight)
done(canvas.toDataURL())
}
img.src = dataURL
})
}
Usage:
scaleDataURL(base64data, 600, 200)
.then((newBase64data) => {
console.log(newBase64data)
})
.catch((err) => console.log(err))

noisy pic after drawing big PNG file on cavnas

I have problem with drawing this (1766 * 2880) PNG file on canvas.
But I don't have any problem with JPG format of that size or (1533 * 2500) size of PNG file.
I consider devicePixelRatio for scaling canvas, and ignoring the ratio makes no changes.
var loadImage = function (url) {
var ratio = Math.max(window.devicePixelRatio || 1, 1),
image = new Image();
image.onload = function () {
var paper = '#paper-0',
canvas = $(paper)[0],
ctx = canvas.getContext("2d"),
img = this;
var w = Math.ceil(img.width / ratio), h = Math.ceil(img.height / ratio);
$(paper).css({ width: w + 'px', height: h + 'px' });
canvas.width = w * ratio;
canvas.height = h * ratio;
ctx.scale(ratio, ratio);
ctx.drawImage(img, 0, 0, w, h);
}
image.src = url;
}

Scaling an image to fit on canvas

I have a form that allows a user to upload an image.
Once the image is loaded, we perform some scaling on it in order to reduce its filesize before we pass it back to the server.
To do this, we place it on the canvas and manipulate it there.
This code will render the scaled image on the canvas, with the canvas of size 320 x 240px:
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
... where canvas.width and canvas.height is the image height and width x a scaling factor based on the size of the original image.
But when I go to use the code:
ctx.drawImage(img, 0, 0, canvas.width, canvas.height, 0, 0, canvas.width, canvas.height
... I only get part of the image on the canvas, in this case the top left corner. I need the whole image 'scaled' to fit on the canvas, despite the actual image size being larger than the 320x240 canvas size.
So for the code above, the width and heights are 1142x856, as that is the final image size. I need to maintain that size to pass beck to the server when the form is submitted, but only want a smaller view of it to appear in the canvas for the user.
What am I missing here? Can anyone point me in the right direction please?
You made the error, for the second call, to set the size of source to the size of the target.
Anyway i bet that you want the same aspect ratio for the scaled image, so you need to compute it :
var hRatio = canvas.width / img.width ;
var vRatio = canvas.height / img.height ;
var ratio = Math.min ( hRatio, vRatio );
ctx.drawImage(img, 0,0, img.width, img.height, 0,0,img.width*ratio, img.height*ratio);
i also suppose you want to center the image, so the code would be :
function drawImageScaled(img, ctx) {
var canvas = ctx.canvas ;
var hRatio = canvas.width / img.width ;
var vRatio = canvas.height / img.height ;
var ratio = Math.min ( hRatio, vRatio );
var centerShift_x = ( canvas.width - img.width*ratio ) / 2;
var centerShift_y = ( canvas.height - img.height*ratio ) / 2;
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.drawImage(img, 0,0, img.width, img.height,
centerShift_x,centerShift_y,img.width*ratio, img.height*ratio);
}
you can see it in a jsbin here :
http://jsbin.com/funewofu/1/edit?js,output
Provide the source image (img) size as the first rectangle:
ctx.drawImage(img, 0, 0, img.width, img.height, // source rectangle
0, 0, canvas.width, canvas.height); // destination rectangle
The second rectangle will be the destination size (what source rectangle will be scaled to).
Update 2016/6: For aspect ratio and positioning (ala CSS' "cover" method), check out:
Simulation background-size: cover in canvas
I guess that you want the image to be scaled to a smaller size, without losing the ratio of the dimensions. I have a solution.
var ratio = image.naturalWidth / image.naturalHeight;
var width = canvas.width;
var height = width / ratio;
ctx.drawImage(image, 0, 0, width, height);
the ratio will be maintained. And the image drawn on the canvas will be of the same ratio. you can use the if loop if the height of the image is long, you can replace the canvas.width to some other width
You can call ctx.scale() before calling ctx.drawImage:
var factor = Math.min ( canvas.width / img.width, canvas.height / img.height );
ctx.scale(factor, factor);
ctx.drawImage(img, 0, 0);
ctx.scale(1 / factor, 1 / factor);
This should preserve the aspect ratio.
HTML:
<div id="root"></div>
JavaScript:
const images = [
'https://cdn.pixabay.com/photo/2022/07/25/15/18/cat-7344042_960_720.jpg',
'https://cdn.pixabay.com/photo/2022/06/27/08/37/monk-7287041_960_720.jpg',
'https://cdn.pixabay.com/photo/2022/07/18/19/57/dog-7330712_960_720.jpg',
'https://cdn.pixabay.com/photo/2022/05/22/18/25/spain-7214284_960_720.jpg',
];
const root = document.getElementById('root');
const image = new Image();
image.crossOrigin = 'anonumys';
image.src = images[3];
const canvas = document.createElement('canvas');
canvas.classList.add('track');
canvas.width = 600;
canvas.height = 400;
const ctx = canvas.getContext('2d');
const meta = {
ratio: image.width / image.height,
width: 0,
height: 0,
offsetX: 0,
offsetY: 0,
};
if (meta.ratio >= 1) {
meta.width = canvas.width > image.width ? image.width : canvas.width;
meta.height = meta.width / meta.ratio;
} else {
meta.height = canvas.height > image.height ? image.height : canvas.height;
meta.width = meta.height * meta.ratio;
}
meta.offsetX = canvas.width > meta.width ? (canvas.width - meta.width) / 2 : 0;
meta.offsetY = canvas.height > meta.height ? (canvas.height - meta.height) / 2 : 0;
image.addEventListener('load', () => {
ctx.drawImage(image, meta.offsetX, meta.offsetY, meta.width, meta.height);
root.append(canvas);
});
console.log(meta);

Categories