I'm trying to get an image to be displayed on an HTML5 Canvas while keeping the aspect ratio and having the image reduced (if larger than the canvas). I've look around at some answers but seem to be missing something and wondered if you guys can help.
I'm using the function "calculateAspectRatioFit" suggested by one of the Stackoverflow answers, but it seems to not resize the image for me, as it did in the answer - so I may be doing something wrong :)
Here's my code:
function calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) {
var ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
var rtnWidth = srcWidth*ratio;
var rtnHeight = srcHeight*ratio;
return { width: rtnWidth, height: rtnHeight };
}
var canvasImage = new Image();
canvasImage.src = "http://www.greenwallpaper.org/backgrounds/simply-green-502085.jpeg";
var ctx = this.getContext('2d');
var parentWidth = self._widgetSize[0];
var parentHeight = self._widgetSize[1];
canvasImage.onload = function() {
var imgSize = calculateAspectRatioFit(canvasImage.width, canvasImage.height, parentWidth, parentHeight);
ctx.clearRect(0, 0, parentWidth, parentHeight);
ctx.drawImage(canvasImage, 0, 0,imgSize.width, imgSize.height);
};
The image is displayed but is larger than the HTML5 Canvas. What I am after is to have the image the same width as the Canvas, and if the height is larger than the height of the canvas then it overflows and is hidden...I just want to fill the width and keep the aspect ratio.
Can anyone help point out what I am missing?
Appreciate your help :)
---Update 30/11---
I've just added the answer to my code and I get the following: (this is the 2nd answer)
var canvasImage = new Image();
canvasImage.src = "http://www.greenwallpaper.org/backgrounds/simply-green-502085.jpeg";
var ctx = this.getContext('2d');
canvasImage.onload = scaleAndDraw(this, ctx, canvasImage);
function scaleAndDraw(canvas, ctx, srcImage) {
// Image is 2560x1600 - so we need to scale down to canvas size...
// does this code actually 'scale' the image? Image of result suggests it doesn't.
var aspect = getAspect(canvas, srcImage);
var canvasWidth = (srcImage.width * aspect);
var canvasHeight = (srcImage.height * aspect);
ctx.drawImage(srcImage, 0, 0, canvasWidth|0, canvasHeight|0);
}
function getAspect(canvas, image) {
return canvas.size[0] / image.width;
}
So the code for displaying the image works, but the image is keeping it's dimensions and is not resizing to the dimensions of the canvas.
Hopefully the image will help you see the problem I am having. Larger images do not seem to rescale to fit in the canvas while keeping aspect ratio.
Any thoughts? :)
Your code works for me as long as I supply sane parentWidth,parentHeight values:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
function calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) {
var ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
var rtnWidth = srcWidth*ratio;
var rtnHeight = srcHeight*ratio;
return { width: rtnWidth, height: rtnHeight };
}
var parentWidth = 100; //self._widgetSize[0];
var parentHeight = 50; //self._widgetSize[1];
var canvasImage = new Image();
canvasImage.onload = function() {
var imgSize = calculateAspectRatioFit(canvasImage.width, canvasImage.height, parentWidth, parentHeight);
ctx.clearRect(0, 0, parentWidth, parentHeight);
ctx.drawImage(canvasImage,30,30,imgSize.width, imgSize.height);
};
canvasImage.src = "http://www.greenwallpaper.org/backgrounds/simply-green-502085.jpeg";
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>
Related
I am using cropper JS to crop my image. I am able to get width and height of my canvas, however, need to know co-ordinates (X and Y) of cropped image.
Here is my code-
(function () {
//getting ratio
function getImageRatio(sourceCanvas) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var width = sourceCanvas.width;
var height = sourceCanvas.height;
canvas.width = width;
canvas.height = height;
context.beginPath();
context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI);
context.strokeStyle = 'rgba(0,0,0,0)';
context.stroke();
context.clip();
context.drawImage(sourceCanvas, 0, 0, width, height);
return canvas;
}
window.addEventListener('DOMContentLoaded', function () {
var image = document.getElementById('image');
var button = document.getElementById('button');
var result = document.getElementById('result');
var croppable = false;
var cropper = new Cropper(image, {
aspectRatio: 1,
viewMode: 1,
built: function () {
croppable = true;
}
});
button.onclick = function () {
var croppedCanvas;
var roundedCanvas;
var roundedImage;
if (!croppable) {
return;
}
// Crop
croppedCanvas = cropper.getCroppedCanvas();
console.log(getImageRatio(croppedCanvas));
};
});
})();
Any idea, how to get coordinate of cropped image, so that I can crop this image by PHP.
So, if i understand properly, you are looking for the method getData.
It will return these properties as the documentation says:
x: the offset left of the cropped area
y: the offset top of the cropped area
width: the width of the cropped area
height: the height of the cropped area
rotate: the rotated degrees of the image
scaleX: the scaling factor to apply on the abscissa of the image
scaleY: the scaling factor to apply on the ordinate of the image
Although, i suggest taking a look at this section of the doc:
https://github.com/fengyuanchen/cropper#getcroppedcanvasoptions
Specifically at this part:
After then, you can display the canvas as an image directly, or use HTMLCanvasElement.toDataURL to get a Data URL, or use HTMLCanvasElement.toBlob to get a blob and upload it to server with FormData if the browser supports these APIs.
If you cannot use the toBlob method because some browser limitation (you can always add a polyfill), you can get the image using toDataUrl over the canvas (IE9+). And send it to the backend service already cropped. For this you are going to need to do something like this:
var formData = new FormData();
formData.append('image', canvas.toDataURL());
You can check the formData browser support in caniuse
Hope you find it helpful!
I am attempting to resize an image's dimensions, whilst retaining it's aspect ratio. This seems like a very simple task, but I cannot find a rellevent answer anywhere.. (relating to JavaScript's Image() object). I'm probably missing something very obvious. Below is what I am trying to achieve:
var img = new window.Image();
img.src = imageDataUrl;
img.onload = function(){
if (img.width > 200){
img.width = 200;
img.height = (img.height*img.width)/img.width;
}
if (img.height > 200){
img.height = 200;
img.width = (img.width*img.height)/img.height;
}
};
This is to proportionally resize an image before being drawn onto a canvas like so: context.drawImage(img,0,0,canvas.width,canvas.height);.However it would appear I cannot directly change Image()dimensions, so how is it done? Thanks.
Edit: I haven't correctly solved the image proportions using cross-multiplication. #markE provides a neat method of obtaining the correct ratio. Below is my new (working) implementation:
var scale = Math.min((200/img.width),(200/img.height));
img.width = img.width*scale;
img.height = img.height*scale;
clearCanvas(); //clear canvas
context.drawImage(img,0,0,img.width,img.height);
Here's how to scale your image proportionally:
function scalePreserveAspectRatio(imgW,imgH,maxW,maxH){
return(Math.min((maxW/imgW),(maxH/imgH)));
}
Usage:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/balloon.png";
function start(){
canvas.width=100;
canvas.height=100;
var w=img.width;
var h=img.height;
// resize img to fit in the canvas
// You can alternately request img to fit into any specified width/height
var sizer=scalePreserveAspectRatio(w,h,canvas.width,canvas.height);
ctx.drawImage(img,0,0,w,h,0,0,w*sizer,h*sizer);
}
function scalePreserveAspectRatio(imgW,imgH,maxW,maxH){
return(Math.min((maxW/imgW),(maxH/imgH)));
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<h4>Original Balloon image resized to fit in 100x100 canvas</h4>
<canvas id="canvas" width=100 height=100></canvas>
The image dimensions are set in the constructor
new Image(width, height)
// for proportionally consistent resizing use new Image(width, "auto")
or
the context.drawImage()
the arguments are as follows:
context.drawImage(image source, x-coordinate of upper left portion of image,
y-coordinate of upper left portion of image,image width,image height);
simply position the the image with the first two numeric coordinates and then manually adjust the size with the last two (width,height)
//ex.
var x = 0;
var y = 0;
var img = new Image (200, "auto");
img.src = "xxx.png";
context.drawImage(img,x,y,img.width,img.height);
I have this example of a canvas as a div background (I forked it from another example, don't remember where I found it):
http://jsfiddle.net/sevku/6jace59t/18/
var canvas = document.getElementsByTagName('canvas')[0];
var ctx = canvas.getContext('2d');
var divHeight = document.getElementById('canvas').clientHeight;
var divWidth = document.getElementById('canvas').clientWidth;
function assignToDiv(){ // this kind of function you are looking for
dataUrl = canvas.toDataURL();
document.getElementsByTagName('div')[0].style.background='url('+dataUrl+')'
}
function draw() { // replace with your logic
ctx.fillStyle = "rgb(100, 250, 100)";
ctx.fillRect (10, 10, divWidth-20, divHeight-20);
}
draw()
assignToDiv()
My problem; If I put the dimensions of the div 300 x 150, the canvas does what it is supposed to do. But if I change the dimensions, the canvas is supposed to adapt to the div dimensions. What did I do wrong that this doesn't happen?
PS: I'm a beginner, so please forgive me stupid questions.
It's because if you don't give canvas width and height, it's default to 300x150, so after you get the width and height from div, you should use them to set your canvas' dimensions as well.
Another point worth notice is that you use div.style.background property to set the background image, however, as there's many background related properties (e.g: background-repeat in your jsfiddle, background-position, background-size...), the background can set all of them at once.
When you use div.style.background='url('+dataUrl+')';. It overrides all other background-related properties to initial.
If you want to preserve those properties, you may either reset them after you set style.background, or you can use div.style.backgroundImage to change the background image without affect other background related properties.
jsfiddle
var canvas = document.getElementsByTagName('canvas')[0];
var ctx = canvas.getContext('2d');
var divHeight = document.getElementById('canvas').clientHeight;
var divWidth = document.getElementById('canvas').clientWidth;
// VVVV After you get WxH, set the canvas's dimension too.
canvas.width = divWidth;
canvas.height = divWidth;
var div1 = document.getElementById('canvas');
var div2 = document.getElementById('canvas2');
function assignToDiv(div){ // this kind of function you are looking for
var dataUrl = canvas.toDataURL();
div.style.background='url('+dataUrl+')'; // This line will overwrite your background settings.
div.style.backgroundRepeat = 'repeat-x'; // Use this to set background related properties after above.
}
function assignToDivAlt(div){ // this kind of function you are looking for
var dataUrl = canvas.toDataURL();
div.style.backgroundImage = 'url('+dataUrl+')'; // Only set the background-image would have same effect.
}
function draw() { // replace with your logic
ctx.fillStyle = "rgb(100, 250, 100)";
// If you don't set WH, then the canvas would be 300x150, and those
// you drawed but out of boundary are clipped.
ctx.fillRect (10, 10, divWidth-20, divHeight-20);
}
draw()
assignToDiv(div1);
assignToDiv(div2);
canvas {display:none;}
div {
width:600px;
height:550px;
border:1px solid grey;
background-repeat:repeat-x;
}
<canvas></canvas>
<div id="canvas"></div>
<div id="canvas2"></div>
I am trying to draw the following image to a canvas but it appears blurry despite defining the size of the canvas. As you can see below, the image is crisp and clear whereas on the canvas, it is blurry and pixelated.
and here is how it looks (the left one being the original and the right one being the drawn-on canvas and blurry.)
What am I doing wrong?
console.log('Hello world')
var c = document.getElementById('canvas')
var ctx = c.getContext('2d')
var playerImg = new Image()
// http://i.imgur.com/ruZv0dl.png sees a CLEAR, CRISP image
playerImg.src = 'http://i.imgur.com/ruZv0dl.png'
playerImg.width = 32
playerImg.height = 32
playerImg.onload = function() {
ctx.drawImage(playerImg, 0, 0, 32, 32);
};
#canvas {
background: #ABABAB;
position: relative;
height: 352px;
width: 512px;
z-index: 1;
}
<canvas id="canvas" height="352" width="521"></canvas>
The reason this is happening is because of Anti Aliasing.
Simply set the imageSmoothingEnabled to false like so
context.imageSmoothingEnabled = false;
Here is a jsFiddle verson
jsFiddle : https://jsfiddle.net/mt8sk9cb/
var c = document.getElementById('canvas')
var ctx = c.getContext('2d')
var playerImg = new Image()
// http://i.imgur.com/ruZv0dl.png sees a CLEAR, CRISP image
playerImg.src = 'http://i.imgur.com/ruZv0dl.png'
playerImg.onload = function() {
ctx.imageSmoothingEnabled = false;
ctx.drawImage(playerImg, 0, 0, 256, 256);
};
Your problem is that your css constraints of canvas{width:512}vs the canvas property width=521will make your browser resample the whole canvas.
To avoid it, remove those css declarations.
var c = document.getElementById('canvas')
var ctx = c.getContext('2d')
var playerImg = new Image()
// http://i.imgur.com/ruZv0dl.png sees a CLEAR, CRISP image
playerImg.src = 'http://i.imgur.com/ruZv0dl.png'
playerImg.width = 32
playerImg.height = 32
playerImg.onload = function() {
ctx.drawImage(playerImg, 0, 0, 32, 32);
};
#canvas {
background: #ABABAB;
position: relative;
z-index: 1;
}
<canvas id="canvas" height="352" width="521"></canvas>
Also, if you were resampling the image (from 32x32 to some other size), #canvas' solution would have been the way to go.
As I encountered this older post for some of my issues, here's even more additional insight to blurry images to layer atop the 'imageSmoothingEnabled' solution.
This is more specifically for the use case of monitor specific rendering and only some people will have encountered this issue if they have been trying to render retina quality graphics into their canvas with disappointing results.
Essentially, high density monitors means your canvas needs to accommodate that extra density of pixels. If you do nothing, your canvas will only render enough pixel information into its context to account for a pixel ratio of 1.
So for many modern monitors who have ratios > 1, you should change your canvas context to account for that extra information but keep your canvas the normal width and height.
To do this you simply set the rendering context width and height to: target width and height * window.devicePixelRatio.
canvas.width = target width * window.devicePixelRatio;
canvas.height = target height * window.devicePixelRatio;
Then you set the style of the canvas to size the canvas in normal dimensions:
canvas.style.width = `${target width}px`;
canvas.style.height = `${target height}px`;
Last you render the image at the maximum context size the image allows. In some cases (such as images rendering svg), you can still get a better image quality by rendering the image at pixelRatio sized dimensions:
ctx.drawImage(
img, 0, 0,
img.width * window.devicePixelRatio,
img.height * window.devicePixelRatio
);
So to show off this phenomenon I made a fiddle. You will NOT see a difference in canvas quality if you are on a pixelRatio monitor close to 1.
https://jsfiddle.net/ufjm50p9/2/
In addition to #canvas answer.
context.imageSmoothingEnabled = false;
Works perfect. But in my case changing size of canvas resetting this property back to true.
window.addEventListener('resize', function(e){
context.imageSmoothingEnabled = false;
}, false)
The following code works for me:
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
context.drawImage(img, 0, 0, img.width, img.height, 0, 0, img.width, img.height);
};
img.src = e.target.result; // your src
Simple tip: draw with .5 in x and y position. like drawImage(, 0.5, 0.5) :D There you get crisp edges :D
I'll post a link since theres to much to post here:
http://hem.bredband.net/noor/canvas.htm
My goal is to make the picture fit inside the window with the width of the image being the same as the window, even after resize. If the pictures height becomes to big for the window then the picture should resize itself according to the height and not the width.
Somewhere along my code there is something wrong.. forgive me if this is stupid, i am still learning..
Thanks!
I think this is the behavior you are trying to achieve. I refactored it so you only need to create one Image object (and changed the variable names to English so I could follow the code easier). Hopefully it's clear how the code works. Feel free to ask in the comments if you have any questions about it. The body onload property will need to be changed to "setupBackground();" instead of "draw();" to work with this code.
function setupBackground() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
function draw() {
canvas.width = 0;
canvas.height = 0;
var divHeight = div.scrollHeight;
var divWidth = div.scrollWidth;
var yScale = divHeight / img.height;
var xScale = divWidth / img.width;
var newImgHeight = img.height * xScale;
var newImgWidth = divWidth;
if (divHeight >= newImgHeight) {
newImgHeight = divHeight;
newImgWidth = img.width * yScale;
}
canvas.width = divWidth;
canvas.height = divHeight;
ctx.drawImage(img,0,0,newImgWidth,newImgHeight);
}
var img = new Image();
img.onload = function() {
window.onresize = draw;
draw();
}
img.src = 'ad.jpg';
};
As far as I know. You only want to make one call to getContext(). I didn't really analyze the code very much past this point though.