TypeError: this.canvas undefined

I am trying to write one of the dart daemons manually in javascript. I get a strange TypeError: this.canvas is undefined error arising from the draw function in the SolarSystem class using firebug and similar chrome. I can’t understand why this is happening.

Here is a link to my code in JS Bin so you can play with it. http://jsbin.com/udujep/1/edit

For posterity, here is the full javascript code:

 var main, fpsAverage, showFps, Point, SolarSystem, PlanetaryBody; main = function(){ var solarSystem; solarSystem = new SolarSystem(document.getElementById('container')); solarSystem.start(); }; showFps = function(fps){ if (fpsAverage != null) { fpsAverage = fps; } fpsAverage = fps * 0.05 + fpsAverage * 0.95; document.getElementById('notes').textContent = Math.round(fpsAverage) + ' fps'; }; Point = (function(){ Point.displayName = 'Point'; var prototype = Point.prototype, constructor = Point; function Point(x, y){ var ref$; ref$ = [x, y], this.x = ref$[0], this.y = ref$[1]; } return Point; }()); SolarSystem = (function(){ SolarSystem.displayName = 'SolarSystem'; var prototype = SolarSystem.prototype, constructor = SolarSystem; prototype.canvas = null; prototype.renderTime = null; function SolarSystem(canvas){ this.canvas = canvas; } prototype.start = function(){ this.width = this.canvas.parentNode.clientWidth; this.height = this.canvas.parentNode.clientWidth; this.canvas.width = this.width; this._start(); }; prototype._start = function(){ var earth, f, h, g, jupiter; this.sun = new PlanetaryBody(this, 'Sun', '#ff2', 14.0); this.sun.addPlanet(new PlanetaryBody(this, 'Mercury', 'orange', 0.382, 0.387, 0.241)); this.sun.addPlanet(new PlanetaryBody(this, 'Venus', 'green', 0.949, 0.723, 0.615)); earth = new PlanetaryBody(this, 'Earth', '#33f', 1.0, 1.0, 1.0); this.sun.addPlanet(earth); earth.addPlanet(new PlanetaryBody(this, 'Moon', 'gray', 0.2, 0.14, 0.075)); this.sun.addPlanet(new PlanetaryBody(this, 'Mars', 'red', 0.532, 1.524, 1.88)); this.addAsteroidBelt(this.sun, 150); f = 0.1; h = 1 / 1500.0; g = 1 / 72.0; jupiter = new PlanetaryBody(this, 'Jupiter', 'gray', 4.0, 5.203, 11.86); this.sun.addPlanet(jupiter); jupiter.addPlanet(new PlanetaryBody(this, 'Io', 'gray', 3.6 * f, 421 * h, 1.769 * g)); jupiter.addPlanet(new PlanetaryBody(this, 'Europa', 'gray', 3.1 * f, 671 * h, 3.551 * g)); jupiter.addPlanet(new PlanetaryBody(this, 'Ganymede', 'gray', 5.3 * f, 1070 * h, 7.154 * g)); jupiter.addPlanet(new PlanetaryBody(this, 'Callisto', 'gray', 4.8 * f, 1882 * h, 16.689 * g)); this.requestRedraw(); }; prototype.draw = function(){ var time, context; time = Date.now(); if (this.renderTime != null) { showFps(Math.round(1000 / (time - this.renderTime))); } this.renderTime = time; context = this.canvas.getContext('2d'); this.drawBackground(context); this.drawPlanets(context); this.requestRedraw(); }; prototype.drawBackground = function(context){ var x$; x$ = context; x$.fillStyle = 'white'; x$.rect(0, 0, this.width, this.height); x$.fill(); }; prototype.drawPlanets = function(context){ this.sun.draw(context, this.width / 2, this.height / 2); }; prototype.requestRedraw = function(){ window.requestAnimationFrame(this.draw); }; prototype.addAsteroidBelt = function(body, count){ var i$, radius; for (i$ = 0; i$ < count; ++i$) { radius = 2.06 + Math.random() * (3.27 - 2.06); body.addPlanet(new PlanetaryBody(this, 'asteroid', '#777', 0.1 * Math.random(), radius, radius * 2)); } }; prototype.normalizeOrbitRadius = function(r){ return r * (this.width / 10.0); }; prototype.normalizePlanetSize = function(r){ return Math.log(r + 1) * (this.width / 100.0); }; return SolarSystem; }()); PlanetaryBody = (function(){ PlanetaryBody.displayName = 'PlanetaryBody'; var prototype = PlanetaryBody.prototype, constructor = PlanetaryBody; prototype.planets = []; function PlanetaryBody(solarSystem, name, color, bodySize, orbitRadius, orbitPeriod){ orbitRadius == null && (orbitRadius = 0.0); orbitPeriod == null && (orbitPeriod = 0.0); this.solarSystem = solarSystem; this.name = name; this.color = color; this.orbitPeriod = orbitPeriod; this.bodySize = solarSystem.normalizePlanetSize(bodySize); this.orbitRadius = solarSystem.normalizeOrbitRadius(orbitRadius); this.orbitSpeed = prototype._calculateSpeed(orbitPeriod); } prototype.addPlanet = function(planet){ this.planets.push(planet); }; prototype.draw = function(context, x, y){ var pos; pos = this._calculatePos(x, y); this.drawSelf(context, pos.x, pos.y); this.drawChildren(context, pos.x, pos.y); }; prototype.drawSelf = function(context, x, y){ var x$; x$ = context; x$.save(); try { x$.lineWidth = 0.5; x$.fillStyle = this.color; x$.strokeStyle = this.color; if (this.bodySize >= 2.0) { x$.shadowOffsetX = 2; x$.shadowOffsetY = 2; x$.shadowBlur = 2; x$.shadowColor = '#ddd'; } x$.beginPath(); x$.arc(x, y, this.bodySize, 0, Math.PI * 2, false); x$.fill(); x$.closePath(); x$.stroke(); x$.shadowOffsetX = 0; x$.shadowOffsetY = 0; x$.shadowBlur = 0; x$.beginPath(); x$.arc(x, y, this.bodySize, 0, Math.PI * 2, false); x$.fill(); x$.closePath(); x$.stroke(); } finally { x$.restore(); } }; prototype.drawChildren = function(context, x, y){ var i$, ref$, len$, planet; for (i$ = 0, len$ = (ref$ = this.planets).length; i$ < len$; ++i$) { planet = ref$[i$]; planet.draw(context, x, y); } }; prototype._calculateSpeed = function(period){ if (period === 0.0) { return 0.0; } else { return 1 / (60.0 * 24.0 * 2 * period); } }; prototype._calculatePos = function(x, y){ var angle; if (this.orbitSpeed === 0.0) { return new Point(x, y); } else { angle = this.solarSystem.renderTime * this.orbitSpeed; return new Point(this.orbitRadius * Math.cos(angle) + x, this.orbitRadius * Math.sin(angle) + y); } }; return PlanetaryBody; }()); window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; window.onload = main; 
+4
source share
2 answers

The problem is this line.

 window.requestAnimationFrame(this.draw); 

What happens when you pass the raw draw method to requestAnimationFrame , you lose the SolarSystem context. For example, if you try this in draw , it will return true.

 alert(this === window) 

The solution is simple, change the context using closure.

 prototype.requestRedraw = function(){ var self = this; window.requestAnimationFrame(function () {self.draw()}); }; 
+2
source

If you are using ES6

You can use the arrow functions, if you use ES6, it stores your this in the area.

 window.requestAnimationFrame(() => this.draw()); 

or if you use setInterval

 setInterval(() => app.draw(), 10); 
0
source

Source: https://habr.com/ru/post/1444989/


All Articles