pjs sketches and html canvas - javascript

I'm trying to understand what I'm doing when I stick a pjs sketch to an html canvas in the following way.
<body>
<canvas id="mycanvas" style="border: 1px solid black;"></canvas>
</body>
<script src="https://cdn.jsdelivr.net/processing.js/1.4.8/processing.min.js">
</script>
<script>
var sketchProc = function(processingInstance) {
with (processingInstance) {
size(800, 400);
frameRate(30);
draw = function() {
background(235, 245, 255);
};
}};
var canvas = document.getElementById("mycanvas");
var processingInstance = new Processing(canvas, sketchProc);
</script>
When I began learning this stuff, I was eager to add my animations to an html doc. I learned the above way pretty much how I imagine a hamster can learn to push a button and receive a pellet; that is, I don't understand the mechanism well, but the result is positive.
Here is the above example in action:
https://jsfiddle.net/h1Lb91ux/
Here is my attempt at making sense of this.
The following code is defining a function called sketchProc.
var sketchProc = function(processingInstance) {
with (processingInstance) {
size(800, 400);
frameRate(30);
draw = function() {
background(235, 245, 255);
};
}};
This doesn't return anything because we're just defining sketchProc, not calling it. It's argument is processingInstance, which is being defined as a local variable just like if I did:
var functionX = function(x) {
return x;
};
But I do not understand the with statement. I know of with statements from excel-vba, which can be used to attach multiple methods to an object without having to repeatedly type out the object name. Is this the same?
The following statement makes perfect sense.
var canvas = document.getElementById("mycanvas");
But this next one is a source of confusion:
var processingInstance = new Processing(canvas, sketchProc);
To me, this looks like we're making a single instance or object from an object constructor function (in javascript) or a class (in processing), which takes 2 arguments: canvas and sketchproc. We're calling this specific instance processingInstance, which confusingly is the same name as the local variable used in the function sketchProc.
I hope this is not too rambling.

The following code is defining a function called sketchProc.
var sketchProc = function(processingInstance) {
with (processingInstance) {
size(800, 400);
frameRate(30);
draw = function() {
background(235, 245, 255);
};
}};
It's defining a function object that itself contains functions like draw(). You can read more about function objects here, but basically this is just an instance that contains functions that Processing can call.
But I do not understand the with statement.
Try googling something like "javascript with" for a ton of results, including this one. But my understanding is that it's a shortcut to avoid having to call processingInstance. before everything. You can try removing the with statement and putting processingInstance. before any Processing function to see the alternative. The with is optional, but makes you code slightly shorter. I personally don't like the use of with here, but that's just me.
But this next one is a source of confusion:
var processingInstance = new Processing(canvas, sketchProc);
To me, this looks like we're making a single instance or object from
an object constructor function (in javascript) or a class (in
processing), which takes 2 arguments: canvas and sketchproc. We're
calling this specific instance processingInstance, which confusingly
is the same name as the local variable used in the function
sketchProc.
Your understanding sounds about right. Creating an instance of Processing is what kicks off all of the Processing magic, calling the setup() function and the draw() function for you automatically. You can also rename the variable if it confuses you.
Taking a step back, this code uses Processing.js's instance mode, which is why you have all this extra code that you don't really understand. I would recommend you start with the more basic global mode, which would allow your code to look like this:
<script type="application/processing">
function setup(){
size(800, 400);
frameRate(30);
}
function draw() {
background(235, 245, 255);
};
</script>
<canvas> </canvas>
I'm also curious why you're using Processing.js. If you're coming from a JavaScript background, you might have more luck with P5.js instead.

Related

forEach problem: lineTo is not defined at CanvasRenderingContext2D

