How to manage memory in case of multiple fabric js canvas? - javascript

In my application, I have multiple Fabric.js canvases, There is no limit on the number of canvases. I'll render heavy JSON via loadFromJson method of Fabric.js.
So I want to release the fabric object memory if the canvas is not in use. How can I do that?
At a time only one canvas will be visible. But I have to render all the canvases as the page loads. Canvas is actually a page and user can switch between pages via clicking on page number or something else.
Remember user can come back to any canvas any time and try to doodle or use any other Fabric.js functionality.
Here is my HTML structure:
<style>
.fabricCanvas {
border: 1px solid green;
margin: 5px;
}
.canvas-container {
margin: 5px;
}
</style>
<canvas class="fabricCanvas" width="300" height="300"></canvas>
<canvas class="fabricCanvas" width="300" height="300"></canvas>
<canvas class="fabricCanvas" width="300" height="300"></canvas>
<canvas class="fabricCanvas" width="300" height="300"></canvas>
My JS code to store fabric instances
var canvasInstances = [];
$('canvas.fabricCanvas').each(function () {
var fabricCanvasObj = new fabric.Canvas(this, {
isDrawingMode: true
});
canvasInstances.push(fabricCanvasObj);
fabricCanvasObj.renderAll();
});
console.log(canvasInstances[0]);
I am storing instances so that I can use them later. I want this for better memory management, basically loading and unloading instances as and when needed.
Sample situation DEMO is here. In this demo consider that the canvases are over each other using z-indexes but they are the part of DOM and has already been rendered on page load.
Let me know in case of any doubt, I can explain further.
When ever there are more than 5 canvases iPad browser crashes which I think is the memory issue.

You might be interested in 3 things (in the order of significance/destruction):
canvas.clear() — removes all canvas objects from it.
canvas.dispose() — removes all canvas objects AND removes all event listeners
$(canvas.wrapperEl).remove() (using jQuery for illustrative purposes) — to remove canvas wrapper element (which contains upper and lower canvases used by Fabric). This can be done AFTER you call dispose, if the goal is to completely remove Fabric canvas from a document.

Related

Object to canvas javascript

