Reputation: 311
I am having some trouble with this because Javascript just seems terrible for classes and the implementation is interesting. I am trying to get this block working so I can create multiple triangles:
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
var phase = 0;
var tau = 2 * Math.PI;
function animate() {
requestAnimationFrame(animate);
var sides = 3;
var size = 100;
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
phase += 0.005 * tau;
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
for (var i = 0; i <= sides; i++) {
context[i ? 'lineTo' : 'moveTo'](
centerX + size * Math.cos(phase + i / sides * tau),
centerY + size * Math.sin(phase + i / sides * tau)
);
}
context.stroke();
}
animate();
And here I tried making it into a class:
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
var phase = 0;
var tau = 2 * Math.PI;
function Triangle(cntx, canvs) {
this.ctx = cntx;
this.canv = canvs;
this.draw = drawTriangle;
}
function drawTriangle() {
requestAnimationFrame(drawTriangle);
var sides = 3;
var size = 100;
var centerX = this.canv.width / 2;
var centerY = this.canv.height / 2;
phase += 0.005 * tau;
this.ctx.clearRect(0, 0, this.canv.width, this.canv.height);
this.ctx.beginPath();
for (var i = 0; i <= sides; i++) {
this.ctx[i ? 'lineTo' : 'moveTo'](
centerX + size * Math.cos(phase + i / sides * tau),
centerY + size * Math.sin(phase + i / sides * tau)
);
}
this.ctx.stroke();
}
var triangle1 = new Triangle(context,canvas);
triangle1.draw();
The problem is that it just draws the triangle once so I am not really sure what I am doing wrong here.
Upvotes: 2
Views: 107
Reputation: 54026
You have two ways you can do it as a javascript object (don't think of them as classes).
First way using prototype to define object methods.
function Triangle(cntx, canvs) { // define the triange
this.ctx = cntx;
this.canv = canvs;
this.draw = this.drawTriangle.bind(this);
}
// this creates and compiles the draw function ready to be used for any Triangle object you create.
Triangle.prototype.drawTriangle = function() { // define the draw method as part of
// triangle's prototype
requestAnimationFrame(this.draw); // all properties of Triangle.prototype can be referenced via 'this'
var sides = 3;
var size = 100;
var centerX = this.canv.width / 2;
var centerY = this.canv.height / 2;
phase += 0.005 * tau;
this.ctx.clearRect(0, 0, this.canv.width, this.canv.height);
this.ctx.beginPath();
for (var i = 0; i <= sides; i++) {
this.ctx[i ? 'lineTo' : 'moveTo'](
centerX + size * Math.cos(phase + i / sides * tau),
centerY + size * Math.sin(phase + i / sides * tau));
}
this.ctx.stroke();
}
var triangle1 = new Triangle(context, canvas);
triangle1.draw();
Or you can create a safer Triangle with the following. In this case we minimise the use of the this token by using closure to encapsulate the variables we want ctx
and canv
are no longer exposed and only accessible from within the triangle object calls. Using closure is a little faster when running, abut slower at creation.
function Triangle(cntx, canvs) {
var ctx = cntx; // create closure vars
var canv = canvs;
this.draw = (function() { // draw run faster because it does not have
// to search the prototype for the values
// of ctx and canv.
requestAnimationFrame(this.draw);
var sides = 3;
var size = 100;
var centerX = this.canv.width / 2;
var centerY = this.canv.height / 2;
phase += 0.005 * tau;
ctx.clearRect(0, 0, canv.width, canv.height); // dont need the this keyword
ctx.beginPath();
for (var i = 0; i <= sides; i++) {
ctx[i ? 'lineTo' : 'moveTo'](
centerX + size * Math.cos(phase + i / sides * tau),
centerY + size * Math.sin(phase + i / sides * tau));
}
ctx.stroke();
}).bind(this); // because requestAnimationFrame sets this wee need to bind the current this to the method.
}
var triangle1 = new Triangle(context, canvas); // It takes a little longer to invoke
// because it will need to create and compile
// the draw function.
triangle1.draw(); // but call this now runs a little faster.
The differences in speed in this case is very minor, the advantage of one over the other will depend on how often you create the new object and how often you call its methods. Use prototype
if you create often, use closure if you create once and use often.
Upvotes: 0
Reputation: 11725
The problem here is that you're calling the requestAnimationFrame
and passing a callback to the same function, but the this
keyword will refer to the window
object, and not your class anymore.
So, you must explicit that you want to set the context of the callback function as the same context you are, and you can achieve that by calling .bind(this)
. Take a look at the example below:
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
var phase = 0;
var tau = 2 * Math.PI;
function Triangle(cntx, canvs) {
this.ctx = cntx;
this.canv = canvs;
this.draw = drawTriangle;
}
function drawTriangle() {
requestAnimationFrame(drawTriangle.bind(this));
var sides = 3;
var size = 100;
var centerX = this.canv.width / 2;
var centerY = this.canv.height / 2;
phase += 0.005 * tau;
this.ctx.clearRect(0, 0, this.canv.width, this.canv.height);
this.ctx.beginPath();
for (var i = 0; i <= sides; i++) {
this.ctx[i ? 'lineTo' : 'moveTo'](
centerX + size * Math.cos(phase + i / sides * tau),
centerY + size * Math.sin(phase + i / sides * tau)
);
}
this.ctx.stroke();
}
var triangle1 = new Triangle(context, canvas);
triangle1.draw();
<canvas></canvas>
Upvotes: 1