I am trying to resize an image using drawImage but keep the scale. I have this so far...
window.onload = function() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = document.getElementById("scream");
ctx.drawImage(img, 0, 0, 600, 428);
}
<p>Image to use:</p>
<img id="scream" width="400" height="300" src="http://lorempixel.com/1280/960/sports/" alt="The Scream">
<p>Canvas:</p>
<canvas id="myCanvas" width="428" height="600" style="border:2px solid black;">
Your browser does not support the HTML5 canvas tag.
</canvas>
I am trying to make the image fill the container to lose the white space at the bottom, if I put the exact dimensions in then it becomes stretched.
Does anyone have an example they can point me in the direction of?
First you need to find the aspect ratios of the image and the canvas. That is done by:
var canvasRatio = canvas.width / canvas.height,
imgRatio = img.width / img.height,
s = 1;
If a ratio is less than 1, then it is portrait, else if it is equal or higher than 1, it is (considered to be) landscape.
Then you need to compare those ratios like so:
//the image is »more portrait«
//to fully cover the canvas
//scale by width
if (imgRatio < canvasRatio) {
s = canvas.width / img.width;
//image has the same, or a »more landscape«
//ratio than the canvas
//to cover scale by height
} else {
s = canvas.height / img.height;
}
//scale the context
ctx.scale(s, s);
cts.drawImage(…);
UPDATE
It is even shorter this way (no ratios, no if):
var s = Math.max(canvas.width/img.width, canvas.height/img.height);
to fit the image use Math.min.
Related
I have a div that is on top of the image. I have to get the image content which is inside the overlaying div and draw using canvas.
The problem I am facing is, I am not able to draw the correct image. I have to programmatically get the correct image below the div.
The image below shows the comparison of the original image and drawn image in canvas.
Below is my code which drawsImage by taking the coordinates of the div:
DrawImage() {
var canvas: any = document.getElementById("myCanvas");
var canvasRef = canvas.getBoundingClientRect();
var ctx = canvas.getContext("2d");
var imagemarker = document.getElementById("imagemarker");
var markerRef = imagemarker.getBoundingClientRect();
var img = document.getElementById("myimage");
ctx.drawImage(
img,
markerRef.left,
markerRef.top,
markerRef.width,
markerRef.height,
0,
0,
canvasRef.width,
canvasRef.height
);
}
And the html code to display image and canvas element:
<button (click)="DrawImage()">Draw</button>
<div id="imagemarkercontainer">
<div id="imagemarker"></div>
</div>
<img id="myimage" src="https://imgur.com/NWQo2xk.jpeg" width="300px"
height="250px">
<br>
<canvas id="myCanvas" width="300px" height="250px" style="border:1px
solid #d3d3d3;"></canvas>
I have attached a stackblitz example that shows the problem. https://stackblitz.com/edit/angular-4untgz?file=src%2Fapp%2Fapp.component.ts
How can I achieve this?
You need to take care of img bounding rect and also natural width & height of image.
As you are setting image width & height, get naturalWidth and calculate proportional width as below
var imgRef = img.getBoundingClientRect();
var nWidth = img.naturalWidth;
var nHeight = img.naturalHeight;
ctx.drawImage(
img,
( markerRef.left - imgRef.left) * nWidth /img.width ,
( markerRef.top -imgRef.top) * nHeight / img.height ,
markerRef.width * nWidth /img.width,
markerRef.height * nHeight / img.height ,
0,
0,
canvasRef.width,
canvasRef.height
);
Here is working code
I am trying to mirror a section of an image, into a below canvas, of exactly the same size, based on fixed values. For example, present it a picture of a map, and mirror below in the same position and the same size a building on that original map image. However, for some unknown reason, it resizes the mirrored section every time? The only botch fix for this is to apply a multiplier to all "d" variables in the draw image function, as seen here: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
Any ideas as to why this is?
Code (minus the multiplier, multipler avaliable if needed):
<!DOCTYPE html>
<html>
<body>
<img id="scream" src="image1.jpg" alt="The Scream" width="100%" height="100%">
<!--<canvas id="myCanvas" style="border:1px solid #d3d3d3;">-->
Your browser does not support the HTML5 canvas tag.
<script>
document.getElementById("scream").onload = function() {
var c = document.createElement('canvas');
var imgData = document.getElementById("scream");
c.width = imgData.width;
c.height = imgData.height;
var body = document.getElementsByTagName("body")[0];
body.appendChild(c);
var ctx = c.getContext("2d");
var img = document.getElementById("scream");
console.log(img);
//ctx.drawImage(img, 0, 0);
//ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
ctx.drawImage(img, 200, 200, 100, 143, 200, 200, 100*1.87, 143*1.87);
//console.log(ctx);
};
</script>
<p><strong>Note:</strong> The canvas tag is not supported in Internet
Explorer 8 and earlier versions.</p>
</body>
</html>
In order to mirror the image you need to translate the context ctx.translate(0, this.height) and then scale it ctx.scale(1,-1);. However on resize the canvas will stay the initial size while the image will adapt to the window size. If you need the canvas to adapt as well you will need to recalculate everything on resize.
var imgData = document.getElementById("scream");
imgData.onload = function() {
var c = document.createElement('canvas');
c.width = this.width;
c.height = this.height;
var body = document.getElementsByTagName("body")[0];
body.appendChild(c);
var ctx = c.getContext("2d");
ctx.translate(0, this.height);
ctx.scale(1,-1);
ctx.drawImage(this, 0, 0,this.width,this.height);
};
<img id="scream" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/imgres.jpg" alt="The Scream" width="100%" height="100%">
Next comes an example where the canvas size changes on resize. In this case you need to move all the code that draws the image inside a function to be called on resize.
Quote from MDN:
Since resize events can fire at a high rate, the event handler shouldn't execute computationally expensive operations such as DOM modifications. Instead, it is recommended to throttle the event using requestAnimationFrame, setTimeout or customEvent"
window.onload = function() {
var imgData = document.getElementById("scream");
var c = document.createElement("canvas");
var body = document.getElementsByTagName("body")[0];
body.appendChild(c);
var ctx = c.getContext("2d");
imgData.onload = function() {
init();
};
function init() {
c.width = imgData.width;
c.height = imgData.height;
ctx.translate(0, imgData.height);
ctx.scale(1, -1);
ctx.drawImage(imgData, 0, 0, imgData.width, imgData.height);
}
setTimeout(function() {
init();
addEventListener("resize", init, false);
}, 15);
};
<img id="scream" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/imgres.jpg" alt="The Scream" width="100%" height="100%">
This is because you are using the ImageElement's .width and .height properties. At getting, these should return the computed value of their corresponding attribute, in px.
In the markup, these attributes have both been set to the relative value 100%, which means their computed value will be relative to the ImageElement's parent's size.
So when you ask drawIamge to draw your image using these output size, it will indeed resize / stretch your image. To avoid it, you need to use the original size of the image, not of the ImageElement.
To do so, if all you need is to draw at the same size as the original image, then you can simply call the length 3 version of drawImage(source, x, y), which will use by default the source's original size.
But you'll still need to resize your canvas to the correct size before doing it, and to do this, you need to access the image's original size.
Fortunately, this original size of the image, is available from the ImageElement's naturalWidth and naturalHeight properties.
document.getElementById("scream").onload = function() {
var c = document.createElement('canvas');
var imgData = document.getElementById("scream");
console.log('computed width: ', imgData.width);
console.log('original width: ', imgData.naturalWidth);
c.width = imgData.naturalWidth;
c.height = imgData.naturalHeight;
var body = document.body;
body.appendChild(c);
var ctx = c.getContext("2d");
// the 3 length version is enough here
ctx.drawImage(imgData, 0, 0);
};
<img id="scream" src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/86/Edvard_Munch_-_The_Scream_-_Google_Art_Project.jpg/190px-Edvard_Munch_-_The_Scream_-_Google_Art_Project.jpg" alt="The Scream" width="100%" height="100%">
After getting a Canvas element and its context
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
Let's say I drew 3 images on it.
context.drawImage(image,x,y,100,100);
context.drawImage(image,x+20,y+20,100,100);
context.drawImage(image,x+40,y+40,100,100); //image 3
How do I remove image 3 from the canvas keeping the other two? Won't clearRect remove anything in the drawing area?! If not, why?
Thanks a lot.
It does not a contain any separate element like we see in html when multiple element flow over another with different z-index. Its just like a state. So it is like an array of pixel data (they call it ImageData). You can capture a state.
Just a sample code:
<!DOCTYPE html>
<p>Image to use:</p>
<img id="scream" width="220" height="277" src="img_the_scream.jpg" alt="The Scream">
<table>
<tr>
<td>
<p>Canvas:</p>
<canvas id="myCanvas" width="240" height="297" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
</td>
<td>
<p>Extra Canvas:</p>
<canvas id="myCanvas2" width="240" height="297" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
</td>
</table>
<script>
window.onload = function() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = document.getElementById("scream");
ctx.drawImage(img, 10, 10);
ctx.drawImage(img, 110, 40);
var c2 = document.getElementById("myCanvas2"); // will copy the sate here
c2.getContext("2d").putImageData(ctx.getImageData(0, 0, 240, 297), 0, 0);
ctx.drawImage(img, 30, 60);
}
</script>
<p><strong>Note:</strong> The canvas tag is not supported in Internet Explorer 8 and earlier versions.</p>
I just modified the sample code of w3schools tryIt (http://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_canvas_drawimage).
You can use ctx.save() to keep a restorable state while making changes using drawImage(). And to restore to that very last saves state, you can use ctx.restore().
You can either simply clear and redraw the images you require, if you're doing this via a loop.
Or, you can simply draw over the image you no longer require.
E.g.
<img id="img1" src="GITS01.jpg" style="display:none;">
<img id="img2" src="Matrix01.jpg" style="display:none;">
<img id="img3" src="silent-hills2.jpg" style="display:none;">
<canvas id="canvas"></canvas>
<script>
var ctx = document.getElementById('canvas').getContext('2d');
ctx.canvas.width = window.innerWidth * 0.8;
ctx.canvas.height = window.innerHeight * 0.8;
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');
var img3 = document.getElementById('img3');
// Let's clear the canvas first.
ctx.fillStyle = '#101010';
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// Draw 3 images, top left, top right and bottom right quadrants.
ctx.drawImage(img1, 0, 0, ctx.canvas.width / 2, ctx.canvas.height / 2);
ctx.drawImage(img2, ctx.canvas.width / 2, 0, ctx.canvas.width / 2, ctx.canvas.height / 2);
ctx.drawImage(img3, 0, ctx.canvas.height / 2, ctx.canvas.width / 2, ctx.canvas.height / 2);
// Now, let's remove that second image in the top right quadrant,
// by simply drawing over it with a blank rectangle.
ctx.fillRect(ctx.canvas.width / 2, 0, ctx.canvas.width / 2, ctx.canvas.height / 2);
</script>
Depending on what else you need to draw, or if there's overlapping elements, then you will need to simply clear the canvas and redraw what you require. This is because the canvas draws in immediate mode. You either need to draw over what you just drew precisely, or you have to clear and start again, removing the draw operations for the things you don't need.
How do I remove image 3 from the canvas keeping the other two?
You need to clear the canvas for example using clearRect(), then redraw only those you want to keep. There are just unbound pixels on the canvas which you need to manually track.
One suggestion on how to do this is encapsulate the information about an image into a simple object:
var oImg1 = {
image: img1, // image object
visible: true, // state
x: 10, // handy
y: 10 // dandy
},
//... etc. for the other images (could use an array if there are many)
Then
if (oImg1.visible) ctx.drawImage(oImg1.image, oImg1.x, oImg1.y);
When you need to remove an image set visible to false, clear and redraw using conditions.
All:
I wonder if I draw a image on canvas, if the image ratio is not fit the canvas, how does canvas scale it( suppose only width and height set on canvas)?
For example:
<canvas id="myCanvas" style="width:200px; height:200px;"></canvas>
<script>
var canvas = d3.select("#myCanvas").node();
var context = canvas.getContext("2d");
var img = new Image();
img.onload = function(){
console.log(img.naturalWidth, img.naturalHeight);
context.drawImage(img, 0, 0);
}
img.src = "https://www.google.com/images/srpr/logo11w.png";
</script>
The result seems that the image has been stretch a little bit vertically and squeeze horizontally, I want to know why canvas deal it in that way and how to draw it as it is or scale in its ratio like ?
I also tried giving width and height when draw the image, but it still seems not quite right, for example:
// in this way, the image still get stretched and squeeze
<script>
var canvas = d3.select("#myCanvas").node();
var context = canvas.getContext("2d");
var img = new Image();
img.onload = function(){
console.log(img.naturalWidth, img.naturalHeight);
context.drawImage(img, 0, 0, img.naturalWidth/5, img.naturalHeight/5);
}
img.src = "https://www.google.com/images/srpr/logo11w.png";
</script>
OR
// Even I give it a 100 by 100 size, it can not do it that way
<script>
var canvas = d3.select("#myCanvas").node();
var context = canvas.getContext("2d");
var img = new Image();
img.onload = function(){
console.log(img.naturalWidth, img.naturalHeight);
context.drawImage(img, 0, 0, 100, 100);
}
img.src = "https://www.google.com/images/srpr/logo11w.png";
</script>
Thanks
The bitmap of the canvas is by default 300x150 independent of the CSS size.
<canvas id="myCanvas" style="width:200px; height:200px;"></canvas>
This will stretch anything drawn to the 300x150 bitmap to fit 200x200 CSS size.
Use canvas' attributes (or properties from JavaScript) to set the size properly:
<canvas id="myCanvas" width=200 height=200></canvas>
How do I use drawImage() to output full size images on a 300px X 380px canvas regardless of the source image size?
Example:
1). If there is a image of 75px X 95px I want to be able to draw it to fit a 300px X 380px canvas.
2). If there is a image of 1500px X 1900px I want to be able to draw it to fit a 300px X 380px canvas.
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById("myPic");
ctx.drawImage(img,10,10);
What options are available to prevent any quality loss?
To scale the image to fit is not so hard, just use simple aspect ratio with the sizes:
var ratioX = canvas.width / image.naturalWidth;
var ratioY = canvas.height / image.naturalHeight;
var ratio = Math.min(ratioX, ratioY);
ctx.drawImage(image, 0, 0, image.naturalWidth * ratio, image.naturalHeight * ratio);
To maintain quality; a canvas of 300x380 will either appear very tiny on print, or very blurry.
It's important to keep the data from it in the target resolution. To do this, calculate the size using the target DPI (or rather, PPI). You will also need to know in advance what size the 300x380 area represents (e.g. in either inches, centimeters, millimeters etc.).
For example:
If the target PDF will have a PPI of 300, and the canvas represents 3 x 3.8 cm (just to keep it simple), then the width and height in pixel will be:
var w = (3 / 2.54) * 300; // cm -> inch x PPI
var h = (3.8 / 2.54) * 300;
Use this size on canvas' bitmap, then scale down the element using CSS:
canvas.width = w|0; // actual bitmap size, |0 cuts fractions
canvas.height = h|0;
canvas.style.width = "300px"; // size in pixel for screen use
canvas.style.height = "380px";
You can now use the canvas directly as an image source for the PDF while keeping print quality for the PDF (have in mind though, small images uploaded will not provide high print quality in any case).
And of course, set the canvas size first, then use the code at top to draw in the image.
You'll need to resize your source image. You will need to calculate the destination size of the image, then use a couple extra parameters during your draw.
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = 188;
var y = 30;
var width = 200;
var height = 137;
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, x, y, width, height);
};
imageObj.src = 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
body {
margin: 0px;
padding: 0px;
}
<canvas id="myCanvas" width="578" height="200"></canvas>
Source: http://www.html5canvastutorials.com/tutorials/html5-canvas-image-size/