multiple canvas layers memory leak - javascript

I am trying to add multiple canvas layers into the DOM and am finding that while adding many layers is not an issue if I use .fillText to add content to the layers I suddenly get a lot of memory being used, often to the point that firefox crashes and brings down the entire machine. If anyone out there can see that I am doing something inthe wrong way I would appreciate any advice you might give - thanks tb!
Code below - this is a simplified version of the real thing but demonstrates the issue - if I increase the number of times the loops run I very quickly encounter problems. If I comment out the .fillText code however, I seem to be able to add many layers but as soon as I try and .fillText them my memory use goes through the roof....
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>.</title>
</head>
<body>
<div id="canvasCont"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
$(function () {
var id = 1;
var top = 100;
for ( i = 0; i < 5; i++ ) {
var left = 100;
for ( a = 0; a < 5; a++ ) {
$('#canvasCont').append(
"<canvas width='1300px' height='1300px' style='position: absolute; border: 1px solid black;' id='canvas_" + id + "'>" +
"Your browser does not support canvas" +
"</canvas>"
);
var context = document.getElementById( 'canvas_' + id ).getContext( '2d' );
context.font = "10px Verdana";
context.fillStyle = "red";
context.fillText(
id,
left,
top
);
left += 55;
id++;
}
top = top + 55;
}
}
});
</script>
</body>
</html>

FillText uses A LOT of memory to display, since it's vector-based drawing.
(I would suggest using images, if you intend on displaying always the same text)
I'll also add that you're creating mutliple canvases with quite high resolution, which can be tough for your machine.

In the end I found this library.....paperJS which solved the performance issues with multiple live data points on multiple canvas layers. Highly recommended.

Related

Unable to Center and fill the viewport with the sketch p5.js

I am attempting to center my sketch but I am having trouble doing so. I want my canvas to take up the entire screen on desktop and mobile and always have my sketch in the middle. What am I missing?
Before, I would just use
function windowResized() {
resizeCanvas(windowWidth, windowHeight)
}
and it would work. I'm guessing this is because of the positioning of my animation, but I have no idea what to do with it.
Here's what I got so far: JS fiddle
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="resources\p5.js"></script>
<script src="resources\p5.dom.js"></script>
<script src="resources\p5.sound.js"></script>
<script type="text/javascript" src="js\app.js"></script>
<link rel="stylesheet" href="css\style.css">
<title>Breathe</title>
</head>
<body>
</body>
</html>
CSS
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
JavaScript (p5.js)
let outerDiam = 0;
let cnv;
let mySounds = []
function centerCanvas() {
let x = (windowWidth - width) / 2;
let y = (windowHeight - height) / 2;
cnv.position(x, y);
}
function setup() {
cnv = createCanvas(windowWidth, windowHeight);
centerCanvas();
}
function windowResized() {
centerCanvas();
}
function draw() {
background(220);
for (let i = 0; i < 5; i++){
let diam = outerDiam - 30 * i;
if (diam > 0) {
let fade = map(diam, 0, width, 0, 255);
stroke(fade);
noFill();
ellipse(300, 300, diam);
}
}
outerDiam = outerDiam + 2;
}
function mousePressed() {
outerDiam = 0;
}
Relativity of the canvas to the "viewport"
When you create a canvas, depending on where your js script is doing that in HTML, it will be within the normal HTML flow as a block element(not 100% sure about this, but confident).
Therefore centering the canvas with respect to the viewport should be handled in the parent containing our script, in this case, our HTML file. p5.js's wiki on positioning the canvas offers a solid and concise scenario-based documented solution that is very relevant to you!
Understanding the canvas anatomy
When you create the canvas it takes the top-left corner of the canvas as the origin and set's up the coordinate system as shown below
p5.js coordinate system
Now the actual problem
When you create that ellipse it's always going to be in the position 300,300 due to ellipse(300, 300, diam);. Now the question is how do you center that breathing to the canvas?
There are a few ways!
Draw the ellipse in the middle of the canvas.
Make the canvas big enough for just the breathing effect and have the canvas background match your page's background color and position the script in the center using HTML or CSS
These are just 2 ways to achieve what you want to do. There's a whole lot more.
NOTE: I am keeping my explanation fairly simple by not mentioning rectMode or ellipseMode and etc. You may go and look them up in the documentation they might offer a little help.
Good Luck!🐱‍💻

