Calling requestAnimationFrame from inside a javascript object - javascript

I've hit a wall trying to figure this out. I'm new to OO Javascript and trying to put together my first class/object. I'm trying to create a canvas loader and it's not working. I've narrowed down the error to the requestAnimationWindow portion inside my animate function in my clock class. I get a Object [object global] has no method 'animate' error. Here is my code.
HTML:
<div id="loader"><canvas id="showLoader" width="250" height="250"></canvas><div id="showTimer"><p id="elapsedTime">
<script>
var clockTest = new clock(document.getElementById("showLoader"), 0, 100);
clockTest.animate();
</script>
</p></div></div>
Javascript:
function clock(canvas, curPerc, endPrecent){
var showPerc = document.getElementById("elapsedTime");
this.canvas = document.getElementById("showLoader");
var context = this.canvas.getContext('2d');
var x = this.canvas.width / 2;
var y = this.canvas.height / 2;
var radius = 75;
this.curPerc = 0;
this.endPercent = 110;
var counterClockwise = false;
var circ = Math.PI * 2;
var quart = Math.PI / 2;
context.lineWidth = 10;
context.strokeStyle = '#ed3f36';
this.animate = function(current) {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.context.beginPath();
this.context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
this.context.stroke();
this.curPerc++;
if(this.curPerc < this.endPercent) {
requestAnimationFrame(function () {
this.animate(curPerc / 100);
showPerc.innerHTML = this.curPerc + '%';
});
}
};
}
Any tips is appreciated. Thanks!

It is to do with the context of this in the anonymous function you pass to requestAnimationFrame, its not the this you think. Use a closure
i.e.
this.animate = function(current) {
var self = this; //<-- Create a reference to the this you want
self.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
/.. etc, etc..
if(self.curPerc < self.endPercent) {
requestAnimationFrame(function () {
self.animate(self.curPerc / 100); //<-- and use it here
showPerc.innerHTML = self.curPerc + '%'; //<-- and here
});
}
};
On a couple of other points, I would try to structure the object a bit better, you don't seem to be keeping reference to the properties correctly. The parameters you passed in , are not store on the object, and you are not storing the context correctly. Something like:
function clock(canvas, curPerc, endPrecent) {
var self = this;
// Set object properties here, i.e. from the parameters passed in
// Note also, anything that is a property (i.e. this. ) is public, can be accessed from otuside this object,
// whereas variable declared with var , are privte, can only be access within this object
self.canvas = canvas;
self.curPerc = curPerc;
self.endPercent = endPrecent;
self.context = self.canvas.getContext('2d'); //needs to be store like this, if you want to access below as this.context
self.context.lineWidth = 10;
self.context.strokeStyle = '#ed3f36';
//Private variables
var showPerc = document.getElementById("elapsedTime");
var x = self.canvas.width / 2;
var y = self.canvas.height / 2;
var radius = 75;
var counterClockwise = false;
var circ = Math.PI * 2;
var quart = Math.PI / 2;
//Methods
self.animate = function (current) {
self.context.clearRect(0, 0, self.canvas.width, self.canvas.height);
self.context.beginPath();
self.context.arc(x, y, radius, -(quart), ((circ) * current) - quart, false);
self.context.stroke();
self.curPerc++;
if (self.curPerc < self.endPercent) {
requestAnimationFrame(function () {
self.animate(curPerc / 100);
showPerc.innerHTML = self.curPerc + '%';
});
}
};
}
is starting to head in a better direction.

I ran into the same problem using three.js while calling requestAnimationFrame from inside an ES6 class method and the way I ended up solving it was by:
animate() {
requestAnimationFrame(() => this.animate());
this.render();
}

Related

How do I make sure that that I can make many balls animate within my draw function in Java Script?

