Why toDataURL() not working when I draw image on canvas? - html

I have canvas and an <img> element in my page and I am simply trying to clone/copy what ever is drawn on the canvas to the img element. I don't have problem as long as I use drawing commands alone and clone the image but when I draw an image then it won't clone anything.
html:
<canvas id="canvas" width="600" height="400">Default Text (No JS)</canvas>
<img id="img" alt="This is dynamic image" src="https://www.gravatar.com/avatar/c585e7964ff782b11a41d19679e43e9d?s=48&d=identicon&r=PG&f=1" style="position:fixed;top:0;right:400px">
A simple HTML page with a canvas in the left and image element in the right side.
js:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var todo = 0;
drawImage(); //commenting this would clone the canvas
drawCloud();
checkAllDone();
function checkAllDone(){
if (this.todo ==0) copyCanvas();
}
function drawImage(){
this.todo++;
var img = new Image();
img.onload = function(){
context.drawImage(img,0,0,200,200);
this.todo--;
checkAllDone();
};
img.src = 'http://i.stack.imgur.com/RxFwQ.png?s=64&g=1';
}
function drawCloud(){
context.beginPath();
context.moveTo(170, 80);
context.bezierCurveTo(130, 100, 130, 150, 230, 150);
context.bezierCurveTo(250, 180, 320, 180, 340, 150);
context.bezierCurveTo(420, 150, 420, 120, 390, 100);
context.bezierCurveTo(430, 40, 370, 30, 340, 50);
context.bezierCurveTo(320, 5, 250, 20, 250, 50);
context.bezierCurveTo(200, 5, 150, 20, 170, 80);
context.closePath();
context.lineWidth = 5;
context.fillStyle = '#8ED6FF';
context.fill();
context.strokeStyle = '#0000ff';
context.stroke();
checkAllDone();
}
function copyCanvas(){
var img = document.getElementById('img');
img.src = canvas.toDataURL();
//img.src = context.getImageData();
}
Now this is very simple script where there are mainly three functions
drawImage(): This will draw an image into the context
drawCloud(): This draws a shape using drawing commands which looks like cloud
copyCanvas(): This one liner is expected to clone the image and what ever raster drawings drawon on the canvas is put as an image and works as source to the <img> element.
Twist is that when I draw both Image and Cloud, copyCanvas won't work as expected. The image element doesn't copy canvas content. Where as if I comment out drawImage then it properly copies canvas contents. Why is that?