How to add sleep and wait logic in javascript

We are working on visualization of sorting algorithms, required to add sleep and wait logic to help visualize the selected element and the element to which it is compared. After searching li'l bit, we found a code "function sleep(milliseconds){...}" which should work as desired but has failed so far.
In function insertionSort(){...}, the current element is depicted with color red and the element to which it is compared with is depicted with color blue, once the current element is swapped with the other the color of the element is again changed to white from blue (working correctly, verified using debugger), However during execution, these color transformations were not visible (only the element in red is displayed after each iteration)
var element = function(value, color)
{
this.value = value;
this.color = color;
};
var x = [];
x[0] = new element(2, "white");
x[1] = new element(1, "white");
x[2] = new element(5, "white");
x[3] = new element(4, "white");
x[4] = new element(3, "white");
x[5] = new element(7, "white");
x[6] = new element(6, "white");
x[7] = new element(8, "white");
x[8] = new element(10, "white");
x[9] = new element(9, "white");
var i = 1;
var context;
var delayTime = 1000;
function myFunction()
{
var bar = document.getElementById("bar");
width = bar.width;
height = bar.height;
context = bar.getContext("2d");
window.setInterval(insertionSort, 3000);
}
function insertionSort()
{
if(i>=0 && i<x.length)
{
var j = i;
x[j].color = "red";
drawGraph(j);
while(j>0 && x[j-1].value > x[j].value)
{
x[j-1].color = "blue";
x[j].color = "red";
drawGraph();
//need to add delay here
sleep(delayTime);
//swap
var temp = x[j];
x[j] = x[j-1];
x[j-1] = temp;
drawGraph();
// and here...
sleep(delayTime);
x[j].color = "white";
drawGraph();
j = j-1;
}
x[j].color = "white";
i++;
}
else if(i>=x.length)
{
for(k=0;k<x.length;k++)
{
x[k].color = "white";
}
drawGraph();
i=-1;
}
}
function sleep(milliseconds)
{
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++)
{
if ((new Date().getTime() - start) > milliseconds)
{
break;
}
}
}
function drawGraph()
{
context.StrokeStyle = "black";
context.clearRect ( 0 , 0 , width, height);
for(k=0;k<x.length;k++)
{
context.fillStyle = x[k].color;
//x and y coordinate of top left corner of rectangle
context.strokeRect(400+k*20, 18, 20, x[k].value*10);
context.fillRect(400+k*20, 18, 20, x[k].value*10);
}
}
<html>
<head>
<script language="javascript" type="text/javascript" src="../p5.js"></script>
<!-- uncomment lines below to include extra p5 libraries -->
<!--<script language="javascript" src="../addons/p5.dom.js"></script>-->
<!--<script language="javascript" src="../addons/p5.sound.js"></script>-->
<script language="javascript" type="text/javascript" src="sketch.js"></script>
<!-- this line removes any default padding and style. you might only need one of these values set. -->
<style> body {padding: 0; margin: 0;} </style>
</head>
<body>
<button onclick="myFunction()">Try it</button>
<canvas id="bar" width="1000" height="400" style="border:2px"></canvas>
</body>
</html>
The approach to used in that implementation of sleep() would be terrible in any programming language, because it consumes a lot of CPU while waiting. In JavaScript, however, it's especially bad, because a JavaScript program is required to relinquish control frequently; it is not permitted to keep computing for an extended period of time. In Chrome browser, for example, Chrome will consider the program to be unresponsive, and will suggest to the user that they kill it.
But even if that weren't the case, it won't produce the desired effect, which I assume is that some animation happens on the screen, with some delay from one step to the next. The way JavaScript works in the browser, is that any changes you make to the page get rendered when your program relinquishes control; nothing updated on-screen while any JavaScript code is running. If you call a sleep function like that one, you are not relinquishing control, you are running JavaScript the whole time, and therefore the browser will not update the screen during that time. It will only update when your entire insertionSort method returns, and the browser has that 3000ms time window (from your setInterval) to take care of its own stuff (rendering).
Unfortunately, you will have to find a way to split up that algorithm, so that each step that you want to be distinctly visible to the user happens in its own timed callback.
It will probably be something along the lines of:
function stepOne() {
do the first bit;
setTimeout(secondStep, delay)
}
secondStep() {
do some more stuff;
setTimeout(thirdStep, delay)
}
and so on. The way you control the speed of the animation is with the delay parameter from one step to the next.
It's going to be tricky, especially because you aren't just trying to animate Insertion Sort, but various algorithms. So then, do you break them all up as in: insertionSortStepOne/Two/Three, shellSortStepOne/Two/Three? that would be quite ugly.
Depending on how ambitious you are, and how much you want to get out of this assignment, you might explore this feature of ES6 (a newer version of JavaScript)
function*
What this lets you do is let your function, with all its nested loops, remain structured pretty much as it is, but you insert points where it relinquishes control. Later, it is called back to continue from the point where it left off. You would use setTimeout or setInterval to do that. I've not experimented with this myself, but it seems super-cool.