I'm testing javascript on a smart TV,
I try to get an object video to canvas. With html5 video tag it works in my browser, but not my smart TV.
But when I try with an object player, I have this error message :
Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The
provided value is not of type '(HTMLImageElement or HTMLVideoElement
or HTMLCanvasElement or ImageBitmap)'
after multiple test with (id, object id,object src..) the result is same, i don't know how i can get an Object video to a canvas.
here's a simple html test:
<canvas id="test" width="300" height="300"></canvas>
<div id="test" style="left: 0%; top: 0%; width: 25%; height: 25%; position: fixed;">
<object type="application/avplayer" style="width: 480px; height: 270px;"></object>
</div>
and the js:
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const video = document.getElementsByTagName('object');
//const video = document.getElementsById('idVideo');
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
Here's an example of my goal but i can't use video tag : http://jsfiddle.net/on1kh4o0/
Any idea or hack to get the same result with an object?
You can't draw an HTMLObjectElement (<object>) directly on a canvas, it is not defined as a CanvasImageSource.
For info, currently the only objects that are defined as being of this type are
HTMLImageElement
SVGImageElement
HTMLVideoElement
HTMLCanvasElement
ImageBitmap
OffscreenCanvas
And even though it's still only part of a draft specs, it is expected that CSSImageValue also gets added to this list.
But HTMLObjectElement is not part of this list, and certainly will never be.
Indeed, even though you can load a video or an image in an <object>, just like with an <iframe>, you can also load a text or an HTML document or many other document types which can't be drawn on a canvas.
Now to your issue, as has been pointed out in the comments, you are definitely facing an XY problem.
Using a video element is currently the only way to draw a video on a canvas (maybe in the future we'll be able to use the Web-Codecs API too, but that's for the future).
So try to find out why your browser doesn't want to draw this video on the canvas.
Try different videos, from different sources, back in the days some Android browsers were blocking drawing any cross-origin mp4 video on a canvas, maybe you are facing a similar issue, if you can, try to input the video from an <input type="file">.
And if your browser has debugging tools, use them. (For instance if it's based on chromium, you should be able to navigate to chrome://media-internals/ or chrome://inspect which might be bale to lead you to some logs.
But anyway, using an <object> as source here won't help you.

How efficient is layering multiple canvases?

I've been wondering if layering canvases is actually an efficient means of increasing performance on a game.
E.g, 2 or 3 canvases of the same size on top of each other.
It's difficult to find results of testing from this, but from what I understand about drawing, if the layer on top of the others is changed, the browser would have to repaint the area to pixels anyway.
Furthermore if those canvases all had to move at the same time, you've multiplied the amount of pixels that would need to be calculated.
I was wondering how the browser handles painting canvases beneath the others if an element above changes.
Is it more efficient due to not having to call the canvas methods themselves again? Wouldn't they still need painting?
My experience is anecdotal, so you really need to test your idea to see if its better.
You're probably going to get the best performance by creating offscreen (in memory) canvases and compositing the different layers of your game with those into a single visible canvas where the game is viewed.
I'm not sure if this will be very helpful, but I'll tell you anyway.
Let's say that you've got 2 canvases and you want your game to switch between them under certain condition. This might be possible by using some HTML, CSS and JavaScript.
<canvas id="canvas1" width=500 height=500 style="border: 2px solid #00F"></canvas>
<canvas id="canvas2" width=500 height=500 style="border: 2px solid #F00"></canvas>
<button id="switcher" onclick="switchcanvas=true">Click here to switch the canvas!</button>
<style>
#canvas1 {
left: 0%;
right: 0%;
display: block;
}
#canvas2 {
left: 0%;
right: 0%;
display: none;
}
</style>
<script>
var switchcanvas = false;
var canvas1 = document.getElementById("canvas1");
var canvas2 = document.getElementById("canvas2");
function update() {
if (switchcanvas) {
if (canvas1.style.display === "block") {
canvas1.style.display = "none";
canvas2.style.display = "block";
switchcanvas = false; //if you set this to true or remove it the canvases will keep switching.
} else {
canvas1.style.display = "block";
canvas2.style.display = "none";
switchcanvas = false;
}
}
}
update();
setInterval(update, 1); //function update will update every ms, you can change that though.
</script>
I tested it and it worked. And yes, it should improve your game's performance since each canvas can have its own context.
I don't know if this is correct, but in my experience if you use two Canvases with the same frame (refresh) rate the performance improvement would be minimal, if there is any at all. You would get an improvement if the refesh rate for Canvas 1 is lower than Canvas 2. A good use case for this would be a grid and a moving object. If the grid doesn't change, you don't have to redraw it. This does not apply for the moving object, because you want to see it moving.

How can i include mouse over effect for each bar in html 5 chart

Am newbie to html5. I want to include some mouse over function through css or javascript so that when i mouse over particular bar it has to display some message. I dont want to go for some api. pls help me out. Here is my code.
<!doctype html>
<html>
<head>
<script type="text/javascript">
window.onload=function(){
var canvas=document.getElementById('mycanvas');
var ctx=canvas.getContext('2d');
var value=[180,140,30,340,50,90];
var width=50;
var currx=30;
ctx.fillStyle="red";
var i = 0;
var interval = setInterval(function(){
if (i == value.length){
clearInterval(interval);
return;
}
var h=value[i];
ctx.fillRect(currx,canvas.height-h,width,h);
currx+=width+10;
i++;
}, 2000);console.log(interval);
};
</script>
</head>
<body>
<canvas id="mycanvas" height="400" width="400" style="border:1px solid #c3c3c3;">
</body>
</html>
Demo on jsFiddle
You have two options:
Use a library like KineticJS which abstracts the drawable shapes into objects and provides nifty methods for binding them to mouse events. This has the benefit of minimizing the work required by your side, therefore allowing you to concentrate on the functionality itself, and not reinventing the wheel.
Roll your own solution, much like someone does when he/she created a library like the one mentioned above. Design some abstraction for your shapes (in this instance some kind of a Bar object) that encompasses the relevant features (notably the x,y,w,h) and on canvas mousemove-event, bruteforce through all your bars and calculate whether or not the event mouse position is within a bar or not. Then act accordingly, e.g. redraw the bar in new color. Of course you'll have to handle mouseout as well (to color it back and the like).