I tried using a forEach loop on an array of points and a canvas drawing context.
This is a simplified sample:
<html>
<head>
<<script>
// assume this is going to be some code independent of the calling web page
function showPoints(context,data) {
context.beginPath();
context.moveTo(0,0);
context.lineTo(0, data[0]); // OK
data.forEach(function(pt,i){
lineTo(10*i, pt); // Error
lineTo(10*(i+1), pt);
},context); // to be made available to the forEach callback
context.stroke();
}
</script>
</head>
<body>
<canvas width="500" height="500" id="myCanvas">
<script>
const data = [10,20,25,30,25];
const c = document.getElementById('myCanvas');
const ctx = c.getContext("2d");
showPoints(ctx, data);
</script>
</body>
<html>
This fails with ReferenceError: lineTo is not defined at CanvasRenderingContext2D
Sure, ctx.lineTo is defined, used before and still visible directly above the forEach line.
Passing ctx as a this parameter seems ok as well, in my understanding of the error message.
A regular named function did not make a difference.
Any hints what I'm doing/understanding wrong, please? (javascript is not my native language)
Sure I could replace the forEach construct, or use the global context constant, but I want to learn its usage.
As #Titus mentioned in his comment already, to use a method context (this) you can pass it to the forEach callback, but you have to use it there by explicitly mentioning this.
function showPoints(context,data) {
context.beginPath();
context.moveTo(0,0);
data.forEach(function(pt,i){
this.lineTo(10*i, pt);
this.lineTo(10*(i+1), pt);
},context); // to be made available to the forEach callback
context.stroke();
}
As many of the comments under your post have mentioned, there is no lineTo() available to be called. However, ctx.lineTo() does exist, and would not cause an error.

Function Scope in P5.js

I am using the p5.js library as the basis for my program due to its ease of implementing visual features to javascript, however I have run into a bit of trouble.
I am wanting to create multiple canvases within the same file to be displayed on an html page. I have attempted a few means of doing this and have had the most success with this means of achieving it. Effectively separating two p5 sketches via “instance mode” to then be able to reference them from separate div elements in the html file.
This means works for what I want it to. I can create x number of instances and separate them into x number of div elements on my html page without problem.
However, an integral feature of my program is that when a key is pressed it calls a function that is specific to a particular canvas. Using the keyPressed() function in p5 I am able to achieve this. However, it is not scalable to multiple canvases.
Effectively it seems that (at least with my implementation) that putting this function inside of multiple instances causes the function to only be called in the first instance and ignoring the others. To go around this I was thinking I could implement this keyPressed() function outside the scope of the instances and then have that keyPressed() function call a function from each instance to achieve what I desire.
This is where my problem has arisen, I do not know how I can refer to a function within an instance from outside the local scope of that instance. Here is some example code of what I am wanting to achieve. I am not very experienced with JavaScript and any help would be much appreciated.
function keyPressed() {
if(keyCode === [SOME_KEY]) {
//Call doSomething() from sketch1 and sketch2
}
}
//Sketch 1
var sketch1 = function( p ) { // p could be any variable name
p.setup = function() {
createCanvas(); //etc
};
p.doSomething = function() {
//etc
}
};
var myp5 = new p5(sketch1, 'c1');
// Sketch Two
var sketch2 = function( p ) {
p.setup = function() {
createCanvas(); //etc
};
p.doSomething = function() {
//etc
}
};
var myp5 = new p5(sketch2, 'c2');

Use p5.js functions inside global variables

I want to have a file with global variables, for example:
function Globals() {
}
Globals.gravity = createVector(0, -9.81);
Unfortunately p5.js functions can only be used when they are declared inside setup() or draw() or are called from one of these functions.
My question is what would be the best approach to make globals easy to use?
My only idea is to make them functions, but that is not very pretty (you have to call function to get a value) and it is probably slow, because each access to a global variable requires making a call.
Globals.gravity = function() { return createVector(0, -9.81); }
Well, fortunately, you could use p5.js functions outside setup() and draw() function.
In order to use those functions, you need to call new p5() beforehand, like so ...
new p5(); //<-- call this
function Globals() {}
Globals.gravity = createVector(0, -9.81);
For more info, refer here

Exporting p5.js function with Browserify

