Canvas clears on parent element attribute change - javascript

I create a canvas on the fly from a video. This works great and the canvas is correct and shall never be changed. So what I do I wrap the canvas in 2 divs. Now I change any attribute, even when I call element.setAttribute('test',''), via js on one of the 3 elements and the canvas clears.
I have sadly no clue why this is happening. I only want to set the positioning of the direct canvas parent, and its parent shall toggle between display: none and display: block
Any idea why this is happening and how I can prevent it?
[EDIT] I found the problem:
To append the canvas at the correct position I changed
container.innerHTML = ''
container.appendChild(canvas)
to
container.innerHTML = canvas.outerHTML
The later is initially fine, however when I change something on any attribute of the canvas, the container or parent the canvas content clears.
Why is this? Shouldn't the code do the same thing?
Tested in Firefox, Chrome & Chromium

It's not the attribute that causes the problem, but the canvas you create by doing
container.innerHTML = canvas.outerHTML
is not the same canvas anymore, but an entirely new canvas element, which doesn't have any context initialized, and which will be fully transparent.
const canvas = document.createElement('canvas');
const output = document.getElementById('output');
output.append( canvas );
console.log(
'same canvas with append?',
document.querySelector('#output>canvas') === canvas
); // true
document.getElementById('output').innerHTML = canvas.outerHTML;
console.log(
'same canvas with innerHTML?',
document.querySelector('#output>canvas') === canvas
); // false
<div id="output"></div>
So if you want to keep the drawings of your canvas, you need to keep that same element in the DOM, and for this you need to append it.

Related

Cannot remove canvas elements from memory that are created but never appended to the page