Regarding your code, these two this are actually window:
function checkAllDone(){
if (this.todo ==0) copyCanvas();
}
function drawImage(){
this.todo++;
// ......
While the one inside img.onload is indeed img:
img.onload = function(){
context.drawImage(img,0,0,200,200);
this.todo--;
checkAllDone();
};
So window.todo (todo you declared in line 3) is always 1 when checkAllDone runs, and it obviously fails.
Read more about this, notice the difference between function context variations.
Since you have declared a global todo indicator, the simplest fix is to use todo instead of this.todo.
However, after fix you will encounter another problem:
Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
which you could find an answer here.

Related

html5 canvas text slide in behind nothing

I want to have html5 canvas text slide in behind nothing, its an effect where text appears like coming out behind some obstacle, but the obstacle is invisible.
Here is some youtube video showing how to do it in after effect:
https://www.youtube.com/watch?v=KIYMy7vLLRo
I know how to slide a text along canvas, and one idea I got is having two canvases on top of each other, and top canvas is smaller and contains the text that is initially out of canvas and slides in. But if there is a way to do it with just one canvas that would be great.
You can using a clipping path to mask out part of the text.
Save the existing clipping path using the save() method. Create a shape/path and make it the new clipping path using the clip() method. Draw your text. Store the previous clipping path using the restore() method.
For example, suppose your canvas is 100 pixels by 100 pixels. The following will draw text on only the left side of the canvas.
context.save();
context.rect(0, 0, 50, 100);
context.clip();
context.fillText("Draw some text.", 30, 50);
context.restore();
jsFiddle : https://jsfiddle.net/CanvasCode/vgpov2yk/3
var c = document.getElementById('myCanvas');
var ctx = c.getContext("2d");
// Positions for the text startint off
var xText = -100;
var yText = 150;
// This will update the canvas as fast as possible (not good)
setInterval(function () {
// Clear the canvas
ctx.fillStyle = "#F0F";
ctx.fillRect(0, 0, c.width, c.height);
ctx.save()
// We create a clip rect
ctx.rect(200,0,400,400);
ctx.clip();
// Draw text
ctx.fillStyle = "#FFF";
ctx.font = "30px Arial";
ctx.fillText("Hello World", xText, yText);
ctx.restore();
// Increase the text x position
if (xText < 200) {
xText += 0.5;
}
}, 15);
All you have to do is use a clip rect which is like a mask in image editing.

Using globalCompositeOperation in few phases

I'm drawing many things on my context. Shapes, texts, images..
I want to use achieve the same effect i'm getting using the clip method on the context with globalCompositeOperation (Using the clip is harder for me to perform and i don't know if possible for texts)
The user can draw few shapes. and then start a mask phase. to draw some more shapes, texts.. which would draw into the mask and then the next draw will be clipped in the masked phase. and then continue to regular drawing...
For ex.
The user draw this drawing
Then started masked mode and drew this 2 red lines
Then he stopped drawing into the mask, and start drawing rectangle to consider the mask
And finally applied the mask clipping and the result should look like this
I've managed to clip the rectangle with the lines if there were no earlier drawings.
// Starting the mask phase
ctx.strokeStyle = 'red';
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(240, 140);
ctx.moveTo(80, 20);
ctx.lineTo(300, 140);
ctx.stroke();
ctx.globalCompositeOperation = 'source-out';
ctx.fillStyle = 'cyan';
ctx.fillRect(50, 70, 250, 20);
// Setting the composition back
ctx.globalCompositeOperation = 'source-over';
but when i'm adding my drawings in the beginning of the code, the composition considering it as well.
ctx.fillStyle = 'brown';
ctx.beginPath();
ctx.arc(80, 80, 50, 50, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'yellow';
ctx.fillRect(80, 60, 150, 40);
ctx.fillStyle = 'black';
ctx.font = '40pt arial';
ctx.fillText('Hello', 130, 110);
// How to tell the context to start from here the compisition ???
How to tell the context to start composition from a certain point, if possible ?
I could create another canvas and draw the mask there.. and then draw the new canvas on the main canvas. But there is better solution ?
You can change the compositing mode at any point in the drawing flow by changing .globalCompositeOperation. But, as you've discovered, any new compositing mode will also affect existing canvas content.
Your intuition is correct about using a second "staging canvas" to do compositing that won't destroy your existing content.
You can use an in-memory canvas to do compositing and create your rect-with-erased-lines. Then you can drawImage this in-memory canvas to your main canvas. Since the compositing was done on your in-memory canvas, your existing circle-hello content is not undesirably affected by compositing.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// create an in-memory canvas to do compositing
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/temp6a.png";
function start(){
ctx.drawImage(img,0,0);
addCompositedRectangle();
ctx.drawImage(c,0,0);
}
function addCompositedRectangle(){
// resize the in-memory canvas
// Note: this automatically clears any previous content
// and also resets any previous compositing modes
c.width=300; // largest width;
c.height=140; // largest height;
// Starting the mask phase
cctx.strokeStyle = 'red';
cctx.lineWidth = 5;
cctx.beginPath();
cctx.moveTo(20, 20);
cctx.lineTo(240, 140);
cctx.moveTo(80, 20);
cctx.lineTo(300, 140);
cctx.stroke();
cctx.globalCompositeOperation = 'source-out';
cctx.fillStyle = 'cyan';
cctx.fillRect(50, 70, 250, 20);
// Setting the composition back
cctx.globalCompositeOperation = 'source-over';
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

does not support HTML5 Canvas element?

The following code creates a Canvas element in an HTML page,
I Have Use this code but Error is shown bellow
<canvas id="exmpl" width="300" height="300">
This is Canvas Element..
</canvas>
Using JavaScript, I have draw on the canvas:
var exmpl= document.getElementById('exmpl');
var context = example.getContext('2d');
context.fillStyle = 'red';
context.fillRect(30, 30, 50, 50);
This code draws a red rectangle on the screen.
This code is perfect always use higher level version if you use canvas tag.
var exmpl= document.getElementById('exmpl');
var context = example.getContext('2d');
context.fillStyle = 'red';
context.fillRect(30, 30, 50, 50);

Drawing repetitively one canvas without that edges become dark

I have this code that for some reason it should draw canvas many times (using setTimeout):
function drawImage() {
var img = new Image();
var canvas = document.getElementById("event");
var context = canvas.getContext("2d");
img.src = "../img/event.png";
img.onload = function () {
context.drawImage(img, 0, 0);
}
setTimeout(drawImage, 3000);
}
but it cause dark edge as draw more times as this:
Is it possible to prevent edges become dark in repetitively drawing?
As long as the image has an alpha-channel you cannot avoid this when re-painting it over and over without some mechanism to reset the alpha values drawn.
The reason for this is that the alpha-value for that pixel will accumulate so when you draw a non-opaque edge (or anti-aliased edge) on top of each other the value for the alpha channel will be added to the value already drawn resulting in that the edge will be more and more visible.
There are fortunately a couple of ways to avoid this:
A) If you want to keep the alpha-channel in the image use clearRect before drawing the image.
context.clearRect(0, 0, img.width, img.height);
context.drawImage(img, 0, 0);
This will clear the canvas and the alpha channel.
ONLINE DEMO
B) If alpha channel is not important save out the image without any alpha-channel (use PNG with transparency off or use JPEG).
Also a note to your loop in the example - this is not a good way to redraw an image as you are initiating a load/cache check as well as image decoding each time.
You can modify your code as this (adopt as you please):
var canvas = document.getElementById("event");
var context = canvas.getContext("2d");
var img = document.createElement('img'); // due to chrome bug
img.onload = drawImage; // set onload first
img.src = "../img/event.png"; // src last..
function drawImage() {
context.clearRect(0, 0, img.width, img.height);
context.drawImage(img, 0, 0);
setTimeout(drawImage, 3000);
}

Adding Kinetic JS to PDF file

I'm trying to add KineticJS shapes to an existing pdf file using pdf.js. The problem I have is that KineticJS creates a new canvas and won't use the canvas created by pdf.js. I've tried referencing the pdf.js canvas in KineticJS but that won't work. Any Ideas in how to integrate the two? The main purpose is to provide annotations to the pdf.
If you’re not too far into the process, you might try Bytescout which generates .pdf and also includes a hook to load your canvas image into your pdf.
Everything is done on the client-side, so you can view/edit/view/edit to your heart’s content.
They are here: http://bytescout.com/products/developer/pdfgeneratorsdkjs/index.html (BTW, No sales pitch here--I’m not connected to bytescout in any way!)
Here’s an example from their website that shows an embedded canvas:
function CreatePDF() {
// create BytescoutPDF object instance
var pdf = new BytescoutPDF();
// set document properties: Title, subject, keywords, author name and creator name
pdf.propertiesSet("Sample document title", "Sample subject", "keyword1, keyword 2, keyword3", "Document Author Name", "Document Creator Name");
// set page size
pdf.pageSetSize(BytescoutPDF.A4);
// set page orientation (BytescoutPDF.PORTRAIT = portrait, BytescoutPDF.LANDSCAPE = landscape)
pdf.pageSetOrientation(BytescoutPDF.PORTRAIT);
// add new page
pdf.pageAdd();
// create temporary canvas (you can also simply get existing canvas)
var canvas = document.createElement('canvas');
// set width and height
canvas.width = 320;
canvas.height = 240;
// get context from canvas (to draw on)
var context = canvas.getContext("2d");
// set white background color
context.fillStyle = "#FFFFFF";
// and fill the background with white color
context.fillRect(0, 0, 320, 240);
// draw the yellow circle
context.strokeStyle = "#000000";
context.fillStyle = "#FFFF00";
context.beginPath();
context.arc(100, 100, 50, 0, Math.PI * 2, true);
context.closePath();
context.stroke();
context.fill();
// load image from canvas into BytescoutPDF
pdf.imageLoadFromCanvas(canvas);
// place this image at given X, Y coordinates on the page
pdf.imagePlace(20, 40);
// return BytescoutPDF object instance
return pdf;
}