I am experiencing an issue with converting a canvas to PNG.
Although the canvas looks exactly as I want it and the conversion from canvas to data URL PNG seems right, the image is blank.
I also tried converting a div to PNG but it did not work for me because I wanted a greyscale filter to be applied. Anyone have any ideas?
JavaScript
var imgis = new Image();
var bubble = new Image();
var canvasWidth;
var canvasHeight;
var ctx = canvas.getContext('2d');
bubble.onload = function() {
var imgis = new Image();
var bubble = new Image();
var ctx = canvas.getContext('2d');
bubble.onload = function() {
// set the canvas' size
canvas.width = this.width;
canvas.height = this.height;
// first fill a rect
ctx.fillStyle = 'rgba(255, 255, 255, 0)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// set the gCO
ctx.globalCompositeOperation = 'luminosity';
// if the browser doesn't support Blend Modes
console.log(ctx.globalCompositeOperation)
if (ctx.globalCompositeOperation !== 'luminosity')
fallback(this);
else {
// draw the image
ctx.drawImage(this, 0, 0);
ctx.drawImage(imgis, 30, 60);
// reset the gCO
ctx.globalCompositeOperation = 'source-over';
}
}
imgis.crossOrigin = "anonymous";
bubble.crossOrigin = "anonymous";
imgis.src = "image1 src";
bubble.src = "image2 src";
function fallback(img) {
// first remove our black rectangle
ctx.clearRect(0, 0, canvas.width, canvas.height);
//draw the image
ctx.drawImage(img, 0, 0);
ctx.drawImage(imgis, 30, 60);
// get the image data
var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var d = imgData.data;
// loop through all pixels
// each pixel is decomposed in its 4 rgba values
for (var i = 0; i < d.length; i += 4) {
// get the medium of the 3 first values
var med = (d[i] + d[i + 1] + d[i + 2]) / 3;
// set it to each value
d[i] = d[i + 1] = d[i + 2] = med;
}
// redraw the new computed image
ctx.putImageData(imgData, 0, 0);
}
canvas = document.getElementById('canvas');
var image = Canvas2Image.convertToPNG(canvas);
console.log(image.src);
// document.getElementById('theDemo').src = image.src;
var image_data = $(image).attr('src');
console.log(image_data);
$("#theDemo").attr('src', image_data);
HTML
<canvas id='canvas' > </canvas>
<img src="" id="theDemo" />
I assume you're using canvas2image. You should replace var image = Canvas2Image.convertToPNG(canvas); with Canvas2Image.convertToPNG(canvas, width, height). Hopefully that helps!
EDIT Since the issue is with the actual canvas to base64 conversion, you can try to use the .toDataURL() method instead of using that library. My comment explains how to test this in your specific code.
Related
I have an image that has holes in it and need to fill in the outline, here is the image for understanding, but I want to fill in the whole image red and leave the rest of the background white.
var canvas = document.getElementById("canvas1");
var ctx = canvas.getContext('2d'),
img = new Image;
img.onload = draw;
img.crossOrigin = 'anonymous';
img.src = "pinkclear.png";
var outline,
outlineCtx;
function draw(color)
{
ctx.clearRect(0,0,canvas.width,canvas.height);
// onload
if(typeof color !== 'string') color = 'red';
var cliparray = [];
var dArr = [-1,-1, 0,-1, 1,-1, -1,0, 1,0, -1,1, 0,1, 1,1], // offset array
s = 1, // scale
i = 1, // iterator
x = 5, // final position
y = 5;
// draw images at offsets from the array scaled by s
for(; i < dArr.length; i += 2)
{
ctx.drawImage(img, x + dArr[i]*s, y + dArr[i+1]*s);
// fill with color
ctx.globalCompositeOperation = "source-in";
ctx.fillStyle = color;
ctx.fillRect(0,0,canvas.width, canvas.height);
// keep only the outline
ctx.globalCompositeOperation = "destination-out";
ctx.drawImage(img, x, y);
// store the imageData in a new Canvas
outline = canvas.cloneNode(true);
outlineCtx = outline.getContext('2d')
//outlineCtx.drawImage(canvas,0,0);
// draw image in original mode
ctx.globalCompositeOperation = "source-over";
//ctx.drawImage(img, x, y);
}
}
I am trying to add four rotating images to an animated background.
I can only get one image working correctly with my code below.
How can I add in the other three images?
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var img = document.createElement('img');
img.onload = function(){
render();
}
img.src = 'nano3.png';
function drawImage(img,x,y,r,sx,sy){
sx=sx||0;
sy=sy||0;
r=(r*Math.PI/180)||0;
var cr = Math.cos(r);
var sr = Math.sin(r);
ctx.setTransform(cr,sr,-sr,cr,x-(cr*sx-sr*sy),y-(sr*sx+cr*sy));
ctx.drawImage(img,1,2);
}
var r = 1;
function render(){
requestAnimationFrame(render);
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0,0,800,800);
drawImage(img,50,50,r++,img.width/2,img.height/2);
}
This should help you out, I just created an object known as rotatingimage which stores a location, an image and its current rotation. We call the 'draw' method in a 'setInterval' function call which deals with rotating the canvas and then drawing the sprite correctly.
Just a note rotating many images can cause the canvas to lag also the CurrentRotation variable never gets reset to 0 when it reaches >359 so the CurrentRotation variable will keep going higher and higher, you may want to fix that in the RotatingImage.prototype.Draw function
jsFiddle:https://jsfiddle.net/xd8brfrk/
Javascript
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
function RotatingImage(x, y, spriteUrl, rotationSpeed) {
this.XPos = x;
this.YPos = y;
this.Sprite = new Image();
this.Sprite.src = spriteUrl;
this.RotationSpeed = rotationSpeed;
this.CurrentRotation = 0;
}
RotatingImage.prototype.Draw = function(ctx) {
ctx.save();
this.CurrentRotation += 0.1;
ctx.translate(this.XPos + this.Sprite.width/2, this.YPos + this.Sprite.height/2);
ctx.rotate(this.CurrentRotation);
ctx.translate(-this.XPos - this.Sprite.width/2, -this.YPos - this.Sprite.height/2);
ctx.drawImage(this.Sprite, this.XPos, this.YPos);
ctx.restore();
}
var RotatingImages = [];
RotatingImages.push(new RotatingImage(50, 75, "http://static.tumblr.com/105a5af01fc60eb94ead3c9b342ae8dc/rv2cznl/Yd9oe4j3x/tumblr_static_e9ww0ckmmuoso0g4wo4okosgk.png", 1));
RotatingImages.push(new RotatingImage(270, 25, "http://static.tumblr.com/105a5af01fc60eb94ead3c9b342ae8dc/rv2cznl/Yd9oe4j3x/tumblr_static_e9ww0ckmmuoso0g4wo4okosgk.png", 1));
RotatingImages.push(new RotatingImage(190, 180, "http://static.tumblr.com/105a5af01fc60eb94ead3c9b342ae8dc/rv2cznl/Yd9oe4j3x/tumblr_static_e9ww0ckmmuoso0g4wo4okosgk.png", 1));
RotatingImages.push(new RotatingImage(100, 270, "http://static.tumblr.com/105a5af01fc60eb94ead3c9b342ae8dc/rv2cznl/Yd9oe4j3x/tumblr_static_e9ww0ckmmuoso0g4wo4okosgk.png", 1));
setInterval(function() {
ctx.fillStyle = "#000"
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < RotatingImage.length; i++) {
var rotatingImage = RotatingImages[i];
rotatingImage.Draw(ctx);
}
}, (1000 / 60));
you can use save and restore to apply different transform to your drawing
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore
I am trying to make a variant of the boomshine game in javascript and everything works when I draw a circular shape with the arc function. However when i try to replace the arc function with the drawImage function to use a coin image instead of a circular shape I start having problems when I clear the canvas to delete the previous drawn circular shapes. If I don't clear the canvas before rendering the images, the images are drawn on the canvas except the old images are still on the canvas. But when i do clear the canvas before rendering the images again, nothing is drawn on the canvas.
I have included screenshots, the links are below.
This is how I clear the canvas:
var ctx = game.context;
ctx.fillStyle = "darkgray";
ctx.fillRect(0, 0, game.canvas.width, game.canvas.height);
This is how i draw the image:
function drawImageBall(x,y,radius,startAngle,color)
{
var img = document.createElement('img');
img.src = 'img/coin-icon.png';
var tmpCtx= game.context;
var ax = x-radius;
var ay = y-radius;
img.onload = function() {
tmpCtx.save();
tmpCtx.beginPath();
tmpCtx.arc(x, y, radius, 0, Math.PI * 2, true);
tmpCtx.closePath();
tmpCtx.clip();
tmpCtx.drawImage(img, ax, ay, img.width, img.height);
tmpCtx.beginPath();
tmpCtx.arc(0, 0, radius, 0, Math.PI * 2, true);
tmpCtx.clip();
tmpCtx.closePath();
tmpCtx.restore();
};
}
Clearing canvas (screenshot)
Without clearing canvas (screenshot)
Keep in mind that downloading the img will take some time.
During that downloading time, Javascript does not stop(!). Instead JS will continue executing any following code. This is causing your unexpected problems.
So download the img just once at the start of your app. That way your drawImage will be done in the order that you expect because there will be no delay while your image is downloading.
Using your code, I maked some changes, I removed the tmpTcx.clip(), look the fidlle. Tip: For performace questions you don't need load the image every time that you want write the canvas.
Poor Example: https://jsfiddle.net/wf4z0d2h/1/
function clearCanvas(){
var ctx = game.context;
ctx.fillStyle = "darkgray";
ctx.fillRect(0, 0, game.canvas.width, game.canvas.height);
}
function drawImageBall(x,y,radius,startAngle,color)
{
if(x == undefined){x = 100;}
if(y == undefined){y = 100;}
if(radius == undefined){radius = 40;}
//var img = document.createElement('img');
//img.src = 'img/coin-icon.png';
//img.src = "http://ps2.lansa.com/images/icons/normal/256/coin_256.png";
var tmpCtx= game.context;
var ax = x-radius;
var ay = y-radius;
//img.onload = function() {
tmpCtx.save();
tmpCtx.beginPath();
tmpCtx.arc(x, y, radius, 0, Math.PI * 2, true);
tmpCtx.stroke(); // Draw it
tmpCtx.closePath();
//tmpCtx.clip();
tmpCtx.drawImage(img, ax, ay, img.width, img.height);
//tmpCtx.beginPath();
//tmpCtx.arc(0, 0, radius, 0, Math.PI * 2, true);
////tmpCtx.clip();
//tmpCtx.stroke(); // Draw it
//tmpCtx.closePath();
//tmpCtx.restore();
//};
}
var img = document.createElement('img');
//img.src = 'img/coin-icon.png';
img.src = "http://ps2.lansa.com/images/icons/normal/256/coin_256.png";
//drawImageBall();
img.onload = function(){
x = 0;
y = 0;
setInterval(function(){
x = x+10;
y = y+10;
clearCanvas();
drawImageBall(x,y);
},300);
}
As an exercise, I would like to fill the background of the browser window with pixels of random color: https://jsfiddle.net/j8fay7bs/3/
My question is how to retrieve the current filling of the background image and randomly change the pixels? Currently the image seems to be reverted to black.
// redraw canvas pixels on resize --------------------------
var render = function resize() {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.canvas.width = window.innerWidth;
context.canvas.height = window.innerHeight;
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
var t0 = performance.now();
for (var i = 0; i < canvas.width*canvas.height; i++) {
if (Math.random() < 0.5) {
imageData.data[i*4] = Math.round(Math.random()*255);
imageData.data[i*4+1] = Math.round(Math.random()*255);
imageData.data[i*4+2] = Math.round(Math.random()*255);
imageData.data[i*4+3] = 255;
}
}
context.putImageData(imageData, 0, 0);
$('#fps').text(1000/(performance.now() - t0) + " fps");
}
window.onresize = render;
// rendering loop ---------------------------------------
(function loop() {
setInterval(render, 10);
})();
You may retrieve the last image data before u touch the canvas width and height,
once u touched them, the canvas will be reset:
var render = function resize() {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var oldImageData = context.getImageData(0, 0, canvas.width, canvas.height); // <----
context.canvas.width = window.innerWidth;
context.canvas.height = window.innerHeight;
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
updated fiddle
Say we have a canvas:
<canvas id="one" width="100" height="200"></canvas>
And on a button click the canvas gets rotated 90 degrees clockwise (around the center) and the dimensions of the canvas get also updated, so in a sense it looks like this afterwards:
<canvas id="one" width="200" height="100"></canvas>
Note that the id of the canvas is the same.
Imagine simply rotating an image clockwise without it being cropped or being padded.
Any suggestions before I do it the long way of creating a new canvas and rotating and copying pixel by pixel?
UPDATE sample code with suggestion from comments still not working:
function imageRotatecw90(){
var canvas = document.getElementById("one");
var context = canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var myImageData = context.getImageData(0,0, cw,ch);
context.save();
context.translate(cw / 2, ch / 2);
context.rotate(Math.PI/2);
context.putImageData(myImageData, 0, 0);
context.restore();
canvas.width=ch;
canvas.height=cw;
}
FiddleJS
Look at this DEMO.
To achieve the results seen in demo, I made use of canvas.toDataURL to cache the canvas into an image, then reset the canvas to their new dimensions, translate and rotate the context properly and finally draw the cached image back to modified canvas.
That way you easily rotate the canvas without need to redraw everything again. But because anti-aliasing methods used by browser, each time this operation is done you'll notice some blurriness in result. If you don't like this behavior the only solution I could figure out is to draw everything again, what is much more difficult to track.
Here follows the code:
var canvas = document.getElementById("one");
var context = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
// Sample graphic
context.beginPath();
context.rect(10, 10, 20, 50);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
// create button
var button = document.getElementById("rotate");
button.onclick = function () {
// rotate the canvas 90 degrees each time the button is pressed
rotate();
}
var myImageData, rotating = false;
var rotate = function () {
if (!rotating) {
rotating = true;
// store current data to an image
myImageData = new Image();
myImageData.src = canvas.toDataURL();
myImageData.onload = function () {
// reset the canvas with new dimensions
canvas.width = ch;
canvas.height = cw;
cw = canvas.width;
ch = canvas.height;
context.save();
// translate and rotate
context.translate(cw, ch / cw);
context.rotate(Math.PI / 2);
// draw the previows image, now rotated
context.drawImage(myImageData, 0, 0);
context.restore();
// clear the temporary image
myImageData = null;
rotating = false;
}
}
}
Rotation
Note it is not possible to rotate a single element.
ctx.save();
ctx.rotate(0.17);
// Clear the current drawings.
ctx.fillRect()
// draw your object
ctx.restore();
Width/height adjustment
The only way I ever found to properly deal with display ratios, screen sizes etc:
canvas.width = 20;// DO NOT USE PIXELS
canvas.height = 40; // AGAIN NO PIXELS
Notice I am intentionally not using canvas.style.width or canvas.style.height. Also for an adjustable canvas don't rely on CSS or media queries to do the transformations, they are a headache because of the pixel ratio differences. JavaScript automatically accounts for those.
Update
You also have to update the width and the height before you draw. Not sure what you are trying to achieve, but I guess this isn't a problem:
Demo here
var canvas = document.getElementById("one");
var context = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
canvas.width = 200;
canvas.height = 400;
// Sample graphic
context.beginPath();
context.rect(10,10,20,50);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
var myImageData = context.getImageData(0, 0, cw, ch);
context.save();
context.translate(cw / 2, ch / 2);
context.putImageData(myImageData, 0, 0);
context.rotate(0.20);
If you want to rotate an image by 90 degrees this might be helpful:
export const rotateBase64Image = async (base64data: string) => {
const image = new Image();
image.src = base64data;
return new Promise<string>((resolve, reject) => {
image.onload = function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("cannnot get context '2d'");
canvas.width = image.height;
canvas.height = image.width;
ctx.setTransform(0, 1, -1, 0, canvas.width, 0); // overwrite existing transform
ctx!.drawImage(image, 0, 0);
canvas.toBlob((blob) => {
if (!blob) {
return reject("Canvas is empty");
}
const fileUrl = window.URL.createObjectURL(blob);
resolve(fileUrl);
}, "image/jpeg");
};
});
};
If you don't have image in base64 format you can do it like this:
const handleRotate = async () => {
const res = await fetch(link);
const blob = await res.blob();
const b64: string = await blobToB64(blob);
const rotatedImage = await rotateBase64Image(b64)
setLink(rotatedImage);
}
Here is my blobTob64 function:
export const blobToB64 = async (blob) => {
return new Promise((resolve, _) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
};