page 1:
//Calls the function from page 2 and the callback image is set as the source of the control.
previewImage(current, function(img) {
jQuery(".mapItem").attr("src",img.src);
});
page 2:
//The functions callback returns an image which we use in page 1 (above)
var canvas = document.createElement('canvas');
context = canvas.getContext('2d');
context.drawImage(this.m_Images[i],0,0,canvas.width,canvas.height);
var t = new Image();
t.src = canvas.toDataURL();
callback(t);
The issue:
I have 2 JavaScript pages, the first one has an image control and the second one has a function that returns a callback as an image.
My control in page 1 (.mapItem) has a height and width of 75.2px (fixed). The image that is coming from the callback however will have a different size each time e.g one day it can be 200px * 300px and one day it can be 150px * 200px etc
How can I clip or CUT the image of the callback? I want the image (t) to zero (0) as x and y starting points and then clip the image where ever the .mapItem control height and width is.
I need this to be proportional ratio. So I can't just add the following code:
context.drawImage(this.m_Images[i],0,0,72.5,72.5); because this will ruin the image as we dont even know if it is square shaped.
Thanks in advance :)
You can determine the proportions of callback image and then apply them to the page 1 image thus:
Let's assume that the callback image is 300x200px. The ratio of the image's height-to-width can be expressed as...
var ratio = callbackImage.height / callbackImage.width;
...or, in this case...
var ratio = 200 / 300; // 0.666...
We know the width of the page 1 canvas is 72.5 so we can apply the ratio to that value to determine the proportional height of the callback Image like so...
var canvasWidthHeight = 72.5;
var imageHeight = canvasWidthHeight * ratio; // 48.333...
To center the callback Image on the page 1 canvas calculate the y offset like so..
var y = (canvasWidthHeight - imageHeight) / 2;
...and now you can use the canvas drawImage method with these values...
context.drawImage(
this.m_Images[i],
0, y,
canvasWidthHeight, imageHeight
);
If the callback image was higher than it was wide you'd apply the ratio the page 1 canvas dimensions to work out the x offset rather than the y offest. If the callback image was square then its ratio would be 1.0 and you could simply paint it into the square page 1 canvas at
context.drawImage(
this.m_Images[i],
0, 0,
canvasWidthHeight, canvasWidthHeight
);
All together the code might look something like this...
var image = this.m_Images[i];
var canvas = {
width: 72.5,
height: 72.5
};
var wh = 0;
var ratio = image.height / image.width;
if (image.width > image.height) { // landscape
wh = canvas.width * ratio;
context.drawImage(
image,
0, (canvas.height - wh) / 2,
canvas.width, wh
);
} else if (image.width < image.height) { // portrait
ratio = image.width / image.height;
wh = canvas.height * ratio;
context.drawImage(
image,
(canvas.width - wh) / 2, 0,
wh, canvas.height
);
} else { // square
context.drawImage(
image,
0, 0,
canvas.width, canvas.height
);
}
Hope that helps. ;)
EDIT: You may need to ensure that the new Image() has fully loaded before initiating the callback. You can do this with the following snippet...
// callback preparation code as before...
var t = new Image();
t.addEventListener('load', function() {
callback(this);
});
t.src = canvas.toDataURL();
Related
I am trying to put image data 100x100 to canvas 1000x1000 , but cant able to do it ,
let width=1000; //canvas width
let height=1000; //canvas height
let img_w=100; //image width
let img_h=100; //image height
let img=new Image();
img.width=img_w
img.height=img_h
img.src="./flower.jpg"
var canvas = document.getElementById('mycanvas');
var context = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
let pixels,scannedimg;
img.onload=()=>{
context.drawImage(img, 0, 0,width,height );
scannedimg = context.getImageData(0, 0, img.width, img.height);
pixels=scannedimg.data
console.log(pixels)
redraw();
}
let row=4*img_w;
let col=img_h;
function redraw(){
for(let i=0;i<row;i+=4){
for(let j=0;j<col;j++){
pixels[i+j*row]=0;
pixels[i+j*row+1]=0;
pixels[i+j*row+2]=0;
//pixels[i+j*400+3]=0;
}
}
scannedimg.data=pixels;
console.log(scannedimg);
context.putImageData(scannedimg,0,0,0,0,width,height);
}
i have converted the original array into a black image array (array of zeros) , but while putting on canvas , it is still 100x100
How to scale it to 1000x1000?
i don't want to iterate through 1000x1000 and set it to zero ,
i need a computationally efficient answer
Unless you outsource the pixel calculations to a WebAssembly module a JavaScript-only approach would indeed be rather slow for a large image.
Honestly I'm not sure what you are actually doing in your code.
First your drawing an unknown-sized .jpg to a 1000x1000 canvas which - unless the .jpg is also 1000x1000 - will scale and eventually distort the source image.
let width=1000;
let height=1000;
context.drawImage(img, 0, 0, width, height);
Secondly you're obtaining the pixel data of a 100x100 region from the top-left of your 1000x1000 canvas.
let img_w=100;
let img_h=100;
img.width=img_w;
img.height=img_h;
scannedimg = context.getImageData(0, 0, img.width, img.height);
Finally in your redraw() function you're rather randomly setting some of the pixels to black and draw it back to the canvas at 1000x1000 (which doesn't work that way but I will get into it later).
Let's do it a little different. Say we have a 300x200 image. First we need to draw it to a 100x100 canvas while maintaining it's aspect ratio to get the 100x100 imagedata.
This can be done using a dynamically created off-screen <canvas> element as we don't need to see it.
Now the tricky part is the CanvasRenderingContext2D putImageData() method. I assume you were thinking that the last pair of parameters for the width & height would stretch existing pixel data to fill the region specifid by (x, y, width, height). Well that's not the case. Instead we need to - again - paint the 100x100 pixel data to a same-sized off-screen canvas (or for simlicity re-use the existing) and draw it to the final canvas using the drawImage() method.
Here's everything put together:
let pixelsWidth = 100;
let pixelsHeight = 100;
let finalWidth = 500;
let finalHeight = 500;
let tempCanvas = document.createElement('canvas');
let tempContext = tempCanvas.getContext('2d');
tempCanvas.width = pixelsWidth;
tempCanvas.height = pixelsHeight;
let pixelData;
let img = new Image();
img.crossOrigin = 'anonymous';
img.onload = (e) => {
let scale = e.target.naturalWidth >= e.target.naturalHeight ? pixelsWidth / e.target.naturalWidth : pixelsHeight / e.target.naturalHeight;
let tempWidth = e.target.naturalWidth * scale;
let tempHeight = e.target.naturalHeight * scale;
tempContext.drawImage(e.target, pixelsWidth / 2 - tempWidth / 2, pixelsHeight / 2 - tempHeight / 2, tempWidth, tempHeight);
pixelData = tempContext.getImageData(0, 0, pixelsWidth, pixelsHeight);
redraw();
}
img.src = 'https://picsum.photos/id/237/300/200';
function redraw() {
let canvas = document.getElementById('canvas');
let context = canvas.getContext('2d');
canvas.width = finalWidth;
canvas.height = finalHeight;
tempContext.putImageData(pixelData, 0, 0);
context.drawImage(tempCanvas, 0, 0, finalWidth, finalHeight);
}
canvas {
background: #cccccc;
}
<canvas id="canvas"></canvas>
I need to wrap an image around another image of a mug using javascript, and I found this:
Wrap an image around a cylindrical object in HTML5 / JavaScript
This helps when loading the image that has the mug handle on the left. However when using the same function (with tweaked position values) the image has an opacity applied to it. I searched endlessly to figure out for what reason this is happening however I found nothing :/
This is the function used to wrap the image for the mug with the right handle:
function canvas2() {
var canvas = document.getElementById('canvas2');
var ctx = canvas.getContext('2d');
var productImg = new Image();
productImg.onload = function() {
var iw = productImg.width;
var ih = productImg.height;
canvas.width = iw;
canvas.height = ih;
ctx.drawImage(
productImg,
0,
0,
productImg.width,
productImg.height,
0,
0,
iw,
ih
);
loadUpperIMage();
};
productImg.src =
'https://i.ibb.co/B2G8y1m/white-right-ear.jpg';
function loadUpperIMage() {
var img = new Image();
img.src =
'https://i.ibb.co/BnQP0TL/my-mug-image.png';
img.onload = function() {
var iw = img.width;
var ih = img.height;
var xOffset = 48, //left padding
yOffset = 68; //top padding
var a = 70; //image width
var b = 8; //round ness
var scaleFactor = iw / (6 * a);
// draw vertical slices
for (var X = 0; X < iw; X += 1) {
var y = (b / a) * Math.sqrt(a * a - (X - a) * (X - a)); // ellipsis equation
if (!isNaN(y)) {
ctx.drawImage(
img,
X * scaleFactor,
0,
iw / 0.78,
ih,
X + xOffset,
y + yOffset,
1,
162
);
}
}
};
}
}
Hope someone can help with this!
Here is a fiddle with the issue https://jsfiddle.net/L20aj5xr/
It is because of the 4th argument you pass to drawImage - iw / 0.78. By multiplying image width by a value lower than one, you get the value larger than image width. The spec for drawImage says:
When the source rectangle is outside the source image, the source rectangle must be clipped to the source image and the destination rectangle must be clipped in the same proportion.
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
Because the source width (sw) you are using is larger than source image size, the destination rectangle "is clipped in the same proportion". The destination rectangle width is 1px because you chose it as a width for each vertical line you are drawing, and after clipping it's width becomes 1 * 0.78 = 0.78px. The width is now less than 1px and to be honest I am not exactly sure how it actually works under the hood, but my guess is that a browser still needs to draw that 1px, but because the source is 0.78px, it kinda stretches the source to that 1px and adds some anti-aliasing to smooth the transition, which results into added transparency (i.e. browser does not have enough information for that 1px and it tries to fill it up the best it can). You can play around with that by incresing sw even more and observe increasing transparency.
To fix your issue I used the value 20 instead of 0.78 like for the first cup and it seemed to look ok.
Let me start by saying that I am not a professional developer, but I am managing a development project that has a specific need -- to scale an image placed on an HTML5 canvas from the center point using a slider. The developer has implemented a slider that scales the image from the top-left point, but usability dictates resizing from the center and he has not been able to figure it out. Is this even possible? Please see here for a beta version of the app pre-loaded with an image to test:
http://beta.drumart.com/head-builder?lib-image-url=https://thumbs.dreamstime.com/800thumbs/12617/126170273.jpg
Click the image to select it and use the "Size" slider to resize. I am just looking to find out if this is possible and, if so, point him in the right direction. Many thanks in advance!
Image slider screenshot
I reccommend creating a drawCenteredImage() function, give it an image, x, y, width, height and it will draw the image centered on the given x, y. Thus when you increase x and y it will scale about the center of the image.
function drawCenteredImage(img, x, y, width, height) {
// Assuming globally accessible canvas context variable 'context'
context.drawImage(img, x - width / 2, y - height / 2, width, height);
}
An example of this in use:
% Updated to scale in realtime %
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
let image = new Image();
image.onload = function() {
drawImg();
};
image.src = "https://thumbs.dreamstime.com/800thumbs/12617/126170273.jpg";
function drawCenteredImage(img, x, y, width, height) {
// Assuming globally accessible canvas context variable 'context'
context.drawImage(img, x - width / 2, y - height / 2, width, height);
}
function drawImg() {
context.fillStyle = "white";
context.fillRect(0,0,canvas.width,canvas.height);
let scale = document.getElementById("scl").value;
drawCenteredImage(image, canvas.width / 2, canvas.height / 2, image.width * scale, image.height * scale);
}
let mousedownID = -1;
function mousedown(event) {
if(mousedownID==-1)
mousedownID = setInterval(drawImg, 5);
}
function mouseup(event) {
if(mousedownID!=-1) { //Only stop if exists
clearInterval(mousedownID);
mousedownID=-1;
}
}
//Assign events
document.getElementById("scl").addEventListener("mousedown", mousedown);
document.getElementById("scl").addEventListener("mouseup", mouseup);
//Also clear the interval when user leaves the window with mouse
document.addEventListener("mouseout", mouseup);
<input id="scl" type="range" min="0" max="1" value="0.3" step="0.05">
<canvas id="canvas" width="500px" height="400px"></canvas>
I'm drawing a large canvas image as a background, the image is larger that the window size. I'm wondering if theres a way for me to center the image to fit on full screen. If so, how? this is what I'm doing:
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d');
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
drawStuff();
}
resizeCanvas();
function drawStuff() {
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 69, 50);
};
imageObj.src = '/resources/img/bg.png';
}
Here is an optional way of centering an image to canvas not using transform (also see note below):
imageObj.onload = function() {
var x = (canvas.width - this.width ) * 0.5, // this = image loaded
y = (canvas.height - this.height) * 0.5;
ctx.drawImage(this, x, y);
};
Since the image is larger than the canvas x and y will be negative in this case, which is perfectly fine. If the image was smaller it would work just as fine too. If you do the drawing outside the load handler you would of course need to use imageObj instead of this.
NOTE: The way you have set up your resize handler is not the best way to handle image repositioning - you should only load the image once, then reuse that object. As resizing typically creates a number of events it would trigger an equal number of image reloads.
For this to work properly you could do something like this instead:
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
imageObj = new Image(); // declare globally
imageObj.onload = function() {
// now set up handler when image is actually loaded
// - else drawImage will fail (width, height is not available and no data)
window.addEventListener('resize', resizeCanvas, false);
// initial call to draw image first time
resizeCanvas();
};
imageObj.src = '/resources/img/bg.png';
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
drawStuff();
}
function drawStuff() {
var x = (canvas.width - imageObj.width ) * 0.5,
y = (canvas.height - imageObj.height) * 0.5;
ctx.drawImage(imageObj, x, y);
}
It's not perfect as the resize event queue will still be large and may lag - there are solutions for this too, for example this one (with minor modifications).
Here's how to center the image on the canvas. Any vertical or horizontal overflow will be off-canvas:
imageObj.onload = function() {
ctx.translate(canvas.width/2,canvas.height/2);
ctx.drawImage(imageObj,-imageObj.width/2,-imageObj.height/2);
ctx.translate(-canvas.width/2,-canvas.height/2);
};
Good luck with your project!
If you are adding image after uploading then you can use this, it works for me :)
var image_center_width = (canvas.width - img.width) / 2;
var image_center_height = (canvas.height - img.height) / 2;
img.set({
left : image_center_width,
top : image_center_height,
});
canvas.add(img)
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);