CanvasRenderingContext2D drawImage() not correctly working in Chrome - javascript

I am currently working on a web-application. As part of it I am creating SVG-images internally. When it comes to rendering, I used the approach provided in this question in order to convert the svg to a bitmap and displaying it on a canvas: https://stackoverflow.com/a/23667012/5767042
Most of the times this works fine, however sometimes it does not render the SVG image. Using the chrome DevTools, I was able to confirm that the object being passed to drawImage() (in the example below this would be imageBitmap) is the same in every call, however sometimes it gets rendered and sometimes it does not. I don't understand this behaviour at all.
var svgStr = "<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"100\" height=\"100\" shape-rendering=\"crispEdges\" stroke-linecap=\"square\"><line x1=\"0\" y1=\"0\" x2=\"50\" y2=\"50\" stroke=\"rgb(0,0,0)\" stroke-width=\"4\"\/><line x1=\"0\" y1=\"50\" x2=\"50\" y2=\"0\" stroke=\"rgb(0,0,0)\" stroke-width=\"4\"\/><\/svg>";
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var imageBitmap = new Image();
imageBitmap.src = 'data:image/svg+xml;base64,'+window.btoa(svgStr);
console.log('drawing bitmap');
ctx.drawImage(imageBitmap, 0, 0);
<canvas id="myCanvas" width="200" height="100"></canvas>
Am I using drawImage() in a wrong way? I had the same behaviour in Firefox, Edge doesn't work at all. I also tested it on three different machines, no difference.

You need to wait until the image is finished loading. For that you can use the .onload handler of the image, which is called when it's loaded.
var svgStr = "<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"100\" height=\"100\" shape-rendering=\"crispEdges\" stroke-linecap=\"square\"><line x1=\"0\" y1=\"0\" x2=\"50\" y2=\"50\" stroke=\"rgb(0,0,0)\" stroke-width=\"4\"\/><line x1=\"0\" y1=\"50\" x2=\"50\" y2=\"0\" stroke=\"rgb(0,0,0)\" stroke-width=\"4\"\/><\/svg>";
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var imageBitmap = new Image();
imageBitmap.src = 'data:image/svg+xml;base64,' + window.btoa(svgStr);
function drawBitmap() {
console.log('drawing bitmap');
ctx.drawImage(imageBitmap, 0, 0);
}
imageBitmap.onload = drawBitmap;
<canvas id="myCanvas" width="200" height="100"></canvas>

Related

Import "img.svg" into Canvas already used by WebGL

