Import files for Black Hole Poem.
Chris Pressey
10 years ago
0 | "use strict"; | |
1 | ||
2 | function launch(prefix, containerId, config) { | |
3 | var config = config || {}; | |
4 | var deps = [ | |
5 | "element-factory.js", | |
6 | "animation.js", | |
7 | "sprite-manager.js", | |
8 | "canvas-resizer.js" | |
9 | ]; | |
10 | var loaded = 0; | |
11 | for (var i = 0; i < deps.length; i++) { | |
12 | var elem = document.createElement('script'); | |
13 | elem.src = prefix + deps[i]; | |
14 | elem.onload = function() { | |
15 | if (++loaded == deps.length) { | |
16 | var container = document.getElementById(containerId); | |
17 | var t = new BlackHolePoem(); | |
18 | var initialized = false; | |
19 | config.canvas = yoob.makeCanvas(container, 800, 450); | |
20 | var cr = (new yoob.CanvasResizer()).init({ | |
21 | canvas: config.canvas, | |
22 | onResizeEnd: function() { | |
23 | if (!initialized) { | |
24 | t.init(config); | |
25 | initialized = true; | |
26 | } | |
27 | t.draw(); | |
28 | }, | |
29 | desiredWidth: 800, | |
30 | desiredHeight: 450 | |
31 | }).register(); | |
32 | } | |
33 | }; | |
34 | document.body.appendChild(elem); | |
35 | } | |
36 | } | |
37 | ||
38 | var makeText = function(cfg) { | |
39 | var sprite = (new yoob.Sprite()).init({ | |
40 | x: cfg.x, | |
41 | y: cfg.y, | |
42 | width: 30, | |
43 | height: 30, | |
44 | isDraggable: true | |
45 | }); | |
46 | ||
47 | sprite.font = cfg.font || "64px Arial,Sans-serif"; | |
48 | sprite.text = cfg.text; | |
49 | sprite.anchorX = cfg.anchorX; | |
50 | sprite.anchorY = cfg.anchorY; | |
51 | ||
52 | sprite.draw = function(ctx) { | |
53 | ctx.fillStyle = "#432e2a"; | |
54 | ctx.fillRect(this.getLeftX(), this.getTopY(), this.getWidth(), this.getHeight()); | |
55 | ||
56 | ctx.textBaseline = "middle"; | |
57 | ctx.font = this.font; | |
58 | ctx.fillStyle = "black"; | |
59 | ||
60 | var x = this.getX(); | |
61 | var endX = this.anchorX(); | |
62 | var y = this.getY(); | |
63 | var endY = this.anchorY(); | |
64 | ||
65 | for (var i = 0; i < this.text.length; i++) { | |
66 | var c = this.text.charAt(i); | |
67 | if (c===' ') continue; | |
68 | var width = ctx.measureText(c).width; | |
69 | var textX = x - width / 2; | |
70 | ctx.fillText(c, textX, y); | |
71 | x += (endX - x) / 2; | |
72 | y += (endY - y) / 2; | |
73 | } | |
74 | }; | |
75 | ||
76 | cfg.manager.addSprite(sprite); | |
77 | return sprite; | |
78 | }; | |
79 | ||
80 | var BlackHolePoem = function() { | |
81 | var canvas; | |
82 | var ctx; | |
83 | var manager; | |
84 | var texts; | |
85 | ||
86 | this.draw = function() { | |
87 | // Illuminant E: #D3BEBA | |
88 | ctx.fillStyle = "#816660"; | |
89 | ctx.fillRect(0, 0, canvas.width, canvas.height); | |
90 | manager.draw(ctx); | |
91 | }; | |
92 | ||
93 | this.update = function() { | |
94 | }; | |
95 | ||
96 | this.init = function(config) { | |
97 | canvas = config.canvas; | |
98 | ctx = canvas.getContext("2d"); | |
99 | ||
100 | var anchorX = function() { return canvas.width / 2; }; | |
101 | var anchorY = function() { return canvas.height / 2; }; | |
102 | ||
103 | manager = (new yoob.SpriteManager()).init({ canvas: canvas }); | |
104 | texts = []; | |
105 | texts.push(makeText({ | |
106 | text: "A billion light-years", | |
107 | manager: manager, | |
108 | x: 20, | |
109 | y: 20, | |
110 | anchorX: anchorX, | |
111 | anchorY: anchorY | |
112 | })); | |
113 | ||
114 | texts.push(makeText({ | |
115 | text: "Distant and unseen", | |
116 | manager: manager, | |
117 | x: canvas.width - 20, | |
118 | y: canvas.height - 20, | |
119 | anchorX: anchorX, | |
120 | anchorY: anchorY | |
121 | })); | |
122 | ||
123 | texts.push(makeText({ | |
124 | text: "Relative to nothing", | |
125 | manager: manager, | |
126 | x: 20, | |
127 | y: canvas.height - 20, | |
128 | anchorX: anchorX, | |
129 | anchorY: anchorY | |
130 | })); | |
131 | ||
132 | texts.push(makeText({ | |
133 | text: "Unequalled forces", | |
134 | manager: manager, | |
135 | x: canvas.width - 20, | |
136 | y: 20, | |
137 | anchorX: anchorX, | |
138 | anchorY: anchorY | |
139 | })); | |
140 | ||
141 | var $this = this; | |
142 | $this.animation = (new yoob.Animation()).init({ | |
143 | object: $this | |
144 | }); | |
145 | $this.animation.start(); | |
146 | }; | |
147 | }; |
0 | <!DOCTYPE html> | |
1 | <head> | |
2 | <meta charset="utf-8"> | |
3 | <title>Black Hole Poem</title> | |
4 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
5 | <style> | |
6 | html, body, div { | |
7 | width: 100%; | |
8 | text-align: center; | |
9 | margin: 0; | |
10 | padding: 0; | |
11 | line-height: 0; | |
12 | } | |
13 | </style> | |
14 | </head> | |
15 | <body> | |
16 | ||
17 | <h1>Black Hole Poem</h1> | |
18 | ||
19 | <div id="container"></div> | |
20 | ||
21 | </body> | |
22 | <script src="black-hole-poem.js"></script> | |
23 | <script> | |
24 | launch('../common-yoob.js-0.9/', 'container'); | |
25 | </script> |
0 | /* | |
1 | * This file is part of yoob.js version 0.9-PRE | |
2 | * Available from https://github.com/catseye/yoob.js/ | |
3 | * This file is in the public domain. See http://unlicense.org/ for details. | |
4 | */ | |
5 | if (window.yoob === undefined) yoob = {}; | |
6 | ||
7 | /* | |
8 | * This class provides objects that resize a canvas to fill (or be centered in) | |
9 | * an area in the viewport, with several options. | |
10 | * | |
11 | * See here for the main use cases I wanted to address: | |
12 | * https://gist.github.com/cpressey/0e2d7f8f9a9a28c863ec | |
13 | * | |
14 | * You don't really need this if all you want is a full-viewport canvas; | |
15 | * that's easy enough to do with CSS and a simple onresize handler (see | |
16 | * above article.) But it does accomodate that if you want. | |
17 | */ | |
18 | yoob.CanvasResizer = function() { | |
19 | /* | |
20 | * Initializes this CanvasResizer and returns it. Does not hook into | |
21 | * any DOM events, so generally you want to call .register() afterwards. | |
22 | * | |
23 | * `canvas`: the canvas to resize | |
24 | * | |
25 | * `onResizeStart`: an optional function which, if supplied, will be | |
26 | * called, passing the new width and height as parameters, after the | |
27 | * new canvas size has been computed, but before the canvas is actually | |
28 | * resized. It may return the exact object `false` to cancel the resize. | |
29 | * | |
30 | * `onResizeEnd`: an optional function which, if supplied, will be | |
31 | * called after the canvas has actually been resized. This can be | |
32 | * used to, for example, redraw the canvas contents. | |
33 | * | |
34 | * `desired{Width,Height}`: the desired width and height of the canvas | |
35 | * | |
36 | * `redimensionCanvas`: should we set the canvas's width and height | |
37 | * properties to the clientWidth and clientHeight of the element | |
38 | * after it has been resized? defaults to true. | |
39 | * | |
40 | * `preserveAspectRatio`: should we try to preserve the aspect ratio | |
41 | * of the canvas after resizing? defaults to true. | |
42 | * | |
43 | * `allowExpansion`: should we ever resize the canvas to a size larger | |
44 | * than the desired width & height? defaults to false. | |
45 | * | |
46 | * `allowContraction`: should we ever resize the canvas to a size smaller | |
47 | * than the desired width & height? defaults to true. | |
48 | * | |
49 | * `missingCanvasElement`: if allowContraction is false, this should be | |
50 | * a DOM element whose `display` style will be changed from `none` to | |
51 | * `inline-block` when the viewport is too small to display the canvas. | |
52 | * | |
53 | * `centerVertically`: should we apply a top margin to the canvas | |
54 | * element, to equal half the available space below it, after resizing | |
55 | * it? defaults to defaults to true. | |
56 | */ | |
57 | this.init = function(cfg) { | |
58 | var nop = function() {}; | |
59 | this.canvas = cfg.canvas; | |
60 | this.redraw = cfg.redraw || nop; | |
61 | this.onResizeStart = cfg.onResizeStart || nop; | |
62 | this.onResizeEnd = cfg.onResizeEnd || nop; | |
63 | this.desiredWidth = cfg.desiredWidth || null; | |
64 | this.desiredHeight = cfg.desiredHeight || null; | |
65 | this.redimensionCanvas = cfg.redimensionCanvas === false ? false : true; | |
66 | this.preserveAspectRatio = cfg.preserveAspectRatio === false ? false : true; | |
67 | this.allowExpansion = !!cfg.allowExpansion; | |
68 | this.allowContraction = cfg.allowContraction === false ? false : true; | |
69 | this.missingCanvasElement = cfg.missingCanvasElement; | |
70 | this.centerVertically = cfg.centerVertically === false ? false : true; | |
71 | return this; | |
72 | }; | |
73 | ||
74 | this.register = function(w) { | |
75 | var $this = this; | |
76 | var resizeCanvas = function(e) { | |
77 | $this.resizeCanvas(e); | |
78 | }; | |
79 | window.addEventListener("load", resizeCanvas); | |
80 | window.addEventListener("resize", resizeCanvas); | |
81 | // TODO: orientationchange? | |
82 | return this; | |
83 | }; | |
84 | ||
85 | /* | |
86 | * Returns a two-element list, containing the width and height of the | |
87 | * available space in the viewport, measured from the upper-left corner | |
88 | * of the given element. | |
89 | */ | |
90 | this.getAvailableSpace = function(elem) { | |
91 | var rect = elem.getBoundingClientRect(); | |
92 | var absTop = Math.round(rect.top + window.pageYOffset); | |
93 | var absLeft = Math.round(rect.left + window.pageXOffset); | |
94 | var html = document.documentElement; | |
95 | var availWidth = html.clientWidth - absLeft * 2; | |
96 | var availHeight = html.clientHeight - (absTop + absLeft * 2); | |
97 | return [availWidth, availHeight]; | |
98 | }; | |
99 | ||
100 | /* | |
101 | * Given a destination width and height, return the scaling factor | |
102 | * which is needed to scale the desired width and height to that | |
103 | * destination rectangle. | |
104 | */ | |
105 | this.getFitScale = function(destWidth, destHeight) { | |
106 | var widthFactor = this.desiredWidth / destWidth; | |
107 | var heightFactor = this.desiredHeight / destHeight; | |
108 | return 1 / Math.max(widthFactor, heightFactor); | |
109 | }; | |
110 | ||
111 | this.resizeCanvas = function() { | |
112 | var avail = this.getAvailableSpace(this.canvas.parentElement); | |
113 | var availWidth = avail[0]; | |
114 | var availHeight = avail[1]; | |
115 | var newWidth = availWidth; | |
116 | var newHeight = availHeight; | |
117 | ||
118 | if (this.preserveAspectRatio) { | |
119 | var scale = this.getFitScale(availWidth, availHeight); | |
120 | if (!this.allowExpansion) { | |
121 | scale = Math.min(scale, 1); | |
122 | } | |
123 | if (!this.allowContraction) { | |
124 | scale = Math.max(scale, 1); | |
125 | } | |
126 | newWidth = Math.trunc(this.desiredWidth * scale); | |
127 | newHeight = Math.trunc(this.desiredHeight * scale); | |
128 | } else { | |
129 | // if we don't care about preserving the aspect ratio but do | |
130 | // care about preserving the size, clamp each dimension | |
131 | if (!this.allowExpansion) { | |
132 | newWidth = Math.min(newWidth, this.desiredWidth); | |
133 | newHeight = Math.min(newHeight, this.desiredHeight); | |
134 | } | |
135 | if (!this.allowContraction) { | |
136 | newWidth = Math.max(newWidth, this.desiredWidth); | |
137 | newHeight = Math.max(newHeight, this.desiredHeight); | |
138 | } | |
139 | } | |
140 | ||
141 | if (newWidth > availWidth || newHeight > availHeight) { | |
142 | // due to not allowing contraction, our canvas is still | |
143 | // too big to display. hide it and show the other thing | |
144 | this.canvas.style.display = 'none'; | |
145 | if (this.missingCanvasElement) { | |
146 | this.missingCanvasElement.style.display = 'inline-block'; | |
147 | } | |
148 | return; | |
149 | } | |
150 | ||
151 | this.canvas.style.display = 'inline-block'; | |
152 | if (this.missingCanvasElement) { | |
153 | this.missingCanvasElement.style.display = 'none'; | |
154 | } | |
155 | ||
156 | var result = this.onResizeStart(newWidth, newHeight); | |
157 | if (result === false) { | |
158 | return; | |
159 | } | |
160 | ||
161 | if (true) { | |
162 | // TODO: add an option to skip this part...? | |
163 | // you might want to skip it if you have these as %'s | |
164 | this.canvas.style.width = newWidth + "px"; | |
165 | this.canvas.style.height = newHeight + "px"; | |
166 | } | |
167 | ||
168 | this.canvas.style.marginTop = "0"; | |
169 | if (this.centerVertically) { | |
170 | if (availHeight > newHeight) { | |
171 | this.canvas.style.marginTop = | |
172 | Math.trunc((availHeight - newHeight) / 2) + "px"; | |
173 | } | |
174 | } | |
175 | ||
176 | var changed = false; | |
177 | if (this.redimensionCanvas) { | |
178 | if (this.canvas.width !== newWidth || this.canvas.height !== newHeight) { | |
179 | this.canvas.width = newWidth; | |
180 | this.canvas.height = newHeight; | |
181 | changed = true; | |
182 | } | |
183 | } | |
184 | ||
185 | this.onResizeEnd(newWidth, newHeight, changed); | |
186 | }; | |
187 | }; |
0 | /* | |
1 | * This file is part of yoob.js version 0.9-PRE | |
2 | * Available from https://github.com/catseye/yoob.js/ | |
3 | * This file is in the public domain. See http://unlicense.org/ for details. | |
4 | */ | |
5 | if (window.yoob === undefined) yoob = {}; | |
6 | ||
7 | /* | |
8 | * This is really just an interface; duck-typing-wise, you can use any | |
9 | * Javascript object you want as a sprite, so long as it exposes these | |
10 | * methods. | |
11 | */ | |
12 | yoob.Sprite = function() { | |
13 | ||
14 | /* | |
15 | * x and y always represent the CENTRE of the Sprite(). | |
16 | * Chainable. | |
17 | */ | |
18 | this.init = function(cfg) { | |
19 | this.x = cfg.x; | |
20 | this.y = cfg.y; | |
21 | this.width = cfg.width; | |
22 | this.height = cfg.height; | |
23 | this.dx = cfg.dx || 0; | |
24 | this.dy = cfg.dy || 0; | |
25 | this.isDraggable = cfg.isDraggable || false; | |
26 | this.isClickable = cfg.isClickable || false; | |
27 | this.fillStyle = cfg.fillStyle || "green"; | |
28 | this.visible = (cfg.visible === undefined ? true : (!!cfg.visible)); | |
29 | this._isBeingDragged = false; | |
30 | return this; | |
31 | }; | |
32 | ||
33 | this.getX = function() { | |
34 | return this.x; | |
35 | }; | |
36 | ||
37 | this.getLeftX = function() { | |
38 | return this.x - this.width / 2; | |
39 | }; | |
40 | ||
41 | this.getRightX = function() { | |
42 | return this.x + this.width / 2; | |
43 | }; | |
44 | ||
45 | this.getY = function() { | |
46 | return this.y; | |
47 | }; | |
48 | ||
49 | this.getTopY = function() { | |
50 | return this.y - this.height / 2; | |
51 | }; | |
52 | ||
53 | this.getBottomY = function() { | |
54 | return this.y + this.height / 2; | |
55 | }; | |
56 | ||
57 | this.getWidth = function() { | |
58 | return this.width; | |
59 | }; | |
60 | ||
61 | this.getHeight = function() { | |
62 | return this.height; | |
63 | }; | |
64 | ||
65 | this.isBeingDragged = function() { | |
66 | return this._isBeingDragged; | |
67 | }; | |
68 | ||
69 | /* | |
70 | * Chainable. | |
71 | */ | |
72 | this.setPosition = function(x, y) { | |
73 | this.x = x; | |
74 | this.y = y; | |
75 | return this; | |
76 | }; | |
77 | ||
78 | /* | |
79 | * Chainable. | |
80 | */ | |
81 | this.setDimensions = function(width, height) { | |
82 | this.width = width; | |
83 | this.height = height; | |
84 | return this; | |
85 | }; | |
86 | ||
87 | /* | |
88 | * Chainable. | |
89 | */ | |
90 | this.setVelocity = function(dx, dy) { | |
91 | this.dx = dx; | |
92 | this.dy = dy; | |
93 | return this; | |
94 | }; | |
95 | ||
96 | /* | |
97 | * Chainable. | |
98 | */ | |
99 | this.setDestination = function(x, y, ticks) { | |
100 | this.destX = x; | |
101 | this.destY = y; | |
102 | this.setVelocity((this.destX - this.getX()) / ticks, (this.destY - this.getY()) / ticks); | |
103 | this.destCounter = ticks; | |
104 | return this; | |
105 | }; | |
106 | ||
107 | this.move = function(x, y) { | |
108 | this.x += this.dx; | |
109 | this.y += this.dy; | |
110 | this.onmove(); | |
111 | if (this.destCounter !== undefined) { | |
112 | this.destCounter--; | |
113 | if (this.destCounter <= 0) { | |
114 | this.destCounter = undefined; | |
115 | this.setPosition(this.destX, this.destY).setVelocity(0, 0); | |
116 | this.onreachdestination(); | |
117 | } | |
118 | } | |
119 | }; | |
120 | ||
121 | // override this if your shape is not a rectangle | |
122 | this.containsPoint = function(x, y) { | |
123 | return (x >= this.getLeftX() && x <= this.getRightX() && | |
124 | y >= this.getTopY() && y <= this.getBottomY()); | |
125 | }; | |
126 | ||
127 | // you may need to override this in a sophisticated way if you | |
128 | // expect it to detect sprites of different shapes intersecting | |
129 | this.intersects = function(sprite) { | |
130 | var x1 = this.getLeftX(); | |
131 | var x2 = this.getRightX(); | |
132 | var y1 = this.getTopY(); | |
133 | var y2 = this.getBottomY(); | |
134 | return (sprite.containsPoint(x1, y1) || sprite.containsPoint(x2, y1) || | |
135 | sprite.containsPoint(x1, y2) || sprite.containsPoint(x2, y2)); | |
136 | }; | |
137 | ||
138 | // you will probably want to override this | |
139 | // if you do, it's up to you to honour this.visible. | |
140 | this.draw = function(ctx) { | |
141 | if (!this.visible) return; | |
142 | ctx.fillStyle = this.fillStyle || "green"; | |
143 | ctx.fillRect(this.getLeftX(), this.getTopY(), this.getWidth(), this.getHeight()); | |
144 | }; | |
145 | ||
146 | // event handlers. override to detect these events. | |
147 | this.ongrab = function() { | |
148 | }; | |
149 | this.ondrag = function() { | |
150 | }; | |
151 | this.ondrop = function() { | |
152 | }; | |
153 | this.onclick = function() { | |
154 | }; | |
155 | this.onmove = function() { | |
156 | }; | |
157 | this.onreachdestination = function() { | |
158 | }; | |
159 | ||
160 | }; | |
161 | ||
162 | /* | |
163 | * This still has a few shortcomings at the moment. | |
164 | */ | |
165 | yoob.SpriteManager = function() { | |
166 | /* | |
167 | * Attach this SpriteManager to a canvas. | |
168 | */ | |
169 | this.init = function(cfg) { | |
170 | this.canvasX = undefined; | |
171 | this.canvasY = undefined; | |
172 | this.offsetX = undefined; | |
173 | this.offsetY = undefined; | |
174 | this.dragging = undefined; | |
175 | this.sprites = []; | |
176 | ||
177 | this.canvas = cfg.canvas; | |
178 | ||
179 | var $this = this; | |
180 | this.canvas.addEventListener('mousedown', function(e) { | |
181 | return $this.onmousedown(e, e); | |
182 | }); | |
183 | this.canvas.addEventListener('touchstart', function(e) { | |
184 | return $this.onmousedown(e, e.touches[0]); | |
185 | }); | |
186 | ||
187 | this.canvas.addEventListener('mousemove', function(e) { | |
188 | return $this.onmousemove(e, e); | |
189 | }); | |
190 | this.canvas.addEventListener('touchmove', function(e) { | |
191 | return $this.onmousemove(e, e.touches[0]); | |
192 | }); | |
193 | ||
194 | this.canvas.addEventListener('mouseup', function(e) { | |
195 | return $this.onmouseup(e, e); | |
196 | }); | |
197 | this.canvas.addEventListener('touchend', function(e) { | |
198 | return $this.onmouseup(e, e.touches[0]); | |
199 | }); | |
200 | ||
201 | return this; | |
202 | }; | |
203 | ||
204 | /* | |
205 | * Common handling of mouse and touch events | |
206 | */ | |
207 | this.onmousedown = function(e, touch) { | |
208 | e.preventDefault(); | |
209 | this.canvasX = touch.pageX - this.canvas.offsetLeft; | |
210 | this.canvasY = touch.pageY - this.canvas.offsetTop; | |
211 | ||
212 | var sprite = this.getSpriteAt(this.canvasX, this.canvasY); | |
213 | if (sprite === undefined) return; | |
214 | if (sprite.isDraggable) { | |
215 | this.dragging = sprite; | |
216 | this.dragging._isBeingDragged = true; | |
217 | this.dragging.ongrab(); | |
218 | this.offsetX = sprite.getX() - this.canvasX; | |
219 | this.offsetY = sprite.getY() - this.canvasY; | |
220 | this.canvas.style.cursor = "move"; | |
221 | } else if (sprite.isClickable) { | |
222 | sprite.onclick(e); | |
223 | } | |
224 | }; | |
225 | ||
226 | this.onmousemove = function(e, touch) { | |
227 | e.preventDefault(); | |
228 | if (!this.dragging) return; | |
229 | ||
230 | this.canvasX = touch.pageX - this.canvas.offsetLeft; | |
231 | this.canvasY = touch.pageY - this.canvas.offsetTop; | |
232 | ||
233 | this.dragging.setPosition(this.canvasX + this.offsetX, | |
234 | this.canvasY + this.offsetY); | |
235 | }; | |
236 | ||
237 | this.onmouseup = function(e, touch) { | |
238 | e.preventDefault(); | |
239 | this.canvas.onmousemove = null; | |
240 | this.canvasX = undefined; | |
241 | this.canvasY = undefined; | |
242 | this.offsetX = undefined; | |
243 | this.offsetY = undefined; | |
244 | if (this.dragging !== undefined) { | |
245 | this.dragging.ondrop(); | |
246 | this.dragging._isBeingDragged = false; | |
247 | } | |
248 | this.dragging = undefined; | |
249 | this.canvas.style.cursor = "auto"; | |
250 | }; | |
251 | ||
252 | this.move = function() { | |
253 | this.foreach(function(sprite) { | |
254 | sprite.move(); | |
255 | }); | |
256 | }; | |
257 | ||
258 | this.draw = function(ctx) { | |
259 | if (ctx === undefined) { | |
260 | ctx = this.canvas.getContext('2d'); | |
261 | } | |
262 | this.foreach(function(sprite) { | |
263 | sprite.draw(ctx); | |
264 | }); | |
265 | }; | |
266 | ||
267 | this.addSprite = function(sprite) { | |
268 | this.sprites.push(sprite); | |
269 | }; | |
270 | ||
271 | this.removeSprite = function(sprite) { | |
272 | var index = undefined; | |
273 | for (var i = 0; i < this.sprites.length; i++) { | |
274 | if (this.sprites[i] === sprite) { | |
275 | index = i; | |
276 | break; | |
277 | } | |
278 | } | |
279 | if (index !== undefined) { | |
280 | this.sprites.splice(index, 1); | |
281 | } | |
282 | }; | |
283 | ||
284 | this.moveToFront = function(sprite) { | |
285 | this.removeSprite(sprite); | |
286 | this.sprites.push(sprite); | |
287 | }; | |
288 | ||
289 | this.moveToBack = function(sprite) { | |
290 | this.removeSprite(sprite); | |
291 | this.sprites.unshift(sprite); | |
292 | }; | |
293 | ||
294 | this.getSpriteAt = function(x, y) { | |
295 | for (var i = this.sprites.length-1; i >= 0; i--) { | |
296 | var sprite = this.sprites[i]; | |
297 | if (sprite.containsPoint(x, y)) { | |
298 | return sprite; | |
299 | } | |
300 | } | |
301 | return undefined; | |
302 | }; | |
303 | ||
304 | this.foreach = function(fun) { | |
305 | for (var i = this.sprites.length-1; i >= 0; i--) { | |
306 | var sprite = this.sprites[i]; | |
307 | var result = fun(sprite); | |
308 | if (result === 'remove') { | |
309 | this.removeSprite(sprite); | |
310 | } | |
311 | if (result === 'return') { | |
312 | return sprite; | |
313 | } | |
314 | } | |
315 | }; | |
316 | ||
317 | }; |