How to detect if an image generated via canvas is blank (transparent PNG)?

I am working on an application in which an image is created/edited on a HTML5 canvas and then saved into a file-store/cloud. The problem is that of "saving efficiency". On save of a blank canvas, i.e. a totally transparent blank PNG is sent with toDataURL(). One way of detecting a blank PNG is by switching a boolean value upon click of any editing/drawing functionality and reseting that value upon clear-screen.
However, such a method is not foolproof because a user may save the image after clicking a draw/edit function and yet not draw anything. Is there a more native approach to detect if the canvas returns a binary string that has changed since opening up on the browser? Or some other way to ensure that a blank transparent PNG is detected at client side?
HTML:
<canvas id="canvas_img" width="300" height="200" border="0"></canvas>
SCRIPT:
isCanvasTransparent(document.getElementById("canvas_img"));
function isCanvasTransparent(canvas) { // true if all pixels Alpha equals to zero
var ctx=canvas.getContext("2d");
var imageData=ctx.getImageData(0,0,canvas.offsetWidth,canvas.offsetHeight);
for(var i=0;i<imageData.data.length;i+=4)
if(imageData.data[i+3]!==0)return false;
return true;
}
UPDATE:
Dont use CSS style declarations like border: 1px solid black; for CANVAS, because border included into canvas image, and, as result, alpha chanel is always not equals to zero.
This isn't native, but this should work, because a blank canvas always generates the same data URL.
So, you could create a hidden canvas, get that canvas's data URL and if it matches that of your editor, then don't upload it. Simple as that.
Demo. First, go hit save without going over the canvas. Then go over it and then hit save. Tada!

Save <canvas> contents to be redrawn in later animation frames?

I am drawing a graph on a <canvas> that requires expensive calculations. I would like to create an animation (when moving the mouse across the canvas) where the graph is unchanging, but some other objects are drawn over it.
Because the canvas will have to be redrawn a lot, I don't want to perform the calculations to render the graph for every frame. How can I draw the graph once, save it, and then use the saved rendering to redraw subsequent frames of the animation, so that the expensive calculations only have to happen once & all I have to redraw is the much simpler animation layer?
I tried drawing the graph on a second canvas & then using ctx.drawImage() to render it onto the main canvas, but drawing on the canvas doesn't seem to work unless it's in the dom & not display:none;. Do I have to do something hacky like position the temp canvas out of view, or is there a cleaner way to do this?
You need to use at least 2 canvases : one with the complex drawing, and the second, on top of the first (with the same size, positioned in absolute), with the animated shapes. This method will work on IE, and getImageData doesn't work with ExCanvas.
Every library which does complex drawings on canvases use this method (Flot and others).
<div style="width: 600px; height: 300px; position: relative;" id="container">
<canvas class="canvas" style="position: absolute; left: 0px; top: 0px;" width="600" height="300"/>
<canvas class="overlay" style="position: absolute; left: 0px; top: 0px;" width="600" height="300"/>
</div>
How about drawing your graph the first time on your canvas and then
var imdata = ctx.getImageData(0,0,width,height);
and then
ctx.putImageData( imdata, 0,0);
for the rest of the rendering.
I had to make a few changes to the flot.js charting library. I'm 99% sure that it uses overlapping canvases. There's a chart layer and an overlay layer. You could look at the source code.

Categories