What is the proper way to include a closing tag for the canvas in a Canvas rendered by Three.js

Why does three js always add canvas elements without a closing tag to the page? Is there a specific reason for this?

I'm interested in adding a closing tag to this canvas element.

This example page was inspected, revealing something similar to this image: https://i.sstatic.net/1UNyx.png

My reasoning for wanting this closing tag may seem trivial; I am attempting to retrieve the context using:

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

However, it returns null. After trying several solutions with no success, I stumbled upon this answer. This led me to suspect that the missing closing tag could be the issue.

In essence, my goal is to save the canvas as an image using this method.

Answer №1

The answer that has been upvoted by @K3N is correct about one thing: the issue is not with the </canvas> closing tag missing in the inspector, but rather with attempting to obtain a 2D Context from a canvas where a webgl context was already initialized.


However,

While it's true that certain elements in HTML5 do not require a closing tag, such as some elements, for canvas

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

Here is an example disproving this notion using another fiddle. While <script> content should be executed immediately upon being encountered, and modern browsers make the canvas element available in the DOM before reaching the closing tag, enabling these browsers to execute the script, (although IE9 may not) this practice is generally discouraged :

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

Therefore, despite what your inspector shows you, or potentially internally within your browser, it is imperative that you include it in your markup.
(In fact, particularly with Chrome, only the inspector doesn't display it - you can view it by accessing the outerHTML property of the canvas element, where the closing tag will be visible.)

And now, onto the solution,

You are aiming to export the canvas as an image file.
To do so, since toBlob is a method of the HTMLCanvasElement, you do not require the current context. Simply follow the final steps outlined in the answer referenced in your question, with minor adjustments :

As elucidated in this particular answer given by gman, a webGL canvas operates on a double-buffer system, resulting in the drawing buffer being cleared promptly by the browser, unless the context was established utilizing the preserveDrawingBuffer: true argument in the getContext() method.
Nevertheless, implementing this argument can severely impact performance, hence why the preferred approach would involve executing the toBlob() method synchronously within the same rendering call.

Since the aforementioned details have been comprehensively explained, I shall refrain from further elaboration on this topic. However, do take note that this principle also pertains to methods like toDataURL() and

some2dCanvas.drawImage(yourWebglCanvas,x,y)
.

Answer №2

The issue you're currently facing is not with the canvas closing tag.

In Three.js, it utilizes a webgl context (whenever possible) with the canvas, making it impossible to obtain a 2d context. According to the specifications, if a context like webgl was obtained and 2d context is requested, it will:

Return null.

(Check out this jsfiddle)

https://i.sstatic.net/nYRSX.jpg

To tackle this, you'll need to create a new canvas (which doesn't have to be included in the DOM), and then acquire a 2D context from it. After that, use drawImage() on your 2D context by using the webgl canvas element as the image source into your new canvas. This way, you should be able to extract a bitmap/image from it.

Answer №3

Recently, I've been working on a similar project.

Here are a few key points to consider:

  1. The Elements tab in the browser might not always show closing tags correctly (I experienced this with a closed canvas appearing open).
  2. In the example you mentioned, there is a broken tag rather than a missing one.
  3. After some testing, it seems that obtaining a 2d context for a canvas already equipped with a webgl context, or vice versa, may lead to failure. However, getting a 2d context for a canvas with a 2d context works fine, and the same goes for webgl.

Consider these suggestions:

You could utilize getElementsByTagName('canvas'). If there's only one canvas, your problem is solved. Otherwise, search through the results for the specific canvas you need.

Alternatively, you can attempt something like this (adapted from How to Copy Contents of One Canvas to Another Canvas Locally):

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

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

// Drawing the webgl canvas to a 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 you can manipulate the 2d canvas 'destCtx' as needed

Hope this helps!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Decoding JSON in AngularJS

Looking for assistance in parsing a JSON 2D array with Angular JS and fetching images from the array. Can anyone provide some guidance on this? I have updated my Plunker at this link HTML Code <!DOCTYPE html> <html lang="en" ng-app="myAp ...

Guidelines on resolving the issue of Unsupported platform for [email protected]: requested {"os":"darwin","arch":"any"} (existing: {"os":"win32","arch":"x64"})

Trying to install Parallelshell but encountering a persistent warning. I've checked the package file multiple times without finding a solution. Can someone assist me with this issue? ...

"Once the initial date has been selected, v-Calendar's datepicker allows for setting a

