This question already has an answer here:
html5 canvas toDataURL returns blank image
(1 answer)
Closed 1 year ago.
I am trying to convert an image URL taken from firebase storage to a data URL. But when I opened the data URL, it displays a blank image in the new window. I need help to fix this issue.
var dataURL;
var imageurl;
var folder = formName + "/" + Insti + "/" + tenant + "/" + "Page 3";
var storageRef = firebase.storage().ref();
var listRef = storageRef.child(folder);
listRef.listAll()
.then((res) => {
res.items.forEach((itemRef) => {
itemRef.getDownloadURL().then(function(imageurl) {
console.log(imageurl)
var img = new Image();
var canvas = document.getElementById("image1");
var ctx = canvas.getContext("2d");
img.onload = function() {
canvas.height = canvas.width * (img.height / img.width);
var oc = document.createElement('canvas'),
octx = oc.getContext('2d');
oc.width = img.width * 0.5;
oc.height = img.height * 0.5;
octx.drawImage(img, 0, 0, oc.width, oc.height);
octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);
ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5,
0, 0, canvas.width, canvas.height);
};
img.src = imageurl;
dataURL = canvas.toDataURL("image/png");
console.log(dataURL)
});
});
}).catch((error) => {
// Uh-oh, an error occurred!
});
<canvas width="300" height="200" id="image1"></canvas>
You invoke canvas.toDataURL() before the image is drawn to the canvas. So you export a blank canvas. Just move toDataURL inside the img.onload callback, that's it.
img.onload = function () {
canvas.height = canvas.width * (img.height / img.width);
var oc = document.createElement('canvas'),
octx = oc.getContext('2d');
oc.width = img.width * 0.5;
oc.height = img.height * 0.5;
octx.drawImage(img, 0, 0, oc.width, oc.height);
octx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5);
ctx.drawImage(oc, 0, 0, oc.width * 0.5, oc.height * 0.5,
0, 0, canvas.width, canvas.height);
dataURL = canvas.toDataURL("image/png"); // <-- Here
console.log(dataURL);
};
img.src = imageurl;
Related
I want to make the context inside the canvas to rotate on button click, I'm pretty sure that I have to use onclick, but I don't know where to put it or what logic do I have to write inside it. Can anyone help me out?
I tried using jquery on click but that does not work:
HTML:
<input type="file" id="fileInp" onchange="readImage(this)">
<button type="button" class="rotate">Rotate</button>
<div>
<canvas id="canvasImg" style="max-width:100%; border:solid;" width="100%" height="100%"></canvas>
</div>
Javascript:
function readImage(input) {
const canvas = document.getElementById('canvasImg');
const context = canvas.getContext("2d");
context.canvas.width = window.innerWidth;
context.canvas.height = window.innerHeight;
context.clearRect(0, 0, canvas.width, canvas.height);
if (input.value !== '') {
imgSrc = window.URL.createObjectURL(input.files[0]);
}
const img = new Image();
img.onload = function() {
context.drawImage(img, 0, 0);
}
img.src = imgSrc;
}
jQuery('.rotate').on('click', function() {
degree = 90;
drawRotate(degree);
})
function drawRotate(degree) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.save();
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate(degrees * Math.PI / 180);
context.drawImage(image, -image.width / 2, -image.width / 2);
context.restore();
}
ok so there are a few problems in your code..
in the drawRotate function you use variables such as image, context, canvas which is never has declared..
you call the function param degree but use it as degreeS
you translate the context forward, but never return him back
maybe there was more and i forget..
shortly, please look at the attached code
<input type="file" id="fileInp" onchange="readImage(this)">
<button type="button" class="rotate" onclick="drawRotate(90)">Rotate</button>
<div>
<canvas id="canvasImg" style="max-width:100%; border:solid;" width="100%" height="100%"></canvas>
</div>
<script>
const canvas = document.getElementById('canvasImg');
const context = canvas.getContext("2d");
const input = document.getElementById('fileInp');
function readImage(input) {
context.canvas.width = window.innerWidth;
context.canvas.height = window.innerHeight;
context.clearRect(0, 0, canvas.width, canvas.height);
let imgSrc;
if (input.value !== '') {
imgSrc = window.URL.createObjectURL(input.files[0]);
}
const img = new Image();
img.onload = function() {
context.translate(canvas.width / 2, canvas.height / 2);
context.drawImage(img, -img.width / 2, -img.height / 2, img.width, img.height);
context.translate(-canvas.width / 2, -canvas.height / 2);
}
img.src = imgSrc;
}
function drawRotate(degree) {
let imgSrc;
if (input.value !== '') {
imgSrc = window.URL.createObjectURL(input.files[0]);
}
const img = new Image();
img.onload = function() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate(degree * Math.PI / 180);
context.drawImage(img, -img.width / 2, -img.height / 2, img.width, img.height);
context.translate(-canvas.width / 2, -canvas.height / 2);
}
img.src = imgSrc;
}
</script>
play around rotate(-180deg) for continous rotation
function readImage(input) {
const canvas = document.getElementById('canvasImg');
const context = canvas.getContext("2d");
context.canvas.width = window.innerWidth;
context.canvas.height = window.innerHeight;
context.clearRect(0, 0, canvas.width, canvas.height);
if (input.value !== '') {
imgSrc = window.URL.createObjectURL(input.files[0]);
}
const img = new Image();
img.onload = function() {
context.drawImage(img, 0, 0);
}
img.src = imgSrc;
}
jQuery('.rotate').on('click', function() {
$("#canvasImg").css({'transform': 'rotate(-180deg)'});
})
function drawRotate(degree) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.save();
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate(degrees * Math.PI / 180);
context.drawImage(image, -image.width / 2, -image.width / 2);
context.restore();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="file" id="fileInp" onchange="readImage(this)">
<button type="button" class="rotate">Rotate</button>
<div>
<canvas id="canvasImg" style="max-width:100%; border:solid;" width="100%" height="100%"></canvas>
</div>
I have a base64 string for an image, which i have to display as an icon. If the dimension of an image are bigger i have to display icon maintaining aspect ratio.
I have written a logic to identify if the image is landscape or portrait based on which height and width will be settled for canvas. But seems height and width of icons are not proper as i have hard coded it.
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var height, width;
if (this.height>this.width) {
height = 50;
width = 30;
} else if (this.height<this.width) {
height = 30;
width = 50;
}
canvas.width = width;
canvas.height = height;
ctx.drawImage(image,
0, 0, this.width, this.height,
0, 0, canvas.width, canvas.height
);
var scaledImage = new Image();
scaledImage.src = canvas.toDataURL();
Is there any way we can calculate it dynamically or any other way to preserve aspect ratio for icons.
Working Example can be found on https://codepen.io/imjaydeep/pen/ewBZRK
It will be fine if some space will be left on x-y axis.
You just need to calculate the scale or ratio and multiply both dimensions by that. Here's an example function, and here's your edited codepen.
Creates trimmed, scaled image:
function scaleDataURL(dataURL, maxWidth, maxHeight){
return new Promise(done=>{
var img = new Image;
img.onload = ()=>{
var scale, newWidth, newHeight, canvas, ctx;
if(img.width < maxWidth){
scale = maxWidth / img.width;
}else{
scale = maxHeight / img.height;
}
newWidth = img.width * scale;
newHeight = img.height * scale;
canvas = document.createElement('canvas');
canvas.height = newHeight;
canvas.width = newWidth;
ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, newWidth, newHeight);
done(canvas.toDataURL());
};
img.src = dataURL;
});
}
Or, if you want the image to always be the provided dimensions with empty surrounding area, you can use this: (codepen)
Creates scaled image of exact provided dimensions:
function scaleDataURL(dataURL, maxWidth, maxHeight){
return new Promise(done=>{
var img = new Image;
img.onload = ()=>{
var scale, newWidth, newHeight, canvas, ctx, dx, dy;
if(img.width < maxWidth){
scale = maxWidth / img.width;
}else{
scale = maxHeight / img.height;
}
newWidth = img.width * scale;
newHeight = img.height * scale;
canvas = document.createElement('canvas');
canvas.height = maxWidth;
canvas.width = maxHeight;
ctx = canvas.getContext('2d');
dx = (maxWidth - newWidth) / 2;
dy = (maxHeight - newHeight) / 2;
console.log(dx, dy);
ctx.drawImage(img, 0, 0, img.width, img.height, dx, dy, newWidth, newHeight);
done(canvas.toDataURL());
};
img.src = dataURL;
});
}
For someone who search for a solution when the image is bigger that you need.
function scaleDataURL(dataURL: string, maxWidth: number, maxHeight: number) {
return new Promise((done) => {
var img = new Image()
img.onload = () => {
var scale, newWidth, newHeight, canvas, ctx
if (img.width > maxWidth) {
scale = maxWidth / img.width
} else if (img.height > maxHeight) {
scale = maxHeight / img.height
} else {
scale = 1
}
newWidth = img.width * scale
newHeight = img.height * scale
canvas = document.createElement('canvas')
canvas.height = newHeight
canvas.width = newWidth
ctx = canvas.getContext('2d')
console.log('img', 'scale', scale, 0, 0, img.width, img.height, 0, 0, newWidth, newHeight)
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, newWidth, newHeight)
done(canvas.toDataURL())
}
img.src = dataURL
})
}
Usage:
scaleDataURL(base64data, 600, 200)
.then((newBase64data) => {
console.log(newBase64data)
})
.catch((err) => console.log(err))
I'm trying to crop an image using canvas.
Original image is 2217 x 790.
Loading on page it is scaled to 1515 x 540
Canvas is 960 x 540.
Both image and canvas are on center of screen, so aligned horizontally.
I need to crop central area - 960 x 540.
var img = document.getElementById("imgt");
var canvas = document.getElementById("canvasa");
var ctx = canvas.getContext("2d");
var a = $('#imgt').width() - 960;
var a = a/2; // this is 277.7...
ctx.drawImage(img, a, 0, 960, 540, 0, 0, 960, 540);
//also tried:
ctx.drawImage(img, 0, 0, 960, 540, 0, 0, 960, 540);
var newimg = new Image();
newimg.src = canvas.toDataURL('image/jpeg');
var dl = document.createElement("a");
dl.href = canvas.toDataURL("image/jpeg");
dl.download = true;
document.body.appendChild(dl);
dl.click();
Downloading newimg what I see - it is 300 x 150 !
See my comment for differences between width attribute and width as style. They are not exactly the same. Besides, I just made a fiddle since you didn't and I don't get the same behavior! The downloaded image is 960 * 540
HERE - fires with delay
ONLOAD
setTimeout(function(){
var canvas = document.createElement("canvas");
canvas.width = "960";
canvas.height="540";
var ctx = canvas.getContext("2d");
ctx.drawImage(document.images[0], 0, 0, 960, 540, 0, 0, 960, 540);
var a = document.createElement("a");
a.download = "image.jpeg";
a.href = canvas.toDataURL("image/jpeg");
a.click();
},5000);
In general, if you want to crop an area from a source image, and draw it into a canvas without breaking the aspect ratio and not hardcoding the dimensions into the routine, you can do this:
const canvasAspectRatio = canvas.width / canvas.height;
const cropWidth = canvas.width;
const cropHeight = cropWidth / canvasAspectRatio;
const sx = img.width / 2 - cropWidth / 2;
const sy = img.height / 2 - cropHeight / 2;
ctx.drawImage(img, sx, sy, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const img = document.getElementById('img');
const canvasAspectRatio = canvas.width / canvas.height;
// Crop central canvas sized rectangle area into canvas
const cropWidth = canvas.width;
const cropHeight = cropWidth / canvasAspectRatio; // Here you should calculate the height based on aspect ratio instead of assuming it matches that of the canvas
const sx = img.width / 2 - cropWidth / 2;
const sy = img.height / 2 - cropHeight / 2;
ctx.drawImage(img, sx, sy, cropWidth, cropHeight, 0, 0, canvas.width, canvas.height);
body {
background-color: black;
}
#img {
visibility: hidden;
}
#canvas {
border: 1px solid #f00;
}
<canvas id="canvas" width="960" height="540"></canvas>
<img id="img" src="http://via.placeholder.com/2217x790">
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;
}
I'm taking image input from user, then resizing and showing it on a canvas. Here is the code-
HTML-
<form class="cmxform">
<input type='file' id="Photo" />
<canvas id="canvas" width="300" height="200"></canvas>
</form>
JavaScript-
$("#Photo").change(function (e) {
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.src = URL.createObjectURL(e.target.files[0]);
img.onload = function () {
ctx.drawImage(img, 0, 0, img.width, img.height, // source rectangle
0, 0, canvas.width, canvas.height); // destination rectangle
}
});
But here I'm loosing the aspect ratio. Is there any way to do it?
UPDATE-
I've got the answer from this sa question -
Here is a snippet to help you for that
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
var fill = true;
if (fill)
{
$('#fill').attr("disabled", true);
}
$("#Photo").change(function (e) {
img.src = URL.createObjectURL(e.target.files[0]);
img.onload = function () {
if (fill)
{
drowImageFill(img);
}
else
{
drowImageCenter(img);
}
}
});
$("#fill").click(function(){
//console.log("fill");
var input = document.getElementById('Photo');
if (input.files[0] !== undefined)
{
img.src = URL.createObjectURL(input.files[0]);
img.onload = function () {
drowImageFill(img);
}
}
$('#fill').attr("disabled", true);
$('#center').attr("disabled", false);
fill = true;
});
$("#center").click(function(){
//console.log("center");
var input = document.getElementById('Photo');
if (input.files[0] !== undefined)
{
img.src = URL.createObjectURL(input.files[0]);
img.onload = function () {
drowImageCenter(img);
}
}
$('#center').attr("disabled", true);
$('#fill').attr("disabled", false);
fill = false;
});
//ratio formula
//http://andrew.hedges.name/experiments/aspect_ratio/
function drowImageFill(img){
ctx.clearRect(0, 0, canvas.width, canvas.height);
//detect closet value to canvas edges
if( img.height / img.width * canvas.width > canvas.height)
{
// fill based on less image section loss if width matched
var width = canvas.width;
var height = img.height / img.width * width;
offset = (height - canvas.height) / 2;
ctx.drawImage(img, 0, -offset, width, height);
}
else
{
// fill based on less image section loss if height matched
var height = canvas.height;
var width = img.width / img.height * height;
offset = (width - canvas.width) / 2;
ctx.drawImage(img, -offset , 0, width, height);
}
}
function drowImageCenter(img)
{
ctx.clearRect(0, 0, canvas.width, canvas.height);
if( img.height / img.width * canvas.width < canvas.height)
{
// center based on width
var width = canvas.width;
var height = img.height / img.width * width;
offset = (canvas.height - height) / 2;
ctx.drawImage(img, 0, offset, width, height);
}
else
{
// center based on height
var height = canvas.height;
var width = img.width / img.height * height;
offset = (canvas.width - width) / 2;
ctx.drawImage(img, offset , 0, width, height);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width="300" height="200" style="border:2px solid #000000;"></canvas>
<form class="cmxform">
<input type='file' id="Photo" />
</form>
<button id="fill">Fill</button>
<button id="center">Center</button>