Here I have a p5 object that I am exporting to be bundled by browserify:
var p5 = require('p5')
var colorPicker = require('./color_picker.js')
module.exports = new p5(function () {
this.setup = function setup () {
this.createCanvas(700, 400)
this.background(205)
this.loadImage('/uploads/uploaded_image', function (img) {
image(img, 0, 0)
})
this.updatePixels()
}
this.clearCanvas = function redraw () {
this.background('black')
}
this.mouseDragged = function mouseDragged () {
var rgb = colorPicker.getRGB()
this.stroke(rgb.r, rgb.g, rgb.b)
this.strokeWeight(10)
this.line(this.pmouseX, this.pmouseY, this.mouseX, this.mouseY)
}
})
All of this works fine and I can access all built in p5 functions in other bundled scripts but not the clearCanvas function that I have defined. I also tried attaching it to the window object based on another SO post, like this:
window.this.clearCanvas = function redraw(){
//code
}
Everything so far has yielded Uncaught TypeError: Cannot set property 'clearCanvas' of undefined
Any idea what I'm doing wrong?
The modules build by browserify have their own scope, so nothing is exposed to the window object per default. You explicitly need to append your stuff to the window object to access it from a browser.
var p5 = require('p5')
var colorPicker = require('./color_picker.js')
module.exports = new p5(function () {
// ...
this.clearCanvas = function redraw () {
this.background('black')
}
// ...
window.clearCanvas = this.clearCanvas.bind(this);
});
First, for the section:
window.this.clearCanvas = function redraw(){
//code
}
To attach something to the window object do it directly,changing it to this:
window.clearCanvas = function redraw(){
//code
}
Worked, however I wanted to attach to the window object as infrequently as possible. For p5.js this section in the documentation is important:
By default, all p5.js functions are in the global namespace (i.e. bound to the window object), meaning you can call them simply ellipse(), fill(), etc. However, this might be inconvenient if you are mixing with other JS libraries or writing long programs of your own. To solve this problem, there is something we call "instance mode", where all p5 functions are bound up in a single variable instead of polluting your global namespace.
https://github.com/processing/p5.js/wiki/p5.js-overview
Running p5.js in instance mode allowed me to use the clearCanvas function without binding it to the window object.

why can't these variables be referenced globally in my p5 functions?

I'm having trouble understanding how specially defined p5 functions can reference globally defined variables. Functions like this one -- where a constant supplies the argument for a p5 function locally -- work just fine.
function setup() {
}
function draw() {
ellipse(50, 50, 80, 80);
}
But for some reason, I can't get something like this to draw an ellipse:
var CANVAS_HEIGHT = 1024;
var CANVAS_WIDTH = 768;
var RADIUS = 150;
var circleColor = 150;
var bgColor = 50;
function setup() {
backgroundColor = color(bgColor);
createCanvas(CANVAS_WIDTH, CANVAS_HEIGHT);
}
function draw() {
fill(circleColor);
ellipse(CANVAS_WIDTH/2, CANVAS_HEIGHT/2, RADIUS*2, RADIUS*2);
}
Apparently the setup function does create a canvas referencing the global variables. The draw function, however, doesn't seem to reference them. What am I missing here? Thanks for any help.
You can't use variables like that in the setup() function.
You have to use the values directly, and you have to assign the variables inside the setup() function:
var CANVAS_HEIGHT;
var CANVAS_WIDTH;
var RADIUS;
var circleColor;
var bgColor;
function setup() {
backgroundColor = color(50);
createCanvas(1024, 768);
CANVAS_HEIGHT = 1024;
CANVAS_WIDTH = 768;
RADIUS = 150;
circleColor = 150;
bgColor = 50;
}
From the p5.js faq:
Why can't I assign variables using p5 functions and variables before setup()?
In global mode, p5 variable and function names are not available
outside setup(), draw(), mousePressed(), etc. (Except in the case
where they are placed inside functions that are called by one of these
methods.) What this means is that when declaring variables before
setup(), you will need to assign them values inside setup() if you
wish to use p5 functions. For example:
var n;
function setup() {
createCanvas(100, 100);
n = random(100);
}
The explanation for this is a little complicated, but it has to do
with the way the library is setup in order to support both global and
instance mode. To understand what's happening, let's first look at the
order things happen when a page with p5 is loaded (in global mode).
Scripts in are loaded. of HTML page loads (when this is complete, the
onload event fires, which then triggers step 3). p5 is started, all
functions are added to the global namespace. So the issue is that the
scripts are loaded and evaluated before p5 is started, when it's not
yet aware of the p5 variables. If we try to call them here, they will
cause an error. However, when we use p5 function calls inside setup()
and draw() this is ok, because the browser doesn't look inside
functions when the scripts are first loaded. This is because the
setup() and draw() functions are not called in the user code, they are
only defined, so the stuff inside of them isn't run or evaluated yet.
It's not until p5 is started up that the setup() function is actually
run (p5 calls it for you), and at this point, the p5 functions exist
in the global namespace.

Categories

Resources