Is there a way to trigger an event for the date range picker of v-calendar after the first date is picked or prevent the inputs from adding the dates until both dates have been selected? Here is the Vue component I have: new Vue({ el: "#app", data( ...

Extracting data from websites using Python's Selenium module, focusing on dynamic links generated through Javascript

Currently, I am in the process of developing a webcrawler using Selenium and Python. However, I have encountered an issue that needs to be addressed. The crawler functions by identifying all links with ListlinkerHref = self.browser.find_elements_by_xpath( ...

The user type is not yet loaded from Firestore when the view is rendered

I am currently in the process of developing an Ionic - Angular application that allows hospital patients to submit requests to nursing staff, who can then view the assigned requests based on the patient's room. Nurses have access to all requests, whil ...

"Incorporating splice in a for loop is causing issues and not functioning

I've been attempting to remove an item from an array, but for some reason, it's not working. Here's the code I'm using: vm.Continue = function () { $scope.invalidList = []; if (vm.errorexsist === true) { var table = doc ...

A step-by-step guide on importing a gltf model into Three.js

Recently, I decided to experiment with the gltf format for rendering models in Three.js. To convert my model from Collada to GlTF, I've been using this converter tool available at . The output is a *.gltf file, but the examples in Three.js typically u ...

Identify modifications to properties in Angular

What is the most effective method for responding to property changes within a component? I am seeking a solution that will trigger a specific function every time a property in a component is altered. Consider the following example with a single component: ...

javascript box is hidden to prevent displaying of values

I need the negative character to disappear when the user clicks the minus (-) button. The count should stop at 0. Currently, it is displaying characters like -1, -2, -3. I only want to show 0 or numbers greater than 0. <script type="text/javascript" ...

Output a variable that is generated from invoking an asynchronous function

I'm currently in the process of developing an application that is going to leverage the capabilities of a SOAP server through the use of the https://github.com/vpulim/node-soap module. One of the main challenges I am facing is how to efficiently crea ...

The sendKeys() method in Protractor is failing due to the element being hidden/not

Hi there! I am currently new to automated testing with protractorJS for an angularJS homepage. While the code I've written so far has been successful, I'm facing an issue where I'm unable to input keys into the search field. After running th ...

Unable to choose anything within a draggable modal window

Can someone please assist me? I am struggling to figure this out. I can't select text inside the modal or click on the input to type text into it. I suspect it may be due to z-index issues, but I'm having trouble locating them. var currentZ = n ...

What steps are involved in deploying a Vue.js application on a tomcat server?

Is it possible to deploy a vue.js application on a server using tomcat? If so, how can this be done? ...

Determining the percentage of a bar in d3.js when hovering over it

I am working with a d3 chart and looking to implement a tooltip feature that displays the label of the bar section along with the percentage it represents in relation to the total bar. Initially, I considered calculating the height of the hovered section t ...

Access to JSON.stringify is prohibited

I have an array containing objects in JavaScript that I need to save as a .json file. Prior to saving the objects, I displayed them using console.log. // Client Object {id: "1", color: "#00FF00"} Object {id: "2", color: "#FF7645"} Object {id: "3", color: ...

What is the best way to display a placeholder instead of the state's value when a page loads?

I'm having trouble displaying the placeholder of an input instead of its state value. When the page loads, it shows an empty select box because the testType state is null initially. How can I make sure that only the placeholder is shown instead of the ...

Creating a PDF file from a series of images

I've implemented a function using the jsPDF library to generate a PDF from a list of images. The main task is to add these images to the PDF document. Here is the code snippet: const { allImgs } = useAppContext() const doc = new jsPDF(); const gener ...

Can a package-lock.json file be created without running an npm install command?

Is there a way to create only a package-lock.json file without having to redo the npm install process? I'm looking to generate the lock file without reinstalling any of the package files. Is this achievable? ...

How can I dynamically change a key in an object and replace it with a custom name while also updating its value?

Transform this =>>>>> {1: "Baroque", 2: "Glitch Pop ", 3: "Nu Jazz", 4: "Drumfunk", 5: "Bitpop", 6: "Latin Pop", 7: "Carnatic"} into this ==>>>> [{id: 1 name ...

Having trouble capturing a photo from webcam using HTML5 and getUserMedia() in Google Chrome upon initial page load

Using the information from an article on HTML5Rocks, I am attempting to create a tool that can capture photos from a webcam. Here is an excerpt of my HTML code: <button type="button" name="btnCapture" id="btnCapture">Start camera</button>< ...