I am trying to run my code so that the multiple balls apear on the screen. After debugging I found that my circleX and circleY variable is not appearing in my code. circlX is the location of ball I want CircleX and circleY passed into the drawManyBalls function and drawBallsMoving function.
I'm not sure why it's not running my code or not seeing these variables.
I tried using an apply method to try to apply the circlX and circleY variables, where I would run that function and then apply it to another function. like seen in this link.
I also tried running a function within a function and that didn't work.
check out my code on Js Fiddle: https://jsfiddle.net/Blummer92/kuj2tzy4/1/
Java Script
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
//every frame has been drawn to make it appear that the ball is moving
//this sets the moving ball
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
//every frame has been drawn to make it appear that the ball is moving
//this sets the moving ball
let circleX = canvas.width / 2;
let circleY = canvas.height - 30;
var ballRadius = 10;
// newly spawned objects start at Y=25
var spawnLineY = 25;
// spawn a new object every 1500ms
var spawnRate = 1500;
// set how fast the objects will fall
var spawnRateOfDescent = 0.5;
// when was the last object spawned
var lastSpawn = -1;
// this array holds all spawned object
var objects = [];
// save the starting time (used to calc elapsed time)
var startTime = Date.now();
var dx =2;
var dy =-2;
function spawnRandomObject(object) {
// select a random type for this new object
var t;
// About Math.random()
// Math.random() generates a semi-random number
// between 0-1. So to randomly decide if the next object
// will be A or B, we say if the random# is 0-.49 we
// create A and if the random# is .50-1.00 we create B
if (Math.random() < 0.50) {
t = "red";
} else {
t = "blue";
}
// create the new object
var object = {
// set this objects type
type: t,
// set x randomly but at least 15px off the canvas edges
x: Math.random() * (canvas.width - 30) + 15,
// set y to start on the line where objects are spawned
y: spawnLineY,
}
// add the new object to the objects[] array
objects.push(object);
}
let drawBall = (circleX, circleY) => {
ctx.beginPath();
let dimentions = ballRadius => {
ctx.arc(circleX, circleY, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
};
var drawManyBall = () => {
let CircleX = spawnRandomObject.object.X;
let CicleY = spawnRandomObject.object.Y;
drawBall();
drawBall.dimentions.apply(this, circleX, circleY); // use .apply() to call it
ctx.moveTo(circleX, circleY + spawnLineY);
ctx.lineTo(canvas.width, spawnLineY);
};
let draw = () => {
circleX += dx;
circleY += dy;
// get the elapsed time
var time = Date.now();
// see if its time to spawn a new object
if (time > lastSpawn + spawnRate) {
lastSpawn = time;
spawnRandomObject();
}
// request another animation frame
requestAnimationFrame(draw);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// runs a loop that
for (var i = 0; i < objects.length; i++) {
var object = objects[i];
//should I be using cirlce why as a part of my method?
object.Y += spawnRateOfDescent;
let CircleX = object.X;
let CicleY = object.Y;
var drawBallsmoving = () => {
drawBall();
drawBall.dimentions.apply(this, circleX, circleY);
ctx.beginPath();
ctx.arc(object.x, object.y, 8, 0, Math.PI * 2);
ctx.fillStyle = object.type;
};
}
During Debugging my output was
drawBall: (circleX, circleY) => {…}
arguments: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.invokeGetter (<anonymous>:1:142) at <anonymous>:1:1]
caller: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at Function.invokeGetter (<anonymous>:1:142) at <anonymous>:1:1]
length: 2

How to constantly generate a moving shape with Javascript and Canvas

I'm currently developing a small game for my capstone project. In the game, the user tries to avoid rectangles of random sizes the move from the right side of the screen to the left at a set speed.
It's built using object-oriented Javascript, and I've assigned it an anonymous function, however, I can't seem to get it to generate a shape and animate it more than the initial time the function is called. The problem can be solved if I create more than one object, but I would like this function to run automatically and generate more than just the first rectangle.
I've tried to call the function with an interval to force it to re-run the function with no results. I also attempted to separate the initialization function to call it with a parameter to generate the number of shapes given to it.
This is the function that generates the shape with the initial call, and determines the color, size, and location as well as draws it on the canvas.
var randomRectangle = function(){
this.init = function() {
this.speed = 4;
this.x = canvas.width-50;
this.y = Math.floor(Math.random()*280) + 40;
this.w = Math.floor(Math.random()*200) + 50;
this.h = Math.floor(Math.random()*150) + 20;
this.col = "#b5e61d";
}
this.move = function(){
this.x -= this.speed;
}
this.draw = function(num){
draw.rectangles(this.x, this.y, this.w, this.h, this.col);
}
};
This is where the object is initialized and the loop generates all objects and animations on the canvas.
randRecs = new randomRectangle();
randRecs.init();
function loop(){
draw.clear();
player.draw();
player.move();
wall1.draw();
wall2.draw();
randRecs.draw();
randRecs.move();
}
var handle = setInterval(loop, 30);
I expected the rectangle to continuously be generated at a new y-coordinate with a new size, then move from the right side of the screen to the left. However, only one rectangle is created and animated.
var list = [];
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var randomRectangle = function() {
this.init = function() {
this.speed = 4;
this.x = canvas.width - 50;
this.y = Math.floor(Math.random() * 280) + 40;
this.w = Math.floor(Math.random() * 200) + 50;
this.h = Math.floor(Math.random() * 150) + 20;
this.col = "#b5e61d";
}
this.move = function() {
this.x -= this.speed;
// restart x position to reuse rectangles
// you can change the y value here to a new random value
// or you can just remove with array.splice
if (this.x < -50) this.x = canvas.width - 50;
}
this.draw = function(num) {
draw.rectangles(this.x, this.y, this.w, this.h, this.col);
}
};
function loop() {
draw.clear();
//player.draw();
//player.move();
//wall1.draw();
//wall2.draw();
// call the methods draw and move for each rectangle on the list
for (var i=0; i<list.length; i++) {
rec = list[i];
rec.draw();
rec.move();
}
}
// spawn any number of new rects in a specific interval
var rectsPerSpawn = 1;
function addRects() {
for (var i=0; i<rectsPerSpawn; i++) {
if (list.length < 100) {
var rec = new randomRectangle();
list.push(rec);
rec.init();
}
}
}
// every half second will spawn a new rect
var spawn = setInterval(addRects, 500);
var draw = {
clear: function () {
ctx.clearRect(0,0,canvas.width,canvas.height);
},
rectangles: function (x, y, w, h, col) {
ctx.fillStyle = col;
ctx.fillRect(x,y,w,h);
}
}
var handle = setInterval(loop, 30);
<canvas></canvas>

Constructor not working properly in p5 js

I am having problems with p5 js.
I am trying to add two ellipse objects that spin around the circumference of a circle.
The code below represents the constructor function of the object :
function Obj(){
this.ang = TWO_PI;
this.x = w/2 + cos(ang)*r;
this.y = h/2 + sin(ang)*r;
this.fil = 255;
this.size = 28;
this.show = function(){
noStroke();
fill(this.fil);
ellipse(this.x,this.y,this.size,this.size);
}
this.update = function(){
this.ang+=.02;
}
}
And this is the main file :
let w = innerWidth;
let h = innerHeight;
let xd = [];
let r = 200;
function setup() {
createCanvas(w, h);
for (let i = 0; i < 2; i++)
xd[i] = new Obj();
}
function draw(){
background(0,70,80);
noFill();
strokeWeight(7);
stroke(255);
ellipse(w/2, h/2, r*2, r*2);
xd[0].update();
xd[0].show();
}
The problem is that it says that ang is not defined even though i did clearly define it with this.ang = TWO_PI;. And if I declare it in the main file and in setup() I say ang = TWO_PI; the object stays in place. Can anyone help ?
Thank you.
The problem in the constructor function in this code, it should be like this :
this.x = w/2 + cos(this.ang)*r;
this.y = h/2 + sin(this.ang)*r;
Because you are using a property from the constructor function itself

javascript and html canvas animation image emitter

Hello I have been watching some tutorials on HTML canvas and animations and I wanted to create my own. I am having trouble though. :/
I am trying to create some clouds that start on the left side of the screen and move to the right side of the screen, and will eventually disappear when they get to a certain point. I don't have that code yet. I don't know how to handle transparency. But, that is not where my troubles lie.
Currently, my clouds do not move. I can generate 20 different clouds in different locations but they are failing to move. I have checked my code with other tutorials and I can not seem to find why it's not working. Maybe because I am using an image?? If I could find some help I would really appreciate it. Thank you.
$(function(){
var leftcloudsrc = "ls/pics/cloud1.png";
var rightcloudsrc = "ls/pics/cloud2.png";
var canvas = document.getElementById('cloud');
var cw = canvas.width;
var ch = canvas.height;
var cloudsArray = [];
createclouds();
function createclouds(){
for (var i=0; i < 20; i++){
var x = Math.random() * 150;
var y = Math.random() * 300;
var v = Math.random() * 4;
cloudsArray.push(new Cloud(x, y, v));
}
animate();
console.log(cloudsArray);
}
function animate(){
requestAnimationFrame(animate);
for (var i = 0; i<cloudsArray.length; i++){
cloudsArray[i].move();
//new Cloud(x, y, v).create();
}
}
function Cloud(x, y, v){
this.x = x;
this.y = y;
this.v = v;
this.create = function(){
img = new Image,
ctx = document.getElementById('cloud').getContext('2d');
img.src = leftcloudsrc;
var iw = img.naturalWidth;
var ih = img.naturalHeight;
ctx.drawImage(img, x, y);
}
this.move = function(){
this.x += this.v;
this.create();
}
}
// var cloud = new Cloud(0,0,0);
// cloud.create();
});
i have tried writing to the console to make sure the information is saving and sticking, and yes, it is. i have even tried writing the .move() function to console to make sure the data changes, and it does. but it does not reflect visually???
ctx.drawImage(img, x, y); // wrong
ctx.drawImage(img, this.x, this.y); // right
You arent updating x and y. You are updating this.x and this.y
full code
// test
var leftcloudsrc = "http://www.freepngimg.com/download/cloud/10-cloud-png-image.png";
var rightcloudsrc = "ls/pics/cloud2.png";
var canvas = document.getElementById('cloud');
// you dont have to define ctx again and again
var ctx = canvas.getContext('2d');
var cw = canvas.width;
var ch = canvas.height;
var cloudsArray = [];
createclouds();
function createclouds() {
for (var i = 0; i < 20; i++) {
var x = Math.random() * 150;
var y = Math.random() * 300;
var v = Math.random() * 4;
cloudsArray.push(new Cloud(x, y, v));
}
animate();
console.log(cloudsArray);
}
function animate() {
requestAnimationFrame(animate);
ctx.clearRect(0, 0, cw, ch)
for (var i = 0; i < cloudsArray.length; i++) {
var c = cloudsArray[i]
c.move();
// remove when crosses the canvas width
if(c.x >= 500) {
cloudsArray.splice(i, 1);
}
//new Cloud(x, y, v).create();
}
}
function Cloud(x, y, v) {
this.x = x;
this.y = y;
this.v = v;
this.create = function() {
// invoke the constructor
var img = new Image;
img.src = leftcloudsrc;
var iw = img.naturalWidth;
var ih = img.naturalHeight;
ctx.drawImage(img, this.x, this.y);
}
this.move = function() {
this.x += this.v;
this.create();
}
}
<canvas id="cloud" width="500" height="400"></canvas>

Reusable JavaScript Component: How to make it?

I would like to create a reusable JavaScript component out of the following canvas spinner. Never done this before. How to achieve it and how to use the component?
http://codepen.io/anon/pen/tkpqc
HTML:
<canvas id="spinner"></canvas>
JS:
var canvas = document.getElementById('spinner');
var context = canvas.getContext('2d');
var start = new Date();
var lines = 8,
cW = context.canvas.width,
cH = context.canvas.height;
centerX = canvas.width / 2;
centerY = canvas.height / 2;
radius = 20;
var draw = function() {
var rotation = parseInt(((new Date() - start) / 1000) * lines) % lines;
context.save();
context.clearRect(0, 0, cW, cH);
for (var i = 0; i < lines; i++) {
context.beginPath();
//context.rotate(Math.PI * 2 / lines);
var rot = 2*Math.PI/lines;
var space = 2*Math.PI/(lines * 12);
context.arc(centerX,centerY,radius,rot * (i) + space,rot * (i+1) - space);
if (i == rotation)
context.strokeStyle="#ED3000";
else
context.strokeStyle="#CDCDCD";
context.lineWidth=10;
context.stroke();
}
context.restore();
};
window.setInterval(draw, 1000 / 30);
EDIT - SOLUTION:
Here comes a solution if anybody is interested
http://codepen.io/anon/pen/tkpqc
There are any number of ways to do this. Javascript is an object oriented language so you can easily write like:
var Spinner = function(canvas_context)
{
this.context = canvas_context;
// Whatever other properties you needed to create
this.timer = false;
}
Spinner.prototype.draw = function()
{
// Draw spinner
}
Spinner.prototype.start = function()
{
this.timer = setInterval(this.start, 1000 / 30);
}
Spinner.prototype.stop = function() {
clearInterval(this.timer);
}
Now you can use this object like so:
var canvas = document.getElementById('#canvas');
var context = canvas.getContext('2d');
var spinner = new Spinner(context);
spinner.start();
Basically, you are creating a class whose sole purpose in life is to draw a spinner on a canvas. In this example, you'll note that you're passing in the canvas's context into the object, since the details of the canvas itself is not relevant to this class's interest.

Categories