html 5 canvas how to make a player jump (pure no frameworks)

I have a player, a background, and a land piece all set up. What I need is a way for the player to jump and land when I tap on the screen or onscreen button. I am making this for a mobile device. Here is the coding I have done so far.
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title> Testing Canvas</title>
<script type="text/javascript">
window.onload = function ()
{
var canvas = document.getElementById("canvasOne");
var context = canvas.getContext("2d");
var canvasbk = document.getElementById("bk");
var contextbk = canvasbk.getContext("2d");
var myelf = new Image();
var myice = new Image() ;
var mybk = new Image();
myice.src="iceland.png";
myelf.src="elf1.png" ;
mybk.src="bk.png";
myelf.onload = function()
{
context.drawImage(myelf,40,55);
}
myice.onload = function()
{
context.drawImage(myice,0,150);
}
mybk.onload = function()
{
contextbk.drawImage(mybk,0,0,canvasbk.width,canvasbk.height);
}
}
</script>
</head>
<body>
<div style = “width:500px; height:500px; margin:0 auto; padding:5px;”>
<canvas id="canvasOne" width="500" height="300"
style="border:2px solid black; position:absolute;
left:auto; top:auto; z-index:2"></canvas>
<canvas id="bk" width=500px; height="300px; style="border:solid black;
position:absolute; top:auto left:auto; z-index 1"></canvas>
</div>
</body>
</html>
Since you want to do some learning, I won't spoil your experience with a coded answer, but you should check these 2 ways of making your character jump.
To do a stationary jump (start & land on same coordinate) check out Easing.
Easing lets your character jump fast, slow at their apex & speed up on descent. In particular, check out easeOutQuart for ascent and easeInQuart for descent.
http://jqueryui.com/resources/demos/effect/easing.html
To jump from a starting point to a different ending point check out the quadratic curve.
It draws a curve from an exact starting point to an exact ending point with the amount of "curviness" defined by a control point. The nice thing about a quadratic curve formula is that it naturally generates more points at its apex (more points means your character will start fast, slow at apex and speed up at descent). Here's the quadratic formula which returns XY at a given time (T).
// get XY along quadratic bezier at T
// (T==0.00 at start of curve, T==1.00 at end of curve)
function getQuadraticBezierXYatT(startPt,controlPt,endPt,T) {
var x = Math.pow(1-T,2) * startPt.x + 2 * (1-T) * T * controlPt.x + Math.pow(T,2) * endPt.x;
var y = Math.pow(1-T,2) * startPt.y + 2 * (1-T) * T * controlPt.y + Math.pow(T,2) * endPt.y;
return( {x:x,y:y} );
}
BTW, there is a likely problem in your example code:
Your bk.png image is likely your largest image since it fills the canvas. It will likely take the longest to load and will be drawn last. Therefore, your background will likely overwrite your elf1.png and Iceland.png images. To correct this, check out an image loader so all images are fully loaded before you drawImage them on the canvas.

IE9 raphael animation issue

