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%">
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 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.
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<canvas id="spriteCanvas" width="500" height="500">
<img id="coin" width="440" height="40" src="coin.png">
</canvas>
</body>
</html>
I tried placing an image inside a canvas element, but it won't display on the browser. I know the image tag works because it's displayed if I place it outside of the canvas element. Also, if I inspect the canvas element, I can see that the image is inside, but its dimensions are 0 by 0. Can somebody explain why this isn't working?
EDIT: My original code added the image through javascript, but it wouldn't show on the canvas. It was giving me the same problems as above. But I just realized I was missing "onload".
original code:
var coinImage = new Image();
coinImage.src = "coin.png";
var sCanvas = document.getElementById('spriteCanvas');
function Sprite(canvas, width, height, image){
this.context = canvas.getContext("2d");
this.width = width;
this.height = height;
this.image = image;
}
Sprite.prototype.render = function() {
var that = this;
this.context.drawImage(that.image, 0, 0);
}
function init() {
var coin = new Sprite(sCanvas, 100, 100, coinImage);
coin.render();
}
init();
editted code:
var coinImage = new Image();
coinImage.src = "coin.png";
var sCanvas = document.getElementById('spriteCanvas');
function Sprite(canvas, width, height, image){
this.context = canvas.getContext("2d");
this.width = width;
this.height = height;
this.image = image;
}
Sprite.prototype.render = function() {
var that = this;
this.context.drawImage(that.image, 0, 0);
}
coinImage.onload = function () {
var coin = new Sprite(sCanvas, 100, 100, coinImage);
coin.render();
}
This isn't how a <canvas> tag works. If you want your image to appear in your canvas, you will have to use JavaScript to place the pixels of the image into your canvas.
<canvas>s are exactly what they state: canvases. They are an element for you to draw on programmatically. If you just want to display an image on a page, you don't need a canvas, you just need the <img> tag. In fact, elements should not be placed in <canvas>.
Take a look at CanvasRenderingContext2D.drawImage() and this tutorial: HTML5 Canvas Image Tutorial.
And this snippet:
var canvas = document.getElementById("painting"),
ctx = canvas.getContext("2d"),
image = new Image();
image.onload = function() {
ctx.drawImage(image, 30, 50);
};
image.src = "http://cdn.sstatic.net/Sites/stackoverflow/img/sprites.png?v=10a9e8743fb0";
<canvas id="painting" width="300" height="300"></canvas>
To draw an image on a canvas, use the following method:
drawImage(image,x,y)
If the image you want to draw is not in the DOM already you can load an image directly from a URL with a few lines of javascript.
function loadAndDrawImage(url)
{
// Create an image object. This is not attached to the DOM and is not part of the page.
var image = new Image();
// When the image has loaded, draw it to the canvas
image.onload = function()
{
// draw image...
}
// Now set the source of the image that we want to load
image.src = url;
}
loadAndDrawImage("http://www---.png");
So I am creating a cordova app where I take a photo from the iphone library, draw it to canvas and add another image to it in order to save it as one photo. So far the photo I draw from the iphone photo library gets drawn without problems to the canvas, however the second image doesn't.
When I load the second image, it first gets added to a div with absolute positioning in order to move it to wherever I want. After that I get the actual image it's source and it's positions and try to draw it to canvas. The drawing of the second image happens when I call a method that also performs the canvas2ImagePlugin it's functions. In the end only the first photo without the second image gets saved.
The draw image to canvas function:
function drawImage(image_source, dx, dy)
{
var canvas = document.getElementById('Photo');
var image = new Image();
image.src = image_source;
image.onload = function() {
c=canvas.getContext("2d");
c.canvas.width = window.innerWidth;
c.canvas.height = window.innerHeight;
c.drawImage(image,dx,dy, window.innerWidth, window.innerHeight);
}
}
The method for drawing the second image and saving it:
function saveImage()
{
var img = $('.ObjectImage').attr('src', $('img:first').attr('src'));
var imagePosition = $('.ObjectImage').find('img:first').position();
drawImage(img, imgPosition.left, imgPosition.top);
window.canvas2ImagePlugin.saveImageDataToLibrary(
function(msg){
console.log(msg);
},
function(err){
console.log(err);
},
document.getElementById('Photo')
);
alert("Image saved");
}
The window.innerWidth, window.innerHeight on the canvas is done to get the canvas in full screen of the parent div.
EDIT to the comment:
function drawImage(image_source, dx, dy)
{
var canvas = document.getElementById('Photo');
var image = new Image();
image.onload = function() {
c=canvas.getContext("2d");
c.canvas.width = window.innerWidth;
c.canvas.height = window.innerHeight;
c.drawImage(image,dx,dy, window.innerWidth, window.innerHeight);
}
image.src = image_source;
}
Still not working
The drawImage function works asynchronously, it starts loading an image and exits immediately. Then, when the image loads, the canvas is updated. If you run something like:
drawImage('test.jpg',0,0);
drawImage('test2.jpg',0,0);
you will get both images updating the canvas at approximately the same time and only one will appear.
Also, what wolfhammer said is correct. If you set the size of the canvas, you clear it, so drawing one image after the other, even if they are smaller sizes and should both appear, will only show the last one. Check this link on how to solve it: Preventing Canvas Clear when Resizing Window
Further more, you are drawing all images with the width and height of the window, which doesn't make sense. Probably you want to use the width and height of the image (so this.width instead of window.innerWidth)
When you set the width and height of the canvas the data on "Photo" is cleared. I've provide a resize function if resizing is really needed.
function drawImage(image_source, dx, dy)
{
var canvas = document.getElementById('Photo');
var image = new Image();
image.src = image_source;
image.onload = function() {
c=canvas.getContext("2d");
//c.canvas.width = window.innerWidth;
//c.canvas.height = window.innerHeight;
c.drawImage(image,dx,dy, window.innerWidth, window.innerHeight);
}
}
var can = document.getElementById('can');
var ctx = can.getContext('2d');
ctx.fillStyle = "red";
ctx.beginPath();
ctx.moveTo(20, 90);
ctx.lineTo(50, 10);
ctx.lineTo(80, 90);
ctx.lineTo(10, 40);
ctx.lineTo(90, 40);
ctx.lineTo(20, 90);
ctx.fill();
var btn = document.getElementById('btnResize');
btn.addEventListener('click', function() {
resize(can, can.width * 2, can.height * 2);
});
function resize(can, w, h) {
var ctx = can.getContext('2d');
// copy
var can2 = document.createElement('canvas');
var ctx2 = can2.getContext('2d');
can2.width = can.width;
can2.height = can.height;
ctx2.drawImage(can, 0, 0);
// resize
can.width = w;
can.height = h;
ctx.drawImage(can2, 0, 0, can2.width, can2.height, 0, 0, w, h);
}
#can {
border:1px solid red;
}
<button id='btnResize'>Size x 2</button><br/>
<canvas id="can" width="100" height="100"></canvas>
On load of my image I:
var img = new Image();
img.src = e.target.result;
var canvas = $('#test-canvas')[0];
$('#test-canvas').width(img.width);
$('#test-canvas').height(img.height);
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
But the image is drawn larger than it's original size? I've tried a few images, same problem.
What's the fix?
jQuery will resize the element through CSS, which won't actually change the canvas internal height and width. This will resize the actual canvas element.
var canvas = document.GetElementById('test-canvas');
canvas.width = img.width;
canvas.height = img.height;
jsFiddle (http://jsfiddle.net/L0drfwgL/) just show it drawing it to scale, and resizing the canvas item itself.
It looks like you are pulling the image from an image element on load. You can use the src from the image element rather than recreating an image object and then get the image width/height from the element to draw the image to canvas.
<script>
$(document).ready(function(){
$('#testImg').on('load',function(){
var image = document.getElementById('testImg');
var canvas = document.getElementById('test-canvas');
canvas.width = image.width;
canvas.height = image.height;
var ctx=canvas.getContext("2d");
ctx.drawImage(image,0,0);
})
});
</script>
/** with vue3 -(to fix drawing canvas image is larger than actual image size **/
const captureImage = () => {
let width = video.value.getBoundingClientRect().width;
let height = video.value.getBoundingClientRect().height;
let context = canvas.value.getContext('2d')
canvas.value.width = width
canvas.value.height = height
context.drawImage(video.value, 0, 0, width, height)
}