I just started a new project in p5, I've already used it directly imported in the browser, but this time, since it's a more complex project, I'm going to use it in webpack.
I imported the library and bootstraped it in this way:
import * as p5 from 'p5';
function setup() {
createCanvas(640, 480);
}
function draw() {
if (mouseIsPressed) {
fill(0);
} else {
fill(255);
}
ellipse(mouseX, mouseY, 80, 80);
}
But it doesn't work.
The reason is simple: webpack wraps the module in a local scope, and p5 isn't aware of it.
For this reason, I assigned the functions to the global scope:
import * as p5 from 'p5';
window.setup = function () {
createCanvas(640, 480);
}
window.draw = function () {
if (mouseIsPressed) {
fill(0);
} else {
fill(255);
}
ellipse(mouseX, mouseY, 80, 80);
}
And it works fine, but still looks wrong. I don't think that pollulating the global scope is the correct way of working with JS in 2019. Expecially if I'm using webpack and I'm about to implement TypeScript.
So, how can I tell p5 to look for the functions in the module scope and not in the global one?
You'd use instance mode, which doesn't rely on globals. Here's the example from that page:
var sketch = function( p ) {
var x = 100;
var y = 100;
p.setup = function() {
p.createCanvas(700, 410);
};
p.draw = function() {
p.background(0);
p.fill(255);
p.rect(x,y,50,50);
};
};
var myp5 = new p5(sketch);
Live Example:
var sketch = function( p ) {
var x = 100;
var y = 100;
p.setup = function() {
p.createCanvas(700, 410);
};
p.draw = function() {
p.background(0);
p.fill(255);
p.rect(x,y,50,50);
};
};
var myp5 = new p5(sketch);
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js"></script>
Related
My 9 year old son is learning Javascript. I'm not able to easily help him. He's working on a small project, and can't seem to get past an error:
Uncaught ReferenceError: mainLoop is not defined.
This is a great learning opportunity for him. We appreciate any clues as to what's going on in his code that's causing the error. Thanks!
Here's what he's got:
var CANVAS_WIDTH = 800;
var CANVAS_HEIGHT = 400;
var LEFT_ARROW_KEYCODE = 37;
var RIGHT_ARROW_KEYCODE = 39;
//SETUP
var canvas = document.createElement('canvas');
var c = canvas.getContext('2d');
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;
document.body.appendChild(canvas);
window.requestAnimationFrame(mainLoop);
var shapeInfo = {
squares: {
square1: {
x: 10,
y: 10,
w: 30,
h: 30,
color: 'orange'
}
}
};
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
var leftArrowKeyIsPressed = false;
var rightArrowKeyIsPressed = false;
var touchingRightEdge = false;
// SENSORS
function sense() {
if (shapeInfo.squares.square1.x <= CANVAS_WIDTH - 30) {
touchingRightEdge = true;
}
// PLAYER CONTROLS
function onKeyDown(event) {
if (event.keyCode === RIGHT_ARROW_KEYCODE) {
rightArrowKeyIsPressed = true;
}
}
function onKeyUp(event) {
if (event.keyCode === RIGHT_ARROW_KEYCODE) {
rightArrowKeyIsPressed = false;
}
}
//MAIN LOOP
function mainLoop() {
window.requestAnimationFrame(mainLoop);
draw();
}
//DRAW
function draw() {
c.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
// Draw the frame
c.strokeStyle = 'black';
c.strokeRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
// Draw square1
c.fillStyle = shapeInfo.squares.square1.color;
c.fillRect(shapeInfo.squares.square1.x, shapeInfo.squares.square1.y, shapeInfo.squares.square1.w, shapeInfo.squares.square1.h);
if (rightArrowKeyIsPressed) {
if (!touchingRightEdge) {
shapeInfo.squares.square1.x++;
}
}
if (leftArrowKeyIsPressed) {
shapeInfo.squares.square1.x--;
}
// end
}
}
Great to hear that your son is learning something as cool as JavaScript. Now as #Pointy pointed out (no pun intended) you are calling window.requestAnimationFrame(mainLoop); outside the sense function which causes the error. The mainLoop function does not exist outside sense.
The solution to this would to be define your functions globally, in this case meaning:
not inside another function.
So prevent doing:
function foo() {
// Do something
function bar() {
// Do something else
}
}
foo() // Do someting
bar() // Uncaught ReferenceError: bar is not defined.
Now bar only exists within foo. Instead do this:
function foo() {
// Do something
}
function bar() {
// Do something else
}
foo() // Do something
bar() // Do something else
Both functions can now be called from the same scope (remember this word).
Also in your mainLoop function you got to switch some things around. Try to call the draw function first before you start the mainLoop again. JavaScript works from top to bottom. So in the example below it will first draw and then start the loop again.
function mainLoop() {
draw();
window.requestAnimationFrame(mainLoop);
}
You're doing great, kid! Keep it up and come back whenever you want. We'll help you out!
I have this class object that needs to have its draw function inside the constructor to update the score, if it is outside the score returns undefined.
export class Hud {
constructor(world) {
var self = this;
this.canvas = world.canvas;
this.ctx = world.ctx
this.score = 0;
this.draw = function() {
this.ctx.font = "16px Arial";
this.ctx.fillStyle = "#0095DD";
this.ctx.fillText("Score: " + self.score, 8, 20);
}
}
}
my other class objects have the draw function outside of the constructor like so works fine,
export class Ball {
constructor(world) {
var self = this;
this.canvas = world.canvas;
this.ctx = world.ctx;
self.x = canvas.width / 2;
self.y = canvas.height - 30;
this.ballRadius = 10
this.dx = 2;
this.dy = -2
}
draw() {
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.ballRadius, 0, Math.PI * 2);
this.ctx.fillStyle = "#0095DD";
this.ctx.fill();
this.ctx.closePath();
}
}
My question is what the difference between the two are. I thought if its defined in the constructor the variables are accessible throughout the class object. I guess I'm confused, should there be functions outside of constructor function? Having everything in the constructor seems to be hassle free.
Function defined in the class (not in the constructor) live on the class prototype which each instance is linked to. Functions defined in the constructor become own properties of each instance. If you defined the function in the constructor, each instance will get its own copy of the function. If you don't the instances will defer to the prototype chain and all the instances will point to the same function. For example:
class Hud {
constructor(world) {
this.name = world
this.draw = function() {
console.log("draw on instance from", this.name)
}
}
draw_outside(){
console.log("draw on class from", this.name )
}
}
let h1 = new Hud('h1')
let h2 = new Hud('h2')
console.log(h1.__proto__.draw) // draw not on prototype
console.log(h1.__proto__.draw_outside) // draw_outside is
console.log(h1.draw === h2.draw) // each object gets its own draw
console.log(h1.draw_outside === h2.draw_outside) // but both point to the same draw_outside
console.log(Object.getOwnPropertyNames(h1)) // only draw & name
// both access `this` the same way when called on an instance:
h1.draw()
h1.draw_outside()
var bubbles;
function setup() {
createCanvas(600, 400);
bubbles = new Bubble();
}
function draw() {
background(50);
bubbles.displ();
bubbles.mov();
}
class Bubble {
constructor() {
this.x = 200;
this.y = 200;
};
displ() {
noFill();
stroke(255);
strokeWeight(4);
ellipse(this.x, this.y, 25, 25);
};
mov() {
this.x = this.x + random(-1, 1);
this.y = this.y + random(-1, 1);
}
}
ERROR MESSAGE
14: Uncaught SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
Did you just try to use p5.js's str() function?
If so, you may want to move it into your sketch's setup() function. For more details, see here.
What is wrong here?
The error message says it all... You are not allowed to declare variables before setup.
And there is a solution for your problem, too. 1 I have rewritten it so it fits your code.
var s = function( sketch ) {
var bubbles;
function setup() {
sketch.createCanvas(600,400);
bubbles = new Bubble();
}
function draw() {
sketch.background(50);
sketch.bubbles.displ();
sketch.bubbles.mov();
}
};
var myp5 = new p5(s);
1 From the github repository referred to in your error-message: https://github.com/processing/p5.js/wiki/Global-and-instance-mode
I'm trying to create a simple Sprite class for use in a canvas game, but the image I specify when I create a new instance of the class never calls onload or onerror.
What's happening, and how do I get it to load and draw the image?
Here's the script:
var cvs = document.getElementById("c");
var ctx = cvs.getContext("2d");
var imagesToLoad = 1 ;
var imagesLoaded = 0;
var gameState = 0;
//START OF IMAGE STUFF//
function imageOnload() {
imagesLoaded+=1;
console.log("Images: loaded "+imagesLoaded+" overall.");
}
function loadLoop() {
if (imagesLoaded == imagesToLoad) {
gameState = 1;
}
}
function loop1() {
ctx.drawImage(circle.image);
}
function gameLoop() {
if(gameState==0){
loadLoop();
}
if(gameState==1){
loop1();
}
}
function Sprite(positionsArray){
this.x = positionsArray[0];
this.y = positionsArray[1];
this.dx = positionsArray[2];
this.dy = positionsArray[3];
this.angle = positionsArray[4];
this.rotating = positionsArray[5];
};
circle = new Sprite(new Array(0, 0, 0, 0, 0, 0));
circle.image = new Image().onload="imageOnload()".src="circle.png".onerror="console.log(\"Nope.\")";
console.log("circle?");
setInterval(function(){gameLoop()}, 10);
//END OF IMAGE STUFF//
Corrections:
No quotes on image.onload function definition
No parens on image.onload function definition
image.onerror requires a function definition or anonymous function
Put image.onerror before image.src
Here's some example code:
circle.image=new Image();
circle.image.onload=imageOnLoad;
circle.image.onerror=function(){console.log("Image failed to load")};
circle.image.src="koolaidman.png";
function imageOnLoad(){
ctx.drawImage(circle.image,0,0); // or do other stuff
}
var ball = {
x: 20,
y: 500,
vx: 100,
vy: 100,
width: 13,
height: 13,
draw: function() {
var img = new Image();
img.src = 'images/ball.png';
img.onload = function(){
ctx.drawImage(img, this.x, this.y);
};
},
I want the drawImage() line of code to refer to the ball.x and ball.y. Instead of using ball.x and ball.y, I want to use "this" keyword so that i can turn the ball object into a function that is a mass constructor/prototype if i end up wanting to (able to make ball1, ball2, ball3 etc.). I think "this" is not referring to ball anymore because it's in a nested function? Is there any way around that without hard-coding ball.x and ball.y into the drawImage arguments?
This is one of the tricky things about JavaScript: this is dynamic. To put it simply, the solution is to put the this you want in a variable while you have it and use that variable to refer to it:
var ball = {
// ...
draw: function() {
// ...
var myself = this;
image.onload = function() {
// use myself rather than this
};
}
};
Another solution is to fix the value of this. That is done using bind:
var ball = {
// ...
draw: function() {
// ...
image.onload = function() {
// ...
}.bind(this);
}
};
That will bind the value of this inside the onload function to whatever it was when draw was called. This latter solution won't work on older browsers, but it is easily shimmed.
Yes, basically you need to use a closure. All you need to do is refer to the variables by their parent instead of by the use of this, which will actually refer to the img element in the function. So just change your code to
ctx.drawImage(img, ball.x, ball.y);
or even
ctx.drawImage(this, ball.x, ball.y);
var ball = function(){
this.x= 20;
this.y=500;
this.vx= 100;
this.vy= 100;
this.width= 13;
this.height= 13;
}
ball.prototype = {
draw: function() {
var img = new Image();
img.src = 'images/ball.png';
var balref = this;
img.onload = function(){
ctx.drawImage(img, balref .x, balref.y);
}
}
var myball = new ball();
myball.draw();
Note: not tested