I'm trying to get image resized from mm to pixels after clicking submit button but all I can achieve is cropping original image. It looks like canvas is not refreshing image after changing size. How can I do it properly?
Example on jsfiddle
JS Code:
document.addEventListener("DOMContentLoaded", function() {
var width = 0;
var height = 0;
var colors = 0;
var order = 0;
var canvas = document.getElementById('project');
var context = canvas.getContext('2d');
var logo = document.getElementById('logo');
logo.onload = function() {
canvas.width = logo.width;
canvas.height = logo.height;
context.drawImage(logo, 0, 0);
};
document.getElementById("btn_sub").addEventListener("click", function(event) {
event.preventDefault();
width = document.getElementById("form1").elements[0].value;
height = document.getElementById("form1").elements[1].value;
colors = document.getElementById("form1").elements[2].value;
order = document.getElementById("form1").elements[3].value;
logo.width = (width * 118) / 25.4;
logo.height = (height * 118) / 25.4;
canvas.width = (width * 118) / 25.4;
canvas.height = (height * 118) / 25.4;
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(logo, 0, 0);
The drawImage function is not taking into consideration the width and height of the image element. To make this work you can use this overload of the drawImage function context.drawImage(img,x,y,width,height).
Like this for example, context.drawImage(logo, 0, 0, logo.width, logo.height).
Related
I want to fill a Canvas with an Image and scale it to a certain width beforehand.
I am trying to achieve an effect where an image in the foreground of the canvas can be erased with the mouse to view an image in the background. This is why I need to use a pattern to fill my canvas instead of just using drawImage(). Everything works apart from the scaling of the foreground image. Here is my code for generating the pattern:
var blueprint_background = new Image();
blueprint_background.src = "myfunurl";
blueprint_background.width = window.innerWidth;
blueprint_background.onload = function(){
var pattern = context.createPattern(this, "no-repeat");
context.fillStyle = pattern;
context.fillRect(0, 0, window.innerWidth, 768);
context.fill();
};
This does exactly what it should do, except that the image keeps its original size.
As you see, I want the image to scale to window.innerWidth (which has the value 1920 when logging it).
If needed, I can provide the rest of the code, but since the error is most likely in this snippet, I decided not to post the rest.
EDIT: Here is my full code with the suggested changes. The front ground image now displays over the full width, however the erasing does not work anymore.
JavaScript (Note that I use jQuery instead of $):
jQuery(document).ready(function() {
var cwidth = window.innerWidth;
var cheight = 768;
function createCanvas(parent, width, height) {
var canvas = {};
canvas.node = document.createElement('canvas');
canvas.context = canvas.node.getContext('2d');
canvas.node.width = width || 100;
canvas.node.height = height || 100;
parent.appendChild(canvas.node);
return canvas;
}
function init(canvas, fillColor) {
var ctx = canvas.context;
canvas.isDrawing = true;
jQuery('#canvas').children().css('position:absolute; top: ' + jQuery('#Top_bar').height() + 'px');
// define a custom fillCircle method
ctx.fillCircle = function(x, y, radius, fillColor) {
this.fillStyle = fillColor;
this.beginPath();
this.moveTo(x, y);
this.arc(x, y, radius, 0, Math.PI * 2, false);
this.fill();
};
// bind mouse events
canvas.onmousemove = function(e) {
if (!canvas.isDrawing) {
return;
}
var x = e.pageX - this.offsetLeft;
var y = e.pageY - jQuery('#Top_bar').outerHeight();
var radius = 30;
var fillColor = '#ff0000';
ctx.globalCompositeOperation = 'destination-out';
ctx.fillCircle(x, y, radius, fillColor);
};
}
var container = document.getElementById('canvas');
jQuery('#canvas').css('position:absolute; top: ' + jQuery('#Top_bar').height() + 'px');
var canvas = createCanvas(container, cwidth, cheight);
init(canvas, '#ddd');
var fgimg = document.getElementById("fgimg");
fgimg.width = cwidth;
var context = canvas.node.getContext("2d");
let canvasP = document.getElementById("pattern");
canvasP.width = window.innerWidth;
canvasP.height = 768;
let ctxP = canvasP.getContext("2d");
ctxP.drawImage( fgimg, 0, 0,window.innerWidth,768 );
context.fillStyle = context.createPattern(canvasP,"no-repeat");
context.fillRect(0,0, canvas.width, canvas.height);
});
CSS:
#canvas {
background:url(http://ulmke-web.de/wp-content/uploads/2019/01/Header-6.jpg);
background-repeat: no-repeat;
background-size: cover;
background-position: center center;
width: 100%;
height: 768px;
}
HTML:
<div id="canvas">
<canvas id="pattern">
</div>
<div style="display:none">
<img id="fgimg" src=" http://ulmke-web.de/wp-content/uploads/2019/01/Header-5.jpg">
</div>
I would use two canvases. On the first one you draw your image and you use this canvas as an image to create the pattern. In order to scale the image you scale the size of the first canvas #pattern in my example.
For example you can do this for a 10/10 image:
canvasP.width = 10;
canvasP.height = 10;
ctxP.drawImage( redpoint, 2.5, 2.5 );
or you can do this for a 20/20 image:
canvasP.width = 20;
canvasP.height = 20;
ctxP.drawImage( redpoint, 5, 5,10,10 );
Furthermore, in my example I'm adding a little margin around the image.
let canvasP = document.getElementById("pattern");
if (canvasP && canvasP.getContext) {
let ctxP = canvasP.getContext("2d");
/*canvasP.width = 10;
canvasP.height = 10;
ctxP.drawImage( redpoint, 2.5, 2.5 ); */
canvasP.width = 20;
canvasP.height = 20;
ctxP.drawImage( redpoint, 5, 5,10,10 );
}
let canvas1 = document.getElementById("canvas");
if (canvas1 && canvas1.getContext) {
let ctx1 = canvas1.getContext("2d");
if (ctx1) {
ctx1.fillStyle = ctx1.createPattern(canvasP,"repeat");
ctx1.fillRect(0,0, canvas1.width, canvas1.height);
}
}
canvas{border:1px solid}
<img id="redpoint" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO 9TXL0Y4OHwAAAABJRU5ErkJggg==">
<canvas id="pattern"></canvas>
<canvas id="canvas"></canvas>
I hope it helps.
I have problem with drawing this (1766 * 2880) PNG file on canvas.
But I don't have any problem with JPG format of that size or (1533 * 2500) size of PNG file.
I consider devicePixelRatio for scaling canvas, and ignoring the ratio makes no changes.
var loadImage = function (url) {
var ratio = Math.max(window.devicePixelRatio || 1, 1),
image = new Image();
image.onload = function () {
var paper = '#paper-0',
canvas = $(paper)[0],
ctx = canvas.getContext("2d"),
img = this;
var w = Math.ceil(img.width / ratio), h = Math.ceil(img.height / ratio);
$(paper).css({ width: w + 'px', height: h + 'px' });
canvas.width = w * ratio;
canvas.height = h * ratio;
ctx.scale(ratio, ratio);
ctx.drawImage(img, 0, 0, w, h);
}
image.src = url;
}
How to place images in html5 canvas in a row based on no.of images given dynamically and how distribute space between the rows, where the no.of rows are also given dynamically?
If you want to do layout as simple as that in javascript, you can just do it yourself. The example below evenly spaces the rows based on the height of the canvas, and then within each row, the rectangles are evenly spaced by the width of the canvas. You can resize the browser and watch how the layout moves the rectangles around.
var imageHeight = 100;
var imageWidth = 80;
//This defines how many pictures are in each row
var rowCount = [
2,
3,
5
];
setCanvasSize();
redrawCanvas();
function setCanvasSize() {
var can = document.getElementById("myCanvas");
var width = window.innerWidth;
var height = window.innerHeight;
can.width = width;
can.height = height;
}
function redrawCanvas() {
var can = document.getElementById("myCanvas");
var ctx = can.getContext("2d");
ctx.clearRect(0, 0, width, height);
var width = can.width;
var height = can.height;
//
// This is the layout function ------------------------
//
//how many pixels per row are available?
var pixelsPerRow = height/rowCount.length;
for (var y=0;y<rowCount.length;y++) {
//draw the yth row:
var imagesInRow = rowCount[y];
var pixelsPerImage = width / imagesInRow;
for (var x=0;x<imagesInRow;x++) {
//now find the center of the cell defined by the
//x and y coordinates:
var xC = (x * pixelsPerImage) + (pixelsPerImage / 2);
var yC = (y * pixelsPerRow) + (pixelsPerRow / 2);
//now place the image in the center of the cell by offsetting half of the image's width and height:
var xI = xC - (imageWidth / 2);
var yI = yC - (imageHeight / 2);
//instead of drawing an image, for this example a rectangle is being drawn instead:
ctx.fillStyle = "#000";
ctx.fillRect(xI, yI, imageWidth, imageHeight);
}
}
}
window.onresize = function() {
setCanvasSize();
redrawCanvas();
};
<canvas id="myCanvas"></canvas>
At my 9-to-5 we regularly Base64 images when we work with image uploads for forms/apps etc for it's 'simplicity' in transmitting over AJAX and some other minor reasons.
However we regularly have issues when using this method with 'old'
Android devices.
The browser sometimes hangs - displays an available memory error
message and just restarts. Other times it just restarts without
popping any alerts.
This happens only on large images and on old Android devices that
have a lot of apps running etc, etc.
One solution is to reduce the size of the image before DataUrl'ing the whole thing - but sometimes this is not enough - we need the image to be of a relative size, e.g we can't use a pinhead size of an image.
How can we prevent the browser from hanging over this?
I have thought of a solution where I would use WebWorkers to asynchronously encode it in the background - but I am thinking what difference would that make? The UI might not freeze but memory is being used anyway.
Are there any other possible solutions?
Note: We are targeting only Android 4.1+ and iOS6+
Anyway,
This is the function we currently use for resizing and encoding to Base64.
function resizeCanvasImage(img, maxWidth, maxHeight) {
var imgWidth = img.width,
imgHeight = img.height;
var ratio = 1, ratio1 = 1, ratio2 = 1;
ratio1 = maxWidth / imgWidth;
ratio2 = maxHeight / imgHeight;
// Use the smallest ratio that the image best fit into the maxWidth x maxHeight box.
if (ratio1 < ratio2) {
ratio = ratio1;
}
else {
ratio = ratio2;
}
var canvas = document.createElement("canvas");
var canvasContext = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
var canvasCopy2 = document.createElement("canvas");
var copyContext2 = canvasCopy2.getContext("2d");
canvasCopy.width = imgWidth;
canvasCopy.height = imgHeight;
copyContext.drawImage(img, 0, 0);
// init
canvasCopy2.width = imgWidth;
canvasCopy2.height = imgHeight;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
var rounds = 2;
var roundRatio = ratio * rounds;
for (var i = 1; i <= rounds; i++) {
// tmp
canvasCopy.width = imgWidth * roundRatio / i;
canvasCopy.height = imgHeight * roundRatio / i;
copyContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvasCopy.width, canvasCopy.height);
// copy back
canvasCopy2.width = imgWidth * roundRatio / i;
canvasCopy2.height = imgHeight * roundRatio / i;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
} // end for
// return Base64 string of the downscaled image
canvas.width = imgWidth * roundRatio / rounds;
canvas.height = imgHeight * roundRatio / rounds;
canvasContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvas.width, canvas.height);
var dataURL = canvas.toDataURL();
return dataURL;
}
My users can select an image using the file-upload HTML input element -
1. From there I downscale
2. I convert to base64
For some reason Chrome mobile & Android browser completely crash - and
display an 'Out of Memory error'.
If the browser runs on a more 'modern/capable' device all goes perfectly fine.
What could be causing the error here - can it be fixed?
Here is the function that downscales(whilst keeping aspect ratios) and returns a Base64 string of the image.
function resizeAndConvertB64(img, maxWidth, maxHeight) {
var imgWidth = img.width,
imgHeight = img.height;
var ratio = 1, ratio1 = 1, ratio2 = 1;
ratio1 = maxWidth / imgWidth;
ratio2 = maxHeight / imgHeight;
// Use the smallest ratio that the image best fit into the maxWidth x maxHeight box.
if (ratio1 < ratio2) {
ratio = ratio1;
}
else {
ratio = ratio2;
}
var canvas = document.createElement("canvas");
var canvasContext = canvas.getContext("2d");
var canvasCopy = document.createElement("canvas");
var copyContext = canvasCopy.getContext("2d");
var canvasCopy2 = document.createElement("canvas");
var copyContext2 = canvasCopy2.getContext("2d");
canvasCopy.width = imgWidth;
canvasCopy.height = imgHeight;
copyContext.drawImage(img, 0, 0);
// init
canvasCopy2.width = imgWidth;
canvasCopy2.height = imgHeight;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
var rounds = 1;
var roundRatio = ratio * rounds;
for (var i = 1; i <= rounds; i++) {
// tmp
canvasCopy.width = imgWidth * roundRatio / i;
canvasCopy.height = imgHeight * roundRatio / i;
copyContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvasCopy.width, canvasCopy.height);
// copy back
canvasCopy2.width = imgWidth * roundRatio / i;
canvasCopy2.height = imgHeight * roundRatio / i;
copyContext2.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvasCopy2.width, canvasCopy2.height);
} // end for
// return Base64 string of the downscaled image
canvas.width = imgWidth * roundRatio / rounds;
canvas.height = imgHeight * roundRatio / rounds;
canvasContext.drawImage(canvasCopy2, 0, 0, canvasCopy2.width, canvasCopy2.height, 0, 0, canvas.width, canvas.height);
var dataURL = canvas.toDataURL();
return dataURL;
}
I think it is an error of Chrome mobile.
Chrome mobile is not stable and my handy crashes often when I use it.
The out of memory message says that your device hasn't got enough memory for this script.
Check if it works with Dolphin Browser.
I think it will work but I don't know it.