Arun Xavier
Arun Xavier

Reputation: 771

How to add closing tag for canvas in three js rendered Canvas?

three js always adds canvas elements without a closing tag to the page, is there any specific reason behind that?

I want to add a closing tag on this canvas element.

Example page , inspecting the page shows, some thing like this. enter image description here

Why I want this closing tag might be silly, I am trying to get the context using

context = canvas.getContext('2d');

But it returns null , so tried some solutions here, none worked and Came across this answer, So I suspect the missing closing tag may be an issue.

What actually I am trying to do is save the canvas to an image using this.

Upvotes: 4

Views: 1083

Answers (3)

user1693593
user1693593

Reputation:

The canvas closing tag isn't the issue you're facing in this case.

Three.js is using webgl context (if it can) with the canvas which means a 2d context cannot be obtained. The specs do state that if a context was obtained, in this case webgl, then if 2d is requested it shall:

Return null.

(jsfiddle)

table snapshot

This means you will have to create a new canvas (does not have to be inserted into DOM) and then obtain a 2D context from that. Then use drawImage() on your 2D context using the webgl canvas element as image source into your new canvas. Then you should be able to extract a bitmap/image from that.

Upvotes: 2

Kaiido
Kaiido

Reputation: 136796

@K3N's upvoted answer's is right about one point : your problem doesn't come from the </canvas> closing tag not shown in the inspector, but from your trial to get a 2D Context from a canvas where a webgl one was already initialized.


But,

While it's true that in HTML5 some elements don't need a closing tag, for canvas

Tag omission in text/html:
      Neither tag is omissible.

Here is a small counter-proof to the other fiddle, since yes, <script> content should be executed directly when met, and that modern browsers make the canvas element available in the DOM before they met the closing tag, these browsers are able to execute this simple script, (IE9 won't be) but this is certainly something you want to avoid :

canvas{border: 1px solid}
<canvas>
<p>I'm not there</p>

So it's just your inspector, or maybe internally your browser which doesn't show it, but you must write it in your markup.
(Actually, if we're talking about chrome, it's only the inspector which doesn't show it, you can see it by calling the outerHTML property of the canvas element, the closing tag will be there.)

And for the solution,

What you are trying to do is to export the canvas as a static image.
For this, since toBlob is an HTMLCanvasElement's method, you don't need the current context , you only need the last part of the answer you pointed us to in your question, and small adjustements :

As well explained in this answer by gman, webgl canvas is double buffered, and the drawing buffer will be cleared as soon as the browser can, except if the context was created with the preserveDrawingBuffer: true argument of the getContext() method.
But this argument will have a serious effect on performances, and the best recommended way, is to call the toBlob() method, in the same call as your rendering, synchronously.

Since it is already really well explained there, I won't extend too much on this subject, but note that it also concerns toDataURL() and some2dCanvas.drawImage(yourWebglCanvas,x,y).

Upvotes: 3

CarlBateman
CarlBateman

Reputation: 167

I've just been trying to do something similar.

First a couple of points:

  1. The Elements tab may incorrectly omit closing tags (this I know because I have a correctly closed canvas but Elements shows it open).
  2. The example you refer to has a broken tag, not a missing tag.
  3. Your jsfiddle still fails when the canvas tag is closed - experimentation indicates that you can't get a 2d context for a canvas that already has a webgl context and vice versa (although you can get a 2d context for a canvas with a 2d context, and likewise for webgl).

Some suggestions:

You could use getElementsByTagName('canvas'). If you only have one canvas, problem solved, otherwise just search the results for the required canvas.

Alternatively, you could try something along these lines (adapted from How to Copy Contents of One Canvas to Another Canvas Locally):

//Append the renderer to a div
div.appendChild(renderer.domElement);
div.id = "renderTarget";

// first child element of div will be canvas
var canvas = div.firstElementChild;

// draw webgl canvas to 2d canvas
var destCtx = document.getElementById("copy").getContext('2d');
destCtx.canvas.height = canvas.height;
destCtx.canvas.width = canvas.width;
destCtx.drawImage(canvas, 0, 0, canvas.width, canvas.height);

// now do what you like with the 2d canvas destCtx

HTH

Upvotes: 1

Related Questions