I'm trying to scale an image proportionately to the canvas. I'm able to scale it with fixed width and height as so:
context.drawImage(imageObj, 0, 0, 100, 100)
But I only want to resize the width and have the height resize proportionately. Something like the following:
context.drawImage(imageObj, 0, 0, 100, auto)
I've looked everywhere I can think of and haven't seen if this is possible.
context.drawImage(imageObj, 0, 0, 100, 100 * imageObj.height / imageObj.width)
#TechMaze's solution is quite good.
Here is the code after some corrections and the introduction of the image.onload event. image.onload is essential in order to avoid any kind of distortion.
function draw_canvas_image() {
var canvas = document.getElementById("image-holder-canvas");
var context = canvas.getContext("2d");
var imageObj = document.getElementById("myImageToDisplayOnCanvas");
imageObj.onload = function() {
var imgWidth = imageObj.naturalWidth;
var screenWidth = canvas.width;
var scaleX = 1;
if (imgWidth > screenWidth)
scaleX = screenWidth/imgWidth;
var imgHeight = imageObj.naturalHeight;
var screenHeight = canvas.height;
var scaleY = 1;
if (imgHeight > screenHeight)
scaleY = screenHeight/imgHeight;
var scale = scaleY;
if(scaleX < scaleY)
scale = scaleX;
if(scale < 1){
imgHeight = imgHeight*scale;
imgWidth = imgWidth*scale;
}
canvas.height = imgHeight;
canvas.width = imgWidth;
context.drawImage(imageObj, 0, 0, imageObj.naturalWidth, imageObj.naturalHeight, 0,0, imgWidth, imgHeight);
}
}
And if you want to properly scale the picture as per the screen size, here is the code you can use:
var imgWidth = imageObj.naturalWidth;
var screenWidth = $(window).width() - 20;
var scaleX = 1;
if (imageWdith > screenWdith)
scaleX = screenWidth/imgWidth;
var imgHeight = imageObj.naturalHeight;
var screenHeight = $(window).height() - canvas.offsetTop-10;
var scaleY = 1;
if (imgHeight > screenHeight)
scaleY = screenHeight/imgHeight;
var scale = scaleY;
if(scaleX < scaleY)
scale = scaleX;
if(scale < 1){
imgHeight = imgHeight*scale;
imgWidth = imgWidth*scale;
}
canvas.height = imgHeight;
canvas.width = imgWidth;
ctx.drawImage(imageObj, 0, 0, imageObj.naturalWidth, imageObj.naturalHeight, 0,0, imgWidth, imgHeight);
If you are not using jQuery, replace $(window).width with the appropriate equivalent option.
Related
I'm trying to make a section on a website have two background images that will reveal the bottom one as the pointer moves across the screen.
I'm still new to javascript, and my code is made up of bits and pieces that I've found on google, but I can't seem to get the top image to share the same resolution and image size for whatever reason as the bottom image.
Here is the link to my codepen: https://codepen.io/Awktopus/pen/zYwKOKO
And here is my code:
HTML:
<canvas id="main-canvas" id="canvas-size" class="background-size"></canvas>
<image src="https://i.imgur.com/PbGAAIy.jpg" id="upper-image" class="hidden-bg"></img>
<image src="https://i.imgur.com/Gx14sKW.jpg" id="lower-image" class="hidden-bg"></img>
CSS:
.hidden-bg {
display: none;
}
JS:
var can = document.getElementById('main-canvas');
var ctx = can.getContext('2d');
can.width = window.innerWidth;
can.height = window.innerWidth / 2;
var upperImg = document.getElementById("upper-image");
var lowerImg = document.getElementById("lower-image");
var pat = ctx.createPattern(upperImg, "no-repeat");
var canvas = ctx.canvas ;
var hRatio = canvas.width / lowerImg.width ;
var vRatio = canvas.height / lowerImg.height ;
var ratio = Math.max ( hRatio, vRatio );
var centerShift_x = ( canvas.width - lowerImg.width*ratio ) / 2;
var centerShift_y = ( canvas.height - lowerImg.height*ratio ) / 2;
can.addEventListener('mousemove', function(e) {
var mouse = getMouse(e, can);
redraw(mouse);
}, false);
function redraw(mouse) {
can.width = can.width;
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.drawImage(lowerImg, 0,0, lowerImg.width, lowerImg.height, centerShift_x,centerShift_y,lowerImg.width*ratio, lowerImg.height*ratio);
ctx.beginPath();
ctx.rect(0,0,can.width,can.height);
ctx.arc(mouse.x, mouse.y, 250, 0, Math.PI*2, true)
ctx.clip();
ctx.fillStyle = pat;
ctx.fillRect(0, 0, lowerImg.width, lowerImg.height, centerShift_x, centerShift_y, lowerImg.width*ratio, lowerImg.height*ratio);
}
var img = new Image();
img.onload = function() {
redraw({x: -500, y:-500})
}
function getMouse(e, canvas) {
var element = canvas,
offsetX = 0,
offsetY = 0,
mx, my;
if (element.offsetParent !== undefined) {
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
} while ((element = element.offsetParent));
}
mx = e.pageX - offsetX;
my = e.pageY - offsetY;
return {
x: mx,
y: my
};
}
If I understood this right you will want to set the width and height and draw the upper image using drawImage(). Just use the same ratios as the lowerImage. No need to use createPattern for this.
codepen: https://codepen.io/jfirestorm44/pen/BaRLoxX
var can = document.getElementById('main-canvas');
var ctx = can.getContext('2d');
can.width = window.innerWidth;
can.height = window.innerWidth / 2;
var upperImg = document.getElementById("upper-image");
var lowerImg = document.getElementById("lower-image");
//var pat = ctx.createPattern(upperImg, "no-repeat");
var canvas = ctx.canvas ;
var hRatio = canvas.width / lowerImg.width ;
var vRatio = canvas.height / lowerImg.height ;
var ratio = Math.max ( hRatio, vRatio );
var centerShift_x = ( canvas.width - lowerImg.width*ratio ) / 2;
var centerShift_y = ( canvas.height - lowerImg.height*ratio ) / 2;
can.addEventListener('mousemove', function(e) {
var mouse = getMouse(e, can);
redraw(mouse);
}, false);
function redraw(mouse) {
can.width = can.width;
ctx.clearRect(0,0,canvas.width, canvas.height);
ctx.drawImage(lowerImg, 0,0, lowerImg.width, lowerImg.height, centerShift_x,centerShift_y,lowerImg.width*ratio, lowerImg.height*ratio);
ctx.beginPath();
ctx.rect(0,0,can.width,can.height);
ctx.arc(mouse.x, mouse.y, 250, 0, Math.PI*2, true)
ctx.clip();
ctx.drawImage(upperImg, 0,0, lowerImg.width, lowerImg.height, centerShift_x,centerShift_y,lowerImg.width*ratio, lowerImg.height*ratio);
}
var img = new Image();
img.onload = function() {
redraw({x: -500, y:-500})
}
function getMouse(e, canvas) {
var element = canvas,
offsetX = 0,
offsetY = 0,
mx, my;
if (element.offsetParent !== undefined) {
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
} while ((element = element.offsetParent));
}
mx = e.pageX - offsetX;
my = e.pageY - offsetY;
return {
x: mx,
y: my
};
}
.hidden-bg {
display: none;
}
<canvas id="main-canvas" id="canvas-size" class="background-size"></canvas>
<image src="https://i.imgur.com/PbGAAIy.jpg" id="upper-image" class="hidden-bg"></img>
<image src="https://i.imgur.com/Gx14sKW.jpg" id="lower-image" class="hidden-bg"></img>
So I was playing around the canvas and I tried rotating images loaded from my device hard disk as seen below:
class imageEdits {
constructor(canvas, imageSrc, canvasWidth, canvasHeight) {
this.canvas = canvas;
this.ctx = this.canvas.getContext("2d");
this.image = new Image();
this.image.src = imageSrc;
this.cWidth = canvasWidth;
this.cHeight = canvasHeight;
}
rotateImage = deg => {
this.ctx.save();
function degToRad(deg) {
return (1 / 57.3) * deg;
}
let imageHeight = this.image.naturalHeight,
imageWidth = this.image.naturalWidth;
if (deg !== 0 && deg !== 180) {
imageHeight = this.image.naturalWidth;
imageWidth = this.image.naturalHeight;
} else {
(imageHeight = this.image.naturalHeight),
(imageWidth = this.image.naturalWidth);
}
const {
canvasStyle: { height, width, scale, aspectRatio },
} = this.computeAspectRatio(imageHeight, imageWidth);
console.log({ height, width, scale, aspectRatio });
const halfWidth = width / 2;
const halfHeight = height / 2;
this.ctx.translate(halfWidth, halfHeight);
this.ctx.rotate(degToRad(deg));
this.canvas.style.transform = `scale3d(${scale}, ${scale}, 1)`;
this.canvas.style.backgroundColor = "rgb(0,0,0)";
this.canvas.style.transformOrigin = `top left`;
console.log({ width, height });
this.ctx.drawImage(this.image, -halfWidth, -halfHeight, width, height);
this.ctx.restore();
};
computeAspectRatio = (imageHeight, imageWidth) => {
const height = imageHeight;
const width = imageWidth;
(scale = 1), (canvasStyle = {});
this.canvas.width = width;
this.canvas.height = height;
const scaleX = this.cWidth / width;
const scaleY = this.cHeight / height;
if (scaleX > scaleY) scale = scaleY;
else scale = scaleX;
canvasStyle = { height, width, scale };
return { canvasStyle };
};
}
The problem with the code is that when I rotate the image to the inverse of its aspect ratio which is 90degree of 180degree the image gets centered and doesn't take the full width or height as the case may be of the canvas.
here is a jsfiddle of the working code
And this is what my expected output should look like
But instead this is what I get
Please does anyone see what I am doing wrong? Thanks in advance :)
In general a rotation around the center is done by translating the context to the mid-point of the canvas, rotating the context and finally drawing the image at the negative half of it's width horizontally and negative half of it's height vertically.
What makes things a bit harder in your case is that the image should always fill the entire canvas, while maintaining it's correct aspect ratio. To do this we would need to know the exact width & height of the image - or more precisely it's bounding box - at a given angle. Luckily we just have to deal with four angles, so it's just a matter of swapping the width & height at 90° and 270° - as you already did.
Now that we know the image's dimensions we need to compute the scale along both axes and see which one of those doesn't exceed the canvas width & height after multiplication.
This scale is then used to scale the context - not the css scale you used to size the canvas itself.
Here's an example based on your code (just click on 'Run code snippet'):
const canvas = document.getElementById("edit-canvas");
const ctx = canvas.getContext("2d");
const canvasWidth = 320;
const canvasHeight = 200;
let deg = 0;
let image;
canvas.width = canvasWidth;
canvas.height = canvasHeight;
function degToRad(deg) {
return (1 / 57.3) * deg;
}
function draw() {
let scale, imageHeight, imageWidth, scaleX, scaleY;
if (deg != 0 && deg != 180) {
imageHeight = image.width;
imageWidth = image.height;
} else {
imageHeight = image.height;
imageWidth = image.width;
}
scaleX = canvasWidth / imageWidth;
scaleY = canvasHeight / imageHeight;
if (imageWidth * scaleX <= canvasWidth && imageHeight * scaleX <= canvasHeight) {
scale = scaleX;
} else {
scale = scaleY;
}
ctx.save();
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
ctx.translate(canvasWidth / 2, canvasHeight / 2);
ctx.rotate(degToRad(deg));
ctx.scale(scale, scale);
ctx.drawImage(image, -image.width / 2, -image.height / 2, image.width, image.height);
ctx.restore();
}
image = new Image();
image.onload = draw;
image.src = "https://picsum.photos/id/1079/300/200";
document.getElementById("rotate").addEventListener("click", () => {
deg += 90;
if (deg == 360) deg = 0;
draw();
});
<div class="canvas-container">
<input type="button" id="rotate" style="padding: 10px; font-size: 16px; position: absolute" value="Rotate" />
<canvas id="edit-canvas" style="border: 1px solid #000; margin-left: 10px;background-color: #c1f0c1;"></canvas>
</div>
I'm scaling the image down so it fits inside the canvas, the thing I am struggling to do is to then center it inside of the canavas element, does anyone know how this could be done please? Any help would be appreciated
https://jsfiddle.net/n7xL5c37/
var canvas = document.getElementById('canvas');
var image = new Image();
image.src = 'http://placehold.it/300x550';
image.onload = function () {
var canvasContext = canvas.getContext('2d');
var wrh = image.width / image.height;
var newWidth = canvas.width;
var newHeight = newWidth / wrh;
if (newHeight > canvas.height) {
newHeight = canvas.height;
newWidth = newHeight * wrh;
}
canvasContext.drawImage(image,0,0, newWidth , newHeight);
};
var canvas = document.getElementById('canvas');
var image = new Image();
image.src = 'http://placehold.it/300x550';
image.onload = function () {
var canvasContext = canvas.getContext('2d');
var wrh = image.width / image.height;
var newWidth = canvas.width;
var newHeight = newWidth / wrh;
if (newHeight > canvas.height) {
newHeight = canvas.height;
newWidth = newHeight * wrh;
}
var xOffset = newWidth < canvas.width ? ((canvas.width - newWidth) / 2) : 0;
var yOffset = newHeight < canvas.height ? ((canvas.height - newHeight) / 2) : 0;
canvasContext.drawImage(image, xOffset, yOffset, newWidth, newHeight);
};
<canvas id="canvas" width="500" height="500" style="border: 1px solid black" />
A solution that will work for both horizontal and vertical.
First find the scale which is the min scale to fit the width or height
var scale = Math.min(canvas.width / img.width, canvas.height / img.height);
Use that scale to get the img width and height
var w = img.width * scale;
var h = img.height * scale;
Then use that scale to calculate the top left as half the dist from the center
var left = canvas.width / 2 - w / 2;
var top = canvas.height / 2 - h / 2;
Solution of Passersby works fine if you want something similar to object-fit: contain
This solution works fine if you want object-fit: cover
const fitImageToCanvas = (image,canvas) => {
const canvasContext = canvas.getContext("2d");
const ratio = image.width / image.height;
let newWidth = canvas.width;
let newHeight = newWidth / ratio;
if (newHeight < canvas.height) {
newHeight = canvas.height;
newWidth = newHeight * ratio;
}
const xOffset = newWidth > canvas.width ? (canvas.width - newWidth) / 2 : 0;
const yOffset =
newHeight > canvas.height ? (canvas.height - newHeight) / 2 : 0;
canvasContext.drawImage(image, xOffset, yOffset, newWidth, newHeight);
};
You can get the element like:
var ctx = document.getElementById('digitalrain').getContext('2d');
and set x and y of your image like:
ctx.drawImage(img, width/2-img.width/2, height/2-img.height/2);
I am trying to make a canvas application and i get success. I just stuck in print image that is output of canvas. Size of image is too small to print enough.
Following is flow of application:
Upload image and draw to the canvas.
Do some operations like zoom, move and rotating on split canvas.
I have successfully generated single images from those splits
And I have export base64 from canvas to printing process.
Problem is here when I export data from canvas it's small image, even if I do some Imagick stuff to resize image quality of final image is not near to print on poster.
JavaScript:
function draw() {
var Activecanvas = document.getElementsByClassName("cf-photoframe-active");
var canvas = Activecanvas[0];
var ctx = canvas.getContext('2d');
var ActiveCanvasId = canvas.getAttribute("id");
var img = canvasDetail[ActiveCanvasId].selectedImage;
var imageX = canvasDetail[ActiveCanvasId].imageX;
var imageY = canvasDetail[ActiveCanvasId].imageY;
var resize = resizeDetail[ActiveCanvasId].resize;
var rotate = rotateDetail[ActiveCanvasId].rotate;
//if(resize)
$j("#resize").slider({
value: resize,
min: -300,
max: 300,
step: 1,
});
$j("#rotat").slider({
value: rotate,
min: -180,
max: 180,
step: 1,
});
if (img != '') {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
var im_width = parseInt(img.width + $j("#resize").slider('value'));
var im_height = parseInt(img.height + $j("#resize").slider('value'));
var maxWidth = canvas.width; // Max width for the image
var maxHeight = canvas.height;
var iswidthMax = 0;
var imageOrgWidth = img.width;
var imageOrgHeight = img.height;
if (maxWidth < maxHeight) {
iswidthMax = 1;
} else if (maxWidth == maxHeight) {
iswidthMax = 2;
}
if (iswidthMax == 1) {
maxWidth = (maxHeight * imageOrgWidth) / imageOrgHeight;
} else if (iswidthMax == 0) {
maxHeight = (maxWidth * imageOrgHeight) / imageOrgWidth;
} else if (iswidthMax == 2) {
if (img.width > img.height) {
maxWidth = (maxHeight * imageOrgWidth) / imageOrgHeight;
} else {
maxHeight = (maxWidth * imageOrgHeight) / imageOrgWidth;
}
}
var resizeVal = $j("#resize").slider('value');
var nw = maxWidth + resizeVal;
var resizeValHeight = maxHeight * resizeVal / maxWidth;
var nh = maxHeight + resizeValHeight;
ctx.translate(imageX, imageY);
ctx.rotate(rotate * Math.PI / 180);
ctx.translate(-nw / 2, -nh / 2);
ctx.drawImage(img, 0, 0, nw, nh);
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.restore();
}
}
If you have smaller canvas elements for editing, then you will have to recombine the data from those smaller canvas elements into a canvas element which is the full size and output the data from that element.
For simplicity's sake, let's say that you have cut the image into four quadrants and drawn to four canvas elements canvas[0] - canvas[3] for editing. You would then need to recombine them into one larger canvas doing something like the following...
var imgData = [];
for (var i = 0; i < 4; i++) {
var tmpCtx = canvas[i].getContext('2d');
imgData[i] = tmpCtx.getImageData(0, 0, canvas[i].width, canvas[i].height);
}
var fullSizeCanvas = document.createElement('canvas');
fullSizeCanvas.width = img.width * 2;
fullSizeCanvas.height = img.height * 2;
var fullCtx = fullSizeCanvas.getContext('2d');
fullCtx.putImageData(imgData[0], 0, 0);
fullCtx.putImageData(imgData[1], img.width, 0);
fullCtx.putImageData(imgData[2], 0, img.height);
fullCtx.putImageData(imgData[3], img.width, img.height);
var data = fullSizeCanvas.toDataURL();
I'm trying to get an HTML5 video to fill the parent container width while keeping the same height with the responsive design. I'm just hiding any overflow. I found JavaScript on stackoverflow that works perfectly sometimes and other times doesn't work correctly at all. Any reason why if I refresh the page I'd get different results?
var sxsw = {
full_bleed: function(boxWidth, boxHeight, imgWidth, imgHeight) {
// Calculate new height and width…
var initW = imgWidth;
var initH = imgHeight;
var ratio = initH / initW;
imgWidth = boxWidth;
imgHeight = boxWidth * ratio;
// If the video is not the right height, then make it so…
if (imgHeight < boxHeight) {
imgHeight = boxHeight;
imgWidth = imgHeight / ratio;
}
// Return new size for video
return {
width: imgWidth,
height: imgHeight
};
},
init: function() {
var browserHeight = Math.round(jQuery('.vid-container').height());
var browserWidth = Math.round(jQuery('vid-container').width());
var videoHeight = $('#fullscreen_video').height();
var videoWidth = $('#fullscreen_video').width();
var new_size = sxsw.full_bleed(browserWidth, browserHeight, videoWidth, videoHeight);
$('#fullscreen_video').width(new_size.width).height(new_size.height);
}
};
jQuery(document).ready(function($) {
sxsw.init();
$(window).resize(function() {
var browserHeight = Math.round($(window).height());
var browserWidth = Math.round($(window).width());
var videoHeight = $('#fullscreen_video').height();
var videoWidth = $('#fullscreen_video').width();
var new_size = sxsw.full_bleed(browserWidth, browserHeight, videoWidth, videoHeight);
$('#fullscreen_video').width(new_size.width).height(new_size.height);
});
})