I have problem with display of correct javascript in IE9. Other browsers (Firefox, Opera, Chrome, Safari) work well, but animation in IE is not fluent. For example see this line which can be dragged from left to right (link at the end of the post).
javascript code:
var w = 1250;
var h = 650;
var drawing = Raphael("obrazek",w,h);
var Ax = 50
var Ay = 50
var Ey = 500
var w = 1250;
var h = 650;
var drawing = Raphael("obrazek",w,h);
var Ax = 50
var function onDragMove(dx,dz) {
this.onDragUpdate(dx - (this.deltax || 0), dz - (this.deltaz || 0));
this.deltax = dx;
this.deltaz = dz;
}
function onDragStart() { this.deltax = this.deltaz = 0; }
function onDragStop() { this.onDragStop(); }
// line 1
var Ax
var line = drawing.path([["M",Ax,Ay],["L",Ax,Ey]]).attr({"stroke-width":3})
line.drag(onDragMove,onDragStart)
line.attr({"cursor":"move"})
line.onDragUpdate = function(dx,dz) {
Ax += dx
line.attr({"path":[["M",Ax,Ay],["L",Ax,Ey]]})
}
and corresponding HTML:
<html>
<head>
<script src="raphael.js"></script>
</head>
<body>
<div id="obrazek">
<script src="ietest.js"></script>
</div>
</body>
</html>
or see the problem in IE9 here and compare it with Chrome:
http://mech.fsv.cvut.cz/~stransky/ietest/ietest.html
Thank in advance for any help.
Your page is missing doctype, so it is rendered in quirks mode. IE9 uses VML instead of SVG in quirks mode, which probably results in slower rendering. Just add this on the first line of your html:
<!DOCTYPE html>
However, your code has some other problems:
Missing semicolons. There is a good explanation of how it may be dangerous.
Variable re-declarations and re-definitions.
When handling rapidly repeating events like mousemove or scroll, it is reasonable to use throttling to avoid redrawing/repainting glitches and performance problems. You can read more about it here. Include the plugin from that site and replace your drag binding with the following:
line.drag($.throttle(30, onDragMove), onDragStart);
In fact, even doing this without specifying the doctype can greatly improve the rendering performance, but there's no reason not to specify it altogether.

javascript / jquery / processing.js - how to most effectively create and animate canvas elements

I'm trying to build an application which, based on various user interactions, allows for various ellipse based visuals to be added to the stage and then animated very simply. I've currently got a basic demo set up where javascript / jquery communicates with processing.js, but it just seems like really inefficient code, because processing relies on running a continuous loop in order to draw to the screen. I'm wondering, one, if the way I'm doing it will be effective on a larger scale, and two, if there's a better technology or method to use. I come from a flash background where nothing on screen is changed or drawn/animated unless a function is triggered telling it to animate, which seems sensible. Anyway, here's my code:
HTML / JS:
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>Processing</title>
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript"></script>
<script src="js/processing-1.3.6.min.js"></script>
<script src="processing/Tween.lib"></script>
</head>
<body>
<canvas id="circles" data-processing-sources="js/drawCircles.js"></canvas>
<div id="clicker">Click</div>
<script>
window.Processing.data = {};
var dataRef = window.Processing.data;
var animInterval;
dataRef.circleArray = new Array();
$('#clicker').click(function(){
var circle = {};
circle.radius = 50;
dataRef.circleArray.push(circle)
var from = {property: 50};
var to = {property: 75};
jQuery(from).animate(to, {
duration: 300,
step: function() {
for (var i in dataRef.circleArray){
circle.radius = this.property;
}
}
});
})
</script>
</body>
</html>
PROCESSING.JS
// Global variables
float radius = 1.0;
int X, Y;
int nX, nY;
int delay = 16;
// Setup the Processing Canvas
void setup(){
// Fill canvas grey
background( 100 );
size( 200, 200 );
strokeWeight( 10 );
frameRate( 15 );
X = width / 2;
Y = width / 2;
nX = X;
nY = Y;
}
// Main draw loop
void draw(){
var dataRef = window.Processing.data;
for (var i in window.Processing.data.circleArray){
radius = dataRef.circleArray[i].radius;
// Set fill-color to blue
fill( 0, 121, 184 );
// Set stroke-color white
stroke(255);
// Draw circle
ellipse( X+(i*10), Y, radius, radius );
}
}
If you want to control when Processing.js draws to the canvas, you have two options. In both cases, the first thing you'll want to do is get access to the Processing instance:
var p = Processing.instances[0];
Now you can make all the Processing API calls you want from JavaScript. You could call noLoop() in your sketch's setup() function, and then inside your jQuery animation loop you could call p.redraw(), which will animate one frame.
In Processing.js we attach all of the functions to the Processing instance. So another option is creating your own function in the sketch, and call it with:
var p = Processing.instances[0];
p.drawEllipses(radius);
You could even pass the data to it in the function parameters, removing the need for windows.Processing.data.
For what you want to do, you might prefer using another library such as paperjs http://paperjs.org/

Categories