2012-11-09 14 views
4

자바 스크립트에서 손으로 다트 데모 중 하나를 쓰려고합니다. firebug와 비슷한 크롬을 사용하여 SolarSystem 클래스의 draw 함수에서 발생하는 이상한 TypeError: this.canvas is undefined 오류가 발생합니다. 왜 이런 일이 일어나는지 알 수 없습니다.TypeError : this.canvas가 정의되지 않았습니다.

여기 JS Bin의 코드에 대한 링크가 있으므로 함께 놀 수 있습니다. http://jsbin.com/udujep/1/edit

후세를 들어, 다음은 전체 자바 스크립트 코드입니다 :

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; 

답변

2

문제는 여기이 선입니다. 무슨 일

window.requestAnimationFrame(this.draw); 

requestAnimationFrame에 원시 draw 방법을 통과 할 때 SolarSystem의 컨텍스트를 잃을 것입니다. 예를 들어 draw에서 이것을 시도하면 true를 반환합니다.

alert(this === window) 

해결책은 간단합니다. 클로저를 사용하여 컨텍스트를 변경하십시오.

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

당신이 ES6를 사용하는 경우에는 화살표 기능을 사용할 수 있습니다 ES6

사용하는 경우, 그것은 범위에 this을 유지합니다.

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

또는 setInterval을 사용하는 경우

setInterval(() => app.draw(), 10); 
관련 문제