I am creating a canvas element like this:
var canvas = document.createElement('canvas');
I use this canvas object to get the context, and from there, use information from the context for further logic.
var context = canvas.getContext('2d');
...
// More logic
I never append the canvas element to the document, or any other element, but using the safari devtools -> Canvas tab, I can see that the canvas elements are constantly being created whenever this code is run. They are about 200KB each, and there can be hundreds, thousands of these canvas elements created in one session. If the page is reloaded, they are removed from memory, and no longer appear in the canvas tab.
However, if the page is not reloaded and the canvas' build up, you can see a dip in performance.
So, how can I remove these canvas elements from memory if they have never been appended to the page?
See below a picture of the canvas tab (only works in Safari as newer versions of Chrome and Firefox don't support it). The first canvas object is expected, the other ones I would like to remove. I am using javascript and jquery.
The problem comes from the very canvas dev-tool.
It is the one culprit that will keep references to these canvas elements and prevent the Garbage-Collector to do its work correctly.
As a proof, try to run the following snippet, first with the dev-tools closed, then with it open on the "canvas" tab.
var canvas, context;
for (let i=0; i<100; i++) {
canvas = document.createElement( 'canvas' );
canvas.width = canvas.height = 5000;
context = canvas.getContext( '2d' );
}
console.log( 'done' );
When the dev-tools are closed, it runs seamlessly on Safari.
Opening it after the "done" message appeared will reveal that a single canvas is active on the page.
However with the dev-tools open, it will start to stutter until some message appear in the console.
"[Warning] Total canvas memory use exceeds the maximum limit ..."
And in the canvas panel, we'll have all the ones generated before this message pops out marked as active.
So to avoid it, avoid creating so much canvases while this tool is open.
Note that according to this answer, as originally proposed by user minimo, setting the width and height attributes of the canvas after use will minimize this issue, which makes perfect sense since now the browser only has a 0 x 0px buffer to keep in memory.

Charts.js sets canvas width/height to 0 and displays nothing

I am trying to use Charts.js to make the default line plot that they show in their example dynamically and put it in a div that I pop up on a user click. My code is like this:
this.chartCanvas = document.createElement('canvas');
this.div.appendChild(this.chartCanvas);
this.chartCanvas.style.height = '480px';
this.chartCanvas.style.width = '900px';
this.chartCanvas.width = 900;
this.chartCanvas.height = 480;
this.ctx = this.chartCanvas.getContext('2d');
this.chart = new Chart(this.ctx).Line(data);
When I make the call to "new Chart" my canvas height and width are set to 0 as I can see in the inspector. When I comment out this call my canvas has the proper width/height and displays as one would expect. If I manually change the canvas height/width in the inspector my chart still doesn't display.
My "data" object is just what I cut and paste directly from their line chart example here: http://www.chartjs.org/docs/#line-chart-example-usage
Can anyone provide some insight on where I might be going wrong, I am completely new to the library.
In my case the canvas needed to be wrapped inside an element with the CSS display: block;
It appears that the issue is that the canvas and all its parent nodes cannot have display none at the time the chart call is made so if you are using a chart in a popup you need to show the popup, construct the chart and then hide the popup.
As this Fiddle shows, if you try and construct a chart in a hidden div and then show it on a timeout it does not work.
If you instead show the div, make the chart and then hide the div, it does work.
http://jsfiddle.net/bjudheoq/4/
//This will break it
//this.div.style.display = 'none';
this.chart = new Chart(this.ctx).Line(data);
this.div.style.display = 'none';
The above fiddle works, but if you uncomment line 40 it does not.
I found an option:
options: {
responsive: false,
...
This makes conflict with default true. I don't know it works properly at the moment.
Ofcourse set the width and height to the canvas and its parent.
Your code works, with the exception of a typo. This line...
this.chartCanvs.style.width = '900px';
...should be this:
this.chartCanvas.style.width = '900px';
// ^ your code is missing this 'a'
And here's a JS Fiddle with that typo fixed: http://jsfiddle.net/dun5dhne/
Also, I strongly recommend opening your browser's developer console when you run into problems. This kind of thing is easy to catch in the console.

Unable to target child element of Canvas

Here is my code
javascript: (function() {
var canvas = document.getElementById("app-view").children[1];
var img = canvas.toDataURL("image/png");
window.open(img);
})();
and here is the app element where I wish to target the second canvas element "children1" but it's not working for some reason and always gets stuck at the first element. A solution will be much appreciated, thanks in advance!
In the picture it always targets the first canvas in yellow but I need the one displayed in blue. You can test the code by saving the script as a bookmark in your browser.
Haven't worked out why toDataURL gets the data of the left canvas, but if you wanted to save the canvas, you can isolate it with
var can = document.getElementById("app-view").children[1];
jQuery("body > *").remove();
jQuery("body").append(can) ;
and then now you can right click and save the canvas
This works for me:
document.getElementById('app-view').childNodes[1]

Wait for image added to CSS to load - JQuery

I have an image and I want to draw on it. To do that, I use jQuery to hide the image:
$("img").hide();
And then I create a canvas and put it in the same div with id drawing in the html. I then set the background of the canvas to be the same as the img src for the image I hid. This makes it look like an image but now it is actually a canvas with the image as it's background. I do this by:
$('#drawing > canvas').css('background-image','url('+$(".image img").attr('src')+')');
context.canvas.width = $("img").width();
context.canvas.height = $("img").height();
The issue I am having is that sometimes, the image isn't displayed in the canvas and the canvas is not the size of the image. I think it's probably because of some loading issue. How can I wait for the canvas to have the image displayed in the background for sure? Thank you
Edit: Note that in the DOM, the canvas always has the right src. It just doesn't display it
Edit 2: Here's the JSfiddle. Here, everything seems fine but I have a lot more going on in my code including fetching stuff from the server so it's slower there. Hope this helps you guys to understand the problem: http://jsfiddle.net/wL3ezLke/2/
Thanks again
You need to use:
$(function(){
// Code executed once the DOM is loaded.
});
Official documentation:
https://api.jquery.com/ready/
If I understand correctly your problem is knowing when the image loaded (from what you describe it could be a lot of other problems though).
To test if an image has loaded it's pretty simple.
var $img = $('#hiddenImg');
if ($img[0].complete) doCanvasStuff();
else {
$img.on('load', function(e) {
var $canvas = $('#drawCanvas');
$canvas.css({width: $img.width(), height: $img.height()});
//you can go ahead with the background image, but this is preferable
var ctx = $canvas[0].getContext("2d");
ctx.drawImage(img,0,0);
}
}
This should make sure you canvas loads an image only after it was loaded, or do canvas stuff right away if image was loaded, fiddle
Change
$('#drawing > canvas').css('background-image','url('+$(".image img").attr('src')+')');
context.canvas.width = $("img").width();
context.canvas.height = $("img").height();
to
context.canvas.width = $("img").width();
context.canvas.height = $("img").height();
$('#drawing > canvas').css('background-image','url('+$(".image img").attr('src')+')');
so the height and width are set before the image goes into the background.

HTML5 Canvas reset when adding to the body with Javascript

So, I am currently working on an HTML5 game that uses a canvas element as a "point bucket", a visual gauge to the number of points the user has gained. But, something strange happens to the canvas when I add an element to the HTML document body
So, when writing to the body with javascript (document.body.innerHTML = "whatever";), the canvas will erase everything within itself and reset. Here is a quick and dirty example that I have whipped up to show you what I mean:
<!DOCTYPE HTML>
<html>
<body>
<canvas id="a"></canvas>
<script type = "text/javascript">
function addToBody()
{
document.body.innerHTML += "<p>I am adding to the body</p>";
}
var can = document.getElementById("a");
var con = can.getContext("2d");
//Draw rectangle on canvas
con.fillRect(0,50,100,100);
//Add text to body after 5 seconds
setTimeout("addToBody()",5000);
</script>
</body>
</html>
I know the traditional way to reset a canvas is to assign the canvas width like so:
canvas.width = canvas.width;
Is this another way to achieve the same effect or just a bug? Can someone give me some insight about this? Thanks.
Doing innerHTML += something means that the contents are retrieved as a string, something is added to that string, then the whole lot is assigned to the body, whereupon (the layout of) it is all recalculated. (and event listeners are dropped - unless they're inline handlers)
You should use the object-oriented functions to do achieve the same.
I.e
var newElem = document.createElement('p');
newElem.appendChild( document.createTextNode('I am adding to the body') );
document.body.appendChild(newElem);
This will leave your canvas untouched.

Categories

Resources