Canvas.toDataURL() not showing background image of canvas - html

I have added background image to canvas using css.
But when converting it into png using canvas.toDataURL() , its not showing background image.
It seems like its not parsing css background image.
var svgString = new XMLSerializer().serializeToString(document.querySelector('svg'));
var canvas = document.getElementById("canvas");
d3.select('canvas')
.attr('style', 'background-image:url("chart.png");background-position:50%;background-repeat:no-repeat');
var ctx = canvas.getContext("2d");
var DOMURL = self.URL || self.webkitURL || self;
var img = new Image();
//var svg = new Blob([svgString], {type: "image/svg+xml;charset=utf-8"});
var svg = new Blob([svgString], {type: "image/svg+xml"});
var url = DOMURL.createObjectURL(svg);
var png = '';
img.onload = function() {
ctx.drawImage(img, 0, 0);
png = canvas.toDataURL("image/png");
document.querySelector('#chart').innerHTML = '<img src="'+png+'"/>';
DOMURL.revokeObjectURL(png);
img.src = "";
};
img.crossOrigin = "anonymous";
img.src = url;

As said in comments, background-image is not part of the canvas context and thus, can't be exported by any canvas' export methods.
If you want to get this image drawn on the canvas, simply draw it before you draw the other image.* The main difficulty being to reproduce the different option CSS background has. But in your case, it's quite simple.
var svgString = new XMLSerializer().serializeToString(document.querySelector('svg'));
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var DOMURL = self.URL || self.webkitURL || self;
var svg = new Blob([svgString], {
type: "image/svg+xml"
});
var url = DOMURL.createObjectURL(svg);
var png = '';
var toLoad = 2;
var loaded = 0;
var background = new Image();
var svgImg = new Image();
// attach the event to both images
background.onload = svgImg.onload = function(){
// only when both has loaded
if(++loaded === toLoad){
// set the canvas size to the svg image's one
canvas.width = svgImg.width;
canvas.height = svgImg.height;
// draw the background image first
ctx.drawImage(background, (canvas.width/2)-(background.width/2), (canvas.height/2)-(background.height/2));
// then the svg image
ctx.drawImage(svgImg, 0, 0);
png = canvas.toDataURL("image/png");
document.querySelector('#chart').innerHTML = '<img src="' + png + '"/>';
// you want to revoke the svgImg ObjectURL, not the canvas dataURI
DOMURL.revokeObjectURL(svgImg.src);
}
}
svgImg.src = url;
// set it only if you're doing a cross origin request
// Object URL are not
background.crossOrigin = 'anonymous';
background.src = 'https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png'
svg{border: 1px solid red;}
canvas{border: 1px solid blue;}
img{border: 1px solid green;}
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="55" height="55" fill-opacity=".5"/>
</svg>
<canvas id="canvas"></canvas>
<p id="chart"></p>
*As said by markE, if you can't draw the background before, you can also set the globalCompositeOperation of your context to destination-over in order to draw behind the actual content.
Now, this wasn't clearly part of your question, but if you tried to set the background-image to your svg node, you should have been able to draw it on your canvas image, as part of the svg.
But since HTMLImage (<img>) element can't load any external resources form its loaded media, you would have to first convert the image to a dataURI version, and append the styles either as the svg's style attribute, either in a <style> element appended inside the svg element.
Here is a ugly example that will convert an svg element with a CSS defined background-image, and draw it on the canvas.
Don't use it, this just here for the demo but is likely to break. Also, this has only been quickly tested against FF, Chrome and Safari.
function convertSVGWithBG(svg, callback) {
// this function is not be bullet proof, better if you know the urls in advance
// in case of multi-images
var imgsToLoad = getComputedStyle(svg).backgroundImage.split(',');
appendDocumentStylesTo(svg);
var toLoad = imgsToLoad.length,
loaded = 0,
// we need to keep it sorted
rules = [],
canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
imgsToLoad.forEach(function(s, i) {
var bgURL = parseURL(s),
bgImg = new Image();
bgImg.crossOrigin = 'anonymous';
var errored = false;
bgImg.onload = function() {
canvas.width = this.width;
canvas.height = this.height;
ctx.drawImage(this, 0, 0);
rules[i] = canvas.toDataURL();
if (++loaded === toLoad) {
onend();
}
bgImg.onerror = function() {
if (!errored) {
errored = true;
this.crossOrigin = null;
this.removeAttribute('crossorigin');
this.src = this.src;
} else {
console.warn('failed to load img at src ', this.src);
if (--toLoad === loaded) {
onend();
}
}
};
};
bgImg.src = bgURL;
function onend() {
var toLoad = rules.filter(function(r) {
return r;
}).length,
loaded = 0;
// wait for the dataURI version has loaded too
rules.forEach(function(url) {
var img = new Image();
img.onload = function() {
if (++loaded === toLoad) {
callback(svg);
}
};
img.src = url
});
// it has to be inline style, or appended in a <style> element directly in our svg element
var fullRule = 'url(' + rules.join('), url(') + ')';
svg.style.backgroundImage = fullRule;
}
});
function parseURL(str) {
// this is ugly and there should be a better way to do so (maybe regex)
var url = str.split('"')[1];
if (!url) {
url = str.split("'")[1];
}
if (!url) {
url = str.split('(')[1].split(')')[0];
}
return url;
}
}
function svgToCanvas(svg) {
var svgString = new XMLSerializer().serializeToString(svg);
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var DOMURL = self.URL || self.webkitURL || self;
var svg = new Blob([svgString], {
type: "image/svg+xml"
});
var url = DOMURL.createObjectURL(svg);
var svgImg = new Image();
svgImg.onload = function() {
canvas.width = svgImg.width;
canvas.height = svgImg.height;
ctx.drawImage(svgImg, 0, 0);
png = canvas.toDataURL("image/png");
document.querySelector('#chart').innerHTML = '<img src="' + png + '"/>';
DOMURL.revokeObjectURL(svgImg.src);
}
svgImg.src = url;
}
var svg = document.querySelector('svg');
convertSVGWithBG(svg, svgToCanvas);
// this is completely unstable and should never be used...
function appendDocumentStylesTo(element) {
var docStyleSheets = document.styleSheets;
var i = 0,
j, rules, r, key;
for (i; i < docStyleSheets.length; i++) {
rules = docStyleSheets[i].cssRules;
for (j = 0; j < rules.length; j++) {
r = rules[j];
if (element.matches(r.selectorText)) {
if (r.style) {
for (k = 0; k < r.style.length; k++) {
key = r.style[k];
// ugly hack for Safari
if (key.indexOf('repeat') > -1) {
key = 'background-repeat';
}
element.style[key] = r.style[key];
}
}
}
}
}
}
canvas {
border: 1px solid blue;
}
img {
border: 1px solid green;
}
svg {
background-image: url(https://dl.dropboxusercontent.com/s/rumlhyme6s5f8pt/ABC.png), url(https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png);
background-position: 50%;
background-repeat: no-repeat;
}
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="55" height="55" fill-opacity=".5" />
</svg>
<canvas id="canvas"></canvas>
<p id="chart"></p>

This solution is working
var svgString = new XMLSerializer().serializeToString(document.querySelector('svg'));
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var DOMURL = self.URL || self.webkitURL || self;
var img = new Image();
//var svg = new Blob([svgString], {type: "image/svg+xml;charset=utf-8"});
var svg = new Blob([svgString], {type: "image/svg+xml"});
var url = DOMURL.createObjectURL(svg);
var png = '';
var emailConfirmationMessage = '';
var img1 = new Image();
img.onload = function() {
ctx.drawImage(img1, 136, 136);
ctx.drawImage(img, 0, 0);
sendEmail(canvas,DOMURL);
};
img1.onload = function(){
img.src = url;
};
img1.src = 'chart1.png';

Related

Selenium - How to debug code in driver.ExecuteScript String

In my vb.net Project with Selenium, I have a set of JS statements to convert html to canvas to image and then download to file. The code so far is as follows:-
driver.ExecuteScript("
var url = '" + str + "';
var pages = [],
heights = [],
width = 0,
height = 0,
currentPage = 1;
var scale = 1.5;
PDFJS.disableWorker = true; // due to CORS
PDFJS.getDocument(url).then(function(pdf) {
pdf.getPage(currentPage).then(function(page) {
console.log('Printing ' + currentPage);
var viewport = page.getViewport({
scale: scale,
});
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render(renderContext).then(function() {
pages.push(ctx.getImageData(0, 0, canvas.width, canvas.height));
heights.push(height);
height += canvas.height;
if (width < canvas.width) width = canvas.width;
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
for (var i = 0; i < pages.length; i++)
ctx.putImageData(pages[i], 0, heights[i]);
document.body.appendChild(canvas);
var element = document.createElement('a');
element.setAttribute('href', document.getElementById('rakCanvas').toDataURL('image/png'));
element.setAttribute('download', '" + fileName + "');
console.log('Raky ========>' + url);
element.style.display = 'block';
document.body.appendChild(element);
element.click();
//document.body.removeChild(element);
return 'Done Rak';
});
});
});
Using jsfiddle, I found the JS code to be without any syntax errors. There are no errors during compiling and execution. And there is no file downloaded. To figure out the problem, I am looking for a way to monitor execution of each statement of above js code.
Alternatively, if there is a code chuck that could help in just converting the HTML canvas to Image to be downloaded as a file.

large Images Canvas with FF

i have a Pronlem with large Image Files to display it in a Canvas with FileReader. With small Images it works fine, in Chrome with large images too. But in Firefox it displays nothing and after toDataUrl its gives a black Image. The Image size is 5580 * 8333 (8mb).
If i load the dataUrl from FileReader in a Image Tag it works too.
var reader = new FileReader();
reader.onloadend = function () {
preview.src = reader.result;
console.log(reader.result);
var canvas = document.getElementById("resize");
var ctx = canvas.getContext("2d");
var image = new Image();
image.src = reader.result;
image.onload = function() {
var MAX_WIDTH = 400;
var MAX_HEIGHT = 300;
var tempW = image.width;
var tempH = image.height;
if (tempW > tempH) {
if (tempW > MAX_WIDTH) {
tempH *= MAX_WIDTH / tempW;
tempW = MAX_WIDTH;
}
} else {
if (tempH > MAX_HEIGHT) {
tempW *= MAX_HEIGHT / tempH;
tempH = MAX_HEIGHT;
}
}
image.width=tempW;
image.height=tempH;
canvas.width = tempW;
canvas.height = tempH;
ctx.drawImage(image, 0, 0, tempW, tempH);
};
}
if (file) {
reader.readAsDataURL(file);
} else {
preview.src = "";
}
}
Thanks for help

Multiple images in html5 canvas not stacking in the right sequence

I'm adding multiple png transparent images to an html5 canvas one above the other but they don't stack in the right sequence (defined by the array containing the url of these images)
I read in other discussions that it may be a closure problem but no solution as far.
This is my code:
function drawCanvas() {
var canvas;
var context;
var imgArray = [
'images/img1.png',
'images/img2.png',
'images/img3.png',
'images/img4.png',
'images/img5.png',
'images/img6.png'
];
canvas = document.getElementById('myCanvas');
context = canvas.getContext("2d");
for (var i = 0; i < imgArray.length; i++) {
var img = new Image();
img.src = imgArray[i];
var drawTee = function(i, context, canvas) {
return function() {
console.log(i, 'loaded')
context.drawImage(this, 0, 0);
}
};
img.onload = drawTee(i, context, canvas);
}
};
Can anyone help me?
The problem is that the order of images on the canvas is determined by the order in which the images finish loading.
Solution: Wait for all the images to finish loading and then add them to the canvas
function drawCanvas() {
var canvas;
var context;
var imgArray = [
'images/img1.png',
'images/img2.png',
'images/img3.png',
'images/img4.png',
'images/img5.png',
'images/img6.png'
];
var images = [];
canvas = document.getElementById('myCanvas');
context = canvas.getContext("2d");
var loadCount = 0;
for (var i = 0; i < imgArray.length; i++) {
var img = new Image();
img.src = imgArray[i];
images.push(img);
img.onload = function() {
if(++loadCount == imgArray.length) {
for(var i=0; i < imgArray.length; i++) {
context.drawImage(images[i], 0, 0);
}
}
};
}
};

drag and drop multiple images between canvas(s)

I want to drag and drop multiple images from one canvas to another using HTML5. The tutorial that I found by googling shows how to drag and drop only one image.
Below is the code that I tried. In that I am not able to add multiple images. I am able to drag and drop one image one but not able to add many image to that canvas.
<script>
window.onload = function ()
{
var canvas1 = document.getElementById("cvs1");
var canvas2 = document.getElementById("cvs2");
var context1 = canvas1.getContext('2d');
var context2 = canvas2.getContext('2d');
var imageXY = {x: 5, y: 5};
/**
* This draws the image to the canvas
*/
function Draw ()
{
//Clear both canvas first
context1.clearRect(0,0,canvas1.width,canvas1.height);
context2.clearRect(0,0,canvas2.width,canvas2.height);
//Draw a red rectangle around the image
if (state && state.dragging) {
state.canvas.getContext('2d').strokeStyle = 'red';
state.canvas.getContext('2d').strokeRect(imageXY.x - 2.5,
imageXY.y - 2.5,
state.image.width + 5,
state.image.height + 5);
}
// Now draw the image
state.canvas.getContext('2d').drawImage(state.image, imageXY.x, imageXY.y);
}
canvas2.onclick =
canvas1.onclick = function (e)
{
if (state && state.dragging) {
state.dragging = false;
Draw();
return;
}
var mouseXY = RGraph.getMouseXY(e);
state.canvas = e.target;
if ( mouseXY[0] > imageXY.x
&& mouseXY[0] < (imageXY.x + state.image.width)
&& mouseXY[1] > imageXY.y
&& mouseXY[1] < (imageXY.y + state.image.height)) {
state.dragging = true;
state.originalMouseX = mouseXY[0];
state.originalMouseY = mouseXY[1];
state.offsetX = mouseXY[0] - imageXY.x;
state.offsetY = mouseXY[1] - imageXY.y;
}
}
canvas1.onmousemove =
canvas2.onmousemove = function (e)
{
if (state.dragging) {
state.canvas = e.target;
var mouseXY = RGraph.getMouseXY(e);
// Work how far the mouse has moved since the mousedon event was triggered
var diffX = mouseXY[0] - state.originalMouseX;
var diffY = mouseXY[1] - state.originalMouseY;
imageXY.x = state.originalMouseX + diffX - state.offsetX;
imageXY.y = state.originalMouseY + diffY - state.offsetY;
Draw();
e.stopPropagation();
}
}
/**
* Load the image on canvas1 initially and set the state up with some defaults
*/
state = {}
state.dragging = false;
state.canvas = document.getElementById("cvs1");
state.image = new Image();
state.image.src = 'images/logo.png';
state.offsetX = 0;
state.offsetY = 0;
state.image.onload = function ()
{
Draw();
}
}
</script>
<canvas id="cvs1" width="400" height="125" style="float: left">[No canvas support]</canvas>
<canvas id="cvs2" width="400" height="125" style="float: left; margin-left: 100px">[No canvas support]</canvas>

HTML5 Canvas background image

I'm trying to place a background image on the back of this canvas script I found. I know it's something to do with the context.fillstyle but not sure how to go about it. I'd like that line to read something like this:
context.fillStyle = "url('http://www.samskirrow.com/background.png')";
Here is my current code:
var waveform = (function() {
var req = new XMLHttpRequest();
req.open("GET", "js/jquery-1.6.4.min.js", false);
req.send();
eval(req.responseText);
req.open("GET", "js/soundmanager2.js", false);
req.send();
eval(req.responseText);
req.open("GET", "js/soundcloudplayer.js", false);
req.send();
eval(req.responseText);
req.open("GET", "js/raf.js", false);
req.send();
eval(req.responseText);
// soundcloud player setup
soundManager.usePolicyFile = true;
soundManager.url = 'http://www.samskirrow.com/client-kyra/js/';
soundManager.flashVersion = 9;
soundManager.useFlashBlock = false;
soundManager.debugFlash = false;
soundManager.debugMode = false;
soundManager.useHighPerformance = true;
soundManager.wmode = 'transparent';
soundManager.useFastPolling = true;
soundManager.usePeakData = true;
soundManager.useWaveformData = true;
soundManager.useEqData = true;
var clientID = "345ae40b30261fe4d9e6719f6e838dac";
var playlistUrl = "https://soundcloud.com/kyraofficial/sets/kyra-ft-cashtastic-good-love";
var waveLeft = [];
var waveRight = [];
// canvas animation setup
var canvas;
var context;
function init(c) {
canvas = document.getElementById(c);
context = canvas.getContext("2d");
soundManager.onready(function() {
initSound(clientID, playlistUrl);
});
aniloop();
}
function aniloop() {
requestAnimFrame(aniloop);
drawWave();
}
function drawWave() {
var step = 10;
var scale = 60;
// clear
context.fillStyle = "#ff19a7";
context.fillRect(0, 0, canvas.width, canvas.height);
// left wave
context.beginPath();
for ( var i = 0; i < 256; i++) {
var l = (i/(256-step)) * 1000;
var t = (scale + waveLeft[i] * -scale);
if (i == 0) {
context.moveTo(l,t);
} else {
context.lineTo(l,t); //change '128' to vary height of wave, change '256' to move wave up or down.
}
}
context.stroke();
// right wave
context.beginPath();
context.moveTo(0, 256);
for ( var i = 0; i < 256; i++) {
context.lineTo(4 * i, 255 + waveRight[i] * 128.);
}
context.lineWidth = 0.5;
context.strokeStyle = "#000";
context.stroke();
}
function updateWave(sound) {
waveLeft = sound.waveformData.left;
}
return {
init : init
};
})();
Revised code - currently just showing black as the background, not an image:
// canvas animation setup
var backgroundImage = new Image();
backgroundImage.src = 'http://www.samskirrow.com/images/main-bg.jpg';
var canvas;
var context;
function init(c) {
canvas = document.getElementById(c);
context = canvas.getContext("2d");
soundManager.onready(function() {
initSound(clientID, playlistUrl);
});
aniloop();
}
function aniloop() {
requestAnimFrame(aniloop);
drawWave();
}
function drawWave() {
var step = 10;
var scale = 60;
// clear
context.drawImage(backgroundImage, 0, 0);
context.fillRect(0, 0, canvas.width, canvas.height);
// left wave
context.beginPath();
for ( var i = 0; i < 256; i++) {
var l = (i/(256-step)) * 1000;
var t = (scale + waveLeft[i] * -scale);
if (i == 0) {
context.moveTo(l,t);
} else {
context.lineTo(l,t); //change '128' to vary height of wave, change '256' to move wave up or down.
}
}
context.stroke();
// right wave
context.beginPath();
context.moveTo(0, 256);
for ( var i = 0; i < 256; i++) {
context.lineTo(4 * i, 255 + waveRight[i] * 128.);
}
context.lineWidth = 0.5;
context.strokeStyle = "#ff19a7";
context.stroke();
}
function updateWave(sound) {
waveLeft = sound.waveformData.left;
}
return {
init : init
};
})();
Theres a few ways you can do this. You can either add a background to the canvas you are currently working on, which if the canvas isn't going to be redrawn every loop is fine. Otherwise you can make a second canvas underneath your main canvas and draw the background to it. The final way is to just use a standard <img> element placed under the canvas. To draw a background onto the canvas element you can do something like the following:
Live Demo
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
canvas.width = 903;
canvas.height = 657;
var background = new Image();
background.src = "http://www.samskirrow.com/background.png";
// Make sure the image is loaded first otherwise nothing will draw.
background.onload = function(){
ctx.drawImage(background,0,0);
}
// Draw whatever else over top of it on the canvas.
Why don't you style it out:
<canvas id="canvas" width="800" height="600" style="background: url('./images/image.jpg')">
Your browser does not support the canvas element.
</canvas>
Make sure that in case your image is not in the dom, and you get it from local directory or server, you should wait for the image to load and just after that to draw it on the canvas.
something like that:
function drawBgImg() {
let bgImg = new Image();
bgImg.src = '/images/1.jpg';
bgImg.onload = () => {
gCtx.drawImage(bgImg, 0, 0, gElCanvas.width, gElCanvas.height);
}
}
Canvas does not using .png file as background image. changing to other file extensions like gif or jpg works fine.