I am drawing a 2D Simulation into a Canvas using WebGL and JavaScript
let gl = c3d.getContext('webgl', {preserveDrawingBuffer: true});
I want to add my company-logo "logo.svg" in the corner of my simulation. I wanted to ask what the best possible solution for my porblem is. I am wondering if i need to write another shader and do all the rendering on every simulation step. Or if there is a simple way to import the svg into the Canvas. I need both the simulation and logo to be in the same canvas because i am downloading the end Result as PNG/JPG.
This is not a WebGL solution but you could modify your download code to use a second canvas to attach the logo:
var c3d = document.getElementById('c3d');
var c2d = document.getElementById('c2d');
var gl = c3d.getContext('webgl');
var c2 = c2d.getContext('2d');
gl.clearColor(1,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Base64 encode WebGL output
var glImage = new Image();
glImage.src = c3d.toDataURL('image/png');
// Base64 encode company logo
var logoImage = new Image();
logoImage.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='50' height='50'%3E%3Crect width='50' height='50' style='fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)' /%3E%3C/svg%3E";
// Combine both images on a canvas
c2.drawImage(glImage, 0, 0);
c2.drawImage(logoImage, 0, 0);
// Here you will download the image as before, but I'm just
// appending it as an img to show that the result is correct
var finalImage = new Image();
finalImage.src = c2d.toDataURL('image/png');
document.body.append(finalImage);
<canvas id="c3d"></canvas>
<canvas id="c2d"></canvas>

How to copy data from one canvas to another

I have forms with arbitrary numbers of canvases. Once one is filled out I would like to copy it to any other canvas that is interacted with.
I have currently
When one canvase is closed I save the canvas data
sourceCanvas = $(this).find('canvas')[0];
Then when the next one loads (they are in modals)
I try to populate it like this
var destCtx = $(this).find('canvas')[0].getContext('2d');
destCtx.drawImage(sourceCanvas, 0, 0);
I am getting this error
Uncaught
TypeError: Failed to execute 'drawImage' on
'CanvasRenderingContext2D': The provided value is not of type
'(CSSImageValue or HTMLImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
Is ('canvas')[0] the wrong thing to grab? should I be getting something else in the canvas element?
Thanks!
When one canvase is closed I save the canvas data
sourceCanvas = $(this).find('canvas')[0];
I think that your code is not accessing the image data in the source canvas.
Call getImageData() on the context of the source canvas then get call putImageData() on the context of the destination canvas
.
Below is a simple example which copies the image data from canvas1 into both canvas1 (at a different location for illustration) and also into canvas2.
Also, check out this excellent text on HTML5 Canvas.
<html>
<body>
<canvas id="canvas1" width="300" height="150" style="border:1px solid #f00;">no canvas</canvas>
<canvas id="canvas2" width="300" height="150" style="border:1px solid #00f;">no canvas</canvas>
<script>
var c1 = document.getElementById("canvas1");
var c2 = document.getElementById("canvas2");
var ctx1 = c1.getContext("2d");
var ctx2 = c2.getContext("2d");
ctx1.fillStyle = "red";
ctx1.fillRect(10, 10, 50, 50);
function copy() {
var imgData = ctx1.getImageData(10, 10, 50, 50);
ctx1.putImageData(imgData, 10, 70);
ctx2.putImageData(imgData, 10, 70);
}
</script>
<button onclick="copy()">Copy</button>
</body>
</html>

Canvas image moves slightly to right onload

I am working with the signature_pad lib to accept signature for my web app. I am having an issue after loading a saved signature.
Assuming I am working with a blank signaturePad, I create some signature and save it.
After refreshing the page, this is what shows up.
Notice it moves to the right and also seems like its zoomed in slightly.
Any idea why this may be happening? Here is some code I am working with:
<canvas class="signature-canvas" height="150" width="500" id="signatureCanvas"></canvas>
var canvas = document.querySelector("canvas#signatureCanvas");
scope.signaturePad = new SignaturePad(canvas);
scope.c2 = document.createElement('canvas');
scope.ctx = scope.c2.getContext('2d');
scope.img = new Image();
scope.img.crossOrigin = "Anonymous";
scope.img.onload = function() {
scope.drawSig();
}
scope.img.src = scope.signature;
scope.drawSig = function(){
scope.ctx.drawImage(scope.img, 0, 0);
scope.imgStr = scope.c2.toDataURL("image/png", "");
scope.signaturePad.fromDataURL( scope.imgStr );
};

How to modify an image with canvas

In my html I have an image already loaded:
<img usemap="#prototypeMap" src="../../projects/tcas/TCAS display.jpg" style="z-index: 2;">
Is it possibile create a canvas to modify that image, by javascript?
For example drawing a line in it, changing colours in it (for some pixels) and so on...
EDIT:
I have found a method on Internet but it doesn't works good for me:
var imgElement = document.getElementById('prototypeMap');
var canvas = document.createElement("canvas");
canvas.width = imgElement.offsetWidth;
canvas.height = imgElement.offsetHeight;
var ctx = canvas.getContext("2d");
ctx.drawImage(imgElement,0,0); //ERROR
The last line give me this error:
TypeError: Value could not be converted to any of: HTMLImageElement, HTMLCanvasElement, HTMLVideoElement.
You can try using SVG image tag:
https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_Image_Tag
And then use the other SVG elements to draw on top.

Why not transfer the image from the canvas

I'm trying to convert an image from the canvas to the image, which can be saved by right-clicking mouse.
Everything works fine, but if I put Image on the canvas (drawImage), the image is not transferred.
Image on the left is, and it is not right.
Why?
I also put an example in the sandbox. http://jsfiddle.net/qS9qP/
<body>
<canvas id="myCanvas" width="200" height="200"></canvas>
<img src="f2.ico"/>
</body>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
var img=new Image();
img.onload = function(){
ctx.drawImage(img, 10, 10);
}
img.src="http://www.cisco.com/favicon.ico"
// transfer canvas to image
document.images[0].src=document.getElementById("myCanvas").toDataURL("image/png");
</script>
toDataURL executes before the picture gets loaded. Try putting it in the onload function.
Also, you can not use images from other domains because of the Same Origin Policy, and it will throw a SecurityError:
Uncaught Error: SecurityError: DOM Exception 18
Now this will work: http://jsfiddle.net/DerekL/qS9qP/2/show/
var img = new Image();
img.onload = function () {
ctx.drawImage(img, 10, 10);
document.images[0].src = canvas.toDataURL("image/png"); //Put it inside
}
img.src = "http://jsfiddle.net/img/logo.png" //Same domain
#Derek and #robertklep are correct:
the image is drawn after the toDataURL call; and then
SecurityError: DOM Exception 18
The cross-domain problem is somewhat beyond the scope of this question, but to demonstrate the point, this can be made to work as you expect (by eliminating the DOM Exception):
http://jsfiddle.net/c24w/E3SPv/

Categories

Resources