OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 /** | |
6 * @file | |
7 * The speedometer object. This draws an array of speedometers as adjacent | |
8 * vertical bars. The height of the bars is a percentage of the maximum | |
9 * possible speed. | |
10 */ | |
11 | |
12 | |
13 goog.provide('Speedometer'); | |
14 goog.provide('Speedometer.Attributes'); | |
15 | |
16 goog.require('goog.Disposable'); | |
17 goog.require('goog.events'); | |
18 goog.require('goog.events.EventTarget'); | |
19 | |
20 /** | |
21 * Manages a map of individual speedometers. Use addMeterWithName() | |
22 * to add speedometer bars. | |
23 * @param {Canvas} opt_canvas The <CANVAS> element to use for drawing. | |
24 * @constructor | |
25 * @extends {goog.events.EventTarget} | |
26 */ | |
27 Speedometer = function(opt_canvas) { | |
28 goog.events.EventTarget.call(this); | |
29 | |
30 /** | |
31 * Speedometer has two canvases, one is the "offscreen" canvas used to | |
32 * assemble the final speedometer image and the other is the "onscreen" | |
33 * canvas in the DOM which renders the final image. | |
34 * @type {Canvas} | |
35 * @private | |
36 */ | |
37 this.domCanvas_ = opt_canvas || null; | |
38 this.offscreenCanvas_ = null; | |
39 | |
40 /** | |
41 * The dictionary of individual meters. | |
42 * @type {Array.Object} | |
43 * @private | |
44 */ | |
45 this.meters_ = []; | |
46 | |
47 /** | |
48 * The number of unique meters added to the speedometer. | |
49 * @type {number} | |
50 * @private | |
51 */ | |
52 this.meterCount_ = 0; | |
53 | |
54 /** | |
55 * The maximum speed that any meter can reach. Meters that go past this | |
56 * value are clipped. | |
57 * @type {real} | |
58 * @private | |
59 */ | |
60 this.maxSpeed_ = 1.0; | |
61 | |
62 /** | |
63 * Display timers. There is a timer for updating the needles and one for | |
64 * updating the odometers, these run on different frequencies. These | |
65 * timers are started with the first call to render(). | |
66 * @type {Timer} | |
67 * @private | |
68 */ | |
69 this.needleUpdateTimer_ = null; | |
70 this.odometerUpdateTimer_ = null; | |
71 | |
72 /** | |
73 * The images. | |
74 * @type {Object.Image} | |
75 * @private | |
76 */ | |
77 this.images_ = {} | |
78 }; | |
79 goog.inherits(Speedometer, goog.events.EventTarget); | |
80 | |
81 /** | |
82 * Meter dictionary values. | |
83 * @enum {string} | |
84 */ | |
85 Speedometer.Attributes = { | |
86 DISPLAY_VALUE: 'displayValue', | |
87 THEME: 'theme', // The needle theme. Has value Speedometer.Themes. | |
88 NAME: 'name', // The name of the meter. Not optional. | |
89 ODOMETER_DISPLAY_VALUE: 'odometerDisplayValue', | |
90 ODOMETER_LEFT: 'odometerLeft', // The left coordinate of the odometer. | |
91 ODOMETER_TOP: 'odometerTop', | |
92 VALUE: 'value', // The value of the meter. Not optional. | |
93 }; | |
94 | |
95 /** | |
96 * Drawing themes. | |
97 * @enum {string} | |
98 */ | |
99 Speedometer.Themes = { | |
100 DEFAULT: 'greenTheme', | |
101 GREEN: 'greenTheme', | |
102 RED: 'redTheme' | |
103 }; | |
104 | |
105 /** | |
106 * Odometer dimensions. | |
107 * @enum {number} | |
108 * @private | |
109 */ | |
110 Speedometer.prototype.OdometerDims_ = { | |
111 COLUMN_HEIGHT: 16, | |
112 COLUMN_WIDTH: 12, | |
113 DIGIT_HEIGHT: 14, | |
114 DIGIT_WIDTH: 8 | |
115 }; | |
116 | |
117 /** | |
118 * The needle update interval time. Measured in milliseconds. | |
119 * @type {number} | |
120 * @private | |
121 */ | |
122 Speedometer.prototype.NEEDLE_UPDATE_INTERVAL_ = 83.0; // About 12 frames/sec. | |
123 | |
124 /** | |
125 * Beginning and ending angles for the meter. |METER_ANGLE_START_| represents | |
126 * "speed" of 0, |METER_ANGLE_RANGE_| is the angular range of the meter needle. | |
127 * maximumSpeed() is |METER_ANGLE_START_ + METER_ANGLE_RANGE_|. Measured | |
128 * cockwise in radians from the line y = 0. | |
129 * @type {number} | |
130 * @private | |
131 */ | |
132 Speedometer.prototype.METER_ANGLE_START_ = 3.0 * Math.PI / 4.0; | |
133 Speedometer.prototype.METER_ANGLE_RANGE_ = 6.0 * Math.PI / 4.0; | |
134 | |
135 /** | |
136 * Value used to provde damping to the meter. The meter reading can change by | |
137 * at most this amount per frame. Measured in radians. | |
138 * @type {number} | |
139 * @private | |
140 */ | |
141 Speedometer.prototype.DAMPING_FACTOR_ = Math.PI / 36.0; | |
142 | |
143 /** | |
144 * Maximum odometer value. Speeds are clapmed to this. | |
145 * @type {number} | |
146 * @private | |
147 */ | |
148 Speedometer.prototype.MAX_ODOMETER_VALUE_ = 999999.99; | |
149 | |
150 /** | |
151 * The odometer update interval time. Measured in milliseconds. | |
152 * @type {number} | |
153 * @private | |
154 */ | |
155 Speedometer.prototype.ODOMETER_UPDATE_INTERVAL_ = 2000.0; | |
156 | |
157 /** | |
158 * The amount of time to roll a digit. Measured in number of needle update | |
159 * intervals. So, if NEEDLE_UPDATE_INTERVAL_ is 12 fps (83 msec), then a roll | |
160 * time of 6 means each digit takes about 1/2 sec to roll. | |
161 * @type {number} | |
162 * @private | |
163 */ | |
164 Speedometer.prototype.ODOMETER_ROLL_TIME_ = 6; | |
165 | |
166 /** | |
167 * The number of odometer digits. | |
168 * @type {number} | |
169 * @private | |
170 */ | |
171 Speedometer.prototype.ODOMETER_DIGIT_COUNT_ = 7; | |
172 | |
173 /** | |
174 * Override of disposeInternal() to dispose of retained objects and unhook all | |
175 * events. | |
176 * @override | |
177 */ | |
178 Speedometer.prototype.disposeInternal = function() { | |
179 function stopTimer(timer) { | |
180 if (timer) { | |
181 clearTimeout(timer); | |
182 } | |
183 } | |
184 | |
185 stopTimer(this.needleUpdateTimer_); | |
186 this.needleUpdateTimer_ = null; | |
187 stopTimer(this.odometerUpdateTimer_); | |
188 this.odometerUpdateTimer_ = null; | |
189 for (image in this.images_) { | |
190 delete this.images_[image]; | |
191 this.images_[image] = null; | |
192 } | |
193 this.domCanvas_ = null; | |
194 delete this.offscreenCanvas_; | |
195 this.offscreenCanvas_ = null; | |
196 Speedometer.superClass_.disposeInternal.call(this); | |
197 } | |
198 | |
199 /** | |
200 * Set the canvas for drawing. | |
201 * @param !{Canvas} canvas The <CANVAS> element for drawing. | |
202 */ | |
203 Speedometer.prototype.setCanvas = function(canvas) { | |
204 this.domCanvas_ = canvas; | |
205 if (this.offscreenCanvas_) { | |
206 delete this.offscreenCanvas_; | |
207 this.offscreenCanvas_ = null; | |
208 } | |
209 if (!canvas) { | |
210 return; | |
211 } | |
212 this.offscreenCanvas_ = document.createElement('canvas'); | |
213 this.offscreenCanvas_.width = canvas.width; | |
214 this.offscreenCanvas_.height = canvas.height; | |
215 } | |
216 | |
217 /** | |
218 * Return the current maximum speed. | |
219 * @return {real} The current maximum speed. Guaranteed to be >= 0.0 | |
220 */ | |
221 Speedometer.prototype.maximumSpeed = function() { | |
222 return this.maxSpeed_; | |
223 } | |
224 | |
225 /** | |
226 * Set the maximum speed value for all meters. | |
227 * @param {real} maxSpeed The new maximum speed. Must be > 0. | |
228 * @return {real} The previous maximum speed. | |
229 */ | |
230 Speedometer.prototype.setMaximumSpeed = function(maxSpeed) { | |
231 if (maxSpeed <= 0.0) { | |
232 throw Error('maxSpeed must be >= 0.0'); | |
233 } | |
234 var oldMaxSpeed = this.maxSpeed_; | |
235 this.maxSpeed_ = maxSpeed; | |
236 return oldMaxSpeed; | |
237 } | |
238 | |
239 /** | |
240 * Add a named meter. If a meter with |meterName| already exists, then do | |
241 * nothing. | |
242 * @param {!string} meterName The name for the new meter. | |
243 * @param {?Object} opt_attributes A dictionary containing optional attributes | |
244 * for the meter. Some key names are special, for example the 'valueLabel' | |
245 * key contains the id of a value label DOM element for the meter - this | |
246 * label will be updated with a text representation of the meter's value. | |
247 */ | |
248 Speedometer.prototype.addMeterWithName = function(meterName, opt_attributes) { | |
249 if (!(meterName in this.meters_)) { | |
250 this.meterCount_++; | |
251 } | |
252 var attributes = opt_attributes || {}; | |
253 // Fill in the non-optional attributes. | |
254 var meter = { | |
255 name: meterName, | |
256 value: 0.0, | |
257 displayValue: 0.0, // Updated every NEEDLE_UPDATE_INTERVAL_. | |
258 maxValue: 0.0, | |
259 theme: Speedometer.Themes.DEFAULT, | |
260 odometerLeft: 0, | |
261 odometerTop: 0, | |
262 previousAngle: 0.0, | |
263 odometerDisplayValue: 0.0 // Updated every ODOMETER_UPDATE_INTERVAL_. | |
264 }; | |
265 for (attrib in attributes) { | |
266 // Overwrite meter defaults with passed-in values if present. Otherwise, | |
267 // add the passed-in element. | |
268 meter[attrib] = attributes[attrib]; | |
269 } | |
270 // Each digit has its current display offset and a delta value used to | |
271 // compute its next display offset when rolling the digit. | |
272 meter.odometerDigits_ = new Array(this.ODOMETER_DIGIT_COUNT_); | |
273 for (var i = 0; i < this.ODOMETER_DIGIT_COUNT_; i++) { | |
274 meter.odometerDigits_[i] = this.createOdometerDigitDict_(); | |
275 } | |
276 | |
277 this.meters_[meterName] = meter; | |
278 } | |
279 | |
280 /** | |
281 * Update the value of a named meter. If the meter does not exist, then do | |
282 * nothing. If the value is successfully changed, then post a VALUE_CHANGED | |
283 * event. | |
284 * @param {!string} meterName The name of the meter. | |
285 * @param {!real} value The new value for the meter. Must be >= 0 | |
286 * @return {real} The previous value of the meter. | |
287 */ | |
288 Speedometer.prototype.updateMeterNamed = | |
289 function(meterName, value) { | |
290 var oldValue = 0.0; | |
291 if (meterName in this.meters_) { | |
292 var meter = this.meters_[meterName]; | |
293 oldValue = meter.value; | |
294 // Update the actual values. Display values are updated by the update | |
295 // interval handlers. | |
296 meter.value = value; | |
297 meter.maxValue = Math.max(meter.maxValue, value); | |
298 } | |
299 return oldValue; | |
300 } | |
301 | |
302 /** | |
303 * The value for a meter. If the meter doesn't exist, or if the given key is | |
304 * not valid, returns -1. | |
305 * @param {!string} meterName The name of the meter. | |
306 * @param {?string} opt_key The key name of the value. Defaults to 'value'. | |
307 * @return {number} The current meter's value or -1 if the meter doesn't exist. | |
308 */ | |
309 Speedometer.prototype.valueForMeterNamed = function(meterName, opt_key) { | |
310 if (meterName in this.meters_) { | |
311 var key = opt_key || Speedometer.Attributes.VALUE; | |
312 var meter = this.meters_[meterName]; | |
313 if (key in meter) { | |
314 return meter[key]; | |
315 } | |
316 } | |
317 return -1; | |
318 } | |
319 | |
320 /** | |
321 * Update the needles to their new values, and restart the needle update timer. | |
322 */ | |
323 Speedometer.prototype.updateNeedles = function() { | |
324 for (meterName in this.meters_) { | |
325 var meter = this.meters_[meterName]; | |
326 meter.displayValue = meter.value; | |
327 } | |
328 this.render(); | |
329 this.needleUpdateTimer_ = setTimeout(goog.bind(this.updateNeedles, this), | |
330 this.NEEDLE_UPDATE_INTERVAL_); | |
331 } | |
332 | |
333 /** | |
334 * Update the odometers to their new values, and restart the odometer update | |
335 * timer. Recomputes all the animation parameters for rolling the digits to | |
336 * their new values. | |
337 */ | |
338 Speedometer.prototype.updateOdometers = function() { | |
339 for (meterName in this.meters_) { | |
340 var meter = this.meters_[meterName]; | |
341 meter.odometerDisplayValue = meter.value; | |
342 var odometerValue = Math.min(meter.odometerDisplayValue.toFixed(2), | |
343 this.MAX_ODOMETER_VALUE_); | |
344 for (var digit = this.ODOMETER_DIGIT_COUNT_ - 1; digit >= 0; digit--) { | |
345 var digitDict = this.createOdometerDigitDict_(); | |
346 var digitValue = (odometerValue - Math.floor(odometerValue)) * 10; | |
347 var prevOffset = meter.odometerDigits_[digit].previousOffset; | |
348 digitDict.offset = Math.floor(digitValue) * | |
349 this.OdometerDims_.DIGIT_HEIGHT; | |
350 digitDict.rollDelta = (digitDict.offset - prevOffset) / | |
351 this.ODOMETER_ROLL_TIME_; | |
352 digitDict.previousOffset = prevOffset; | |
353 meter.odometerDigits_[digit] = digitDict; | |
354 odometerValue /= 10; | |
355 } | |
356 } | |
357 this.odometerUpdateTimer_ = setTimeout(goog.bind(this.updateOdometers, this), | |
358 this.ODOMETER_UPDATE_INTERVAL_); | |
359 } | |
360 | |
361 /** | |
362 * Start the display timers if needed and perform any other display | |
363 * initialization. | |
364 */ | |
365 Speedometer.prototype.reset = function() { | |
366 this.loadImages_(); | |
367 for (meterName in this.meters_) { | |
368 var meter = this.meters_[meterName]; | |
369 meter.displayValue = meter.value; | |
370 meter.odometerDisplayValue = meter.maxValue; | |
371 } | |
372 if (!this.needleUpdateTimer_) { | |
373 this.needleUpdateTimer_ = setTimeout(goog.bind(this.updateNeedles, this), | |
374 this.NEEDLE_UPDATE_INTERVAL_); | |
375 } | |
376 if (!this.odometerUpdateTimer_) { | |
377 this.odometerUpdateTimer_ = setTimeout( | |
378 goog.bind(this.updateOdometers, this), this.ODOMETER_UPDATE_INTERVAL_); | |
379 } | |
380 } | |
381 | |
382 /** | |
383 * Render the speedometer. | |
384 */ | |
385 Speedometer.prototype.render = function() { | |
386 if (!this.offscreenCanvas_) { | |
387 this.setCanvas(this.domCanvas_); | |
388 if (!this.offscreenCanvas_) { | |
389 return; | |
390 } | |
391 } | |
392 var context2d = this.offscreenCanvas_.getContext('2d'); | |
393 context2d.save(); | |
394 this.drawBackground_(context2d, | |
395 this.offscreenCanvas_.width, | |
396 this.offscreenCanvas_.height); | |
397 // Paint the meters. | |
398 for (meterName in this.meters_) { | |
399 var meter = this.meters_[meterName]; | |
400 this.drawOdometer_(context2d, meter); | |
401 this.drawNeedle_(context2d, meter); | |
402 } | |
403 // Draw the hub over the needles. | |
404 var canvasCenterX = context2d.canvas.width / 2; | |
405 var canvasCenterY = context2d.canvas.height / 2; | |
406 var hubRadius = this.images_.hub.width / 2; | |
407 context2d.translate(canvasCenterX - hubRadius, canvasCenterY - hubRadius); | |
408 context2d.drawImage(this.images_.hub, 0, 0); | |
409 context2d.restore(); | |
410 this.flushDrawing_(); | |
411 } | |
412 | |
413 /** | |
414 * Flush the contents of the offscreen canvas to the onscreen one. | |
415 * @private | |
416 */ | |
417 Speedometer.prototype.flushDrawing_ = function() { | |
418 if (!this.domCanvas_ || !this.offscreenCanvas_) { | |
419 return; | |
420 } | |
421 var context2d = this.domCanvas_.getContext('2d'); | |
422 context2d.drawImage(this.offscreenCanvas_, 0, 0); | |
423 } | |
424 | |
425 /** | |
426 * Draw the background. | |
427 * @param {Graphics2d} context2d The 2D canvas context. | |
428 * @param {number} width The background width, measured in pixels. | |
429 * @param {number} height The background height, measured in pixels. | |
430 * @private | |
431 */ | |
432 Speedometer.prototype.drawBackground_ = function(context2d, width, height) { | |
433 // Paint the background image. | |
434 if (this.images_.background.complete) { | |
435 context2d.drawImage(this.images_.background, 0, 0); | |
436 } else { | |
437 context2d.fillStyle = 'black'; | |
438 context2d.fillRect(0, 0, width, height); | |
439 } | |
440 } | |
441 | |
442 /** | |
443 * Draw a meter's needle. | |
444 * @param {Graphics2d} context2d The 2D canvas context. | |
445 * @param {Object} meter The meter. | |
446 * @private | |
447 */ | |
448 Speedometer.prototype.drawNeedle_ = function(context2d, meter) { | |
449 var canvasCenterX = context2d.canvas.width / 2; | |
450 var canvasCenterY = context2d.canvas.height / 2; | |
451 var meterAngle = (meter.displayValue / this.maxSpeed_) * | |
452 this.METER_ANGLE_RANGE_; | |
453 meterAngle = Math.min(meterAngle, this.METER_ANGLE_RANGE_); | |
454 meterAngle = Math.max(meterAngle, 0.0); | |
455 // Dampen the meter's angular change. | |
456 var delta = meterAngle - meter.previousAngle; | |
457 if (Math.abs(delta) > this.DAMPING_FACTOR_) { | |
458 delta = delta < 0 ? -this.DAMPING_FACTOR_ : this.DAMPING_FACTOR_; | |
459 } | |
460 var dampedAngle = meter.previousAngle + delta; | |
461 meter.previousAngle = dampedAngle; | |
462 dampedAngle += this.METER_ANGLE_START_; | |
463 context2d.save(); | |
464 context2d.translate(canvasCenterX, canvasCenterY); | |
465 context2d.rotate(dampedAngle); | |
466 // Select the needle image based on the meter's theme and value. | |
467 var needleImage = null; | |
468 if (meter.displayValue == 0) { | |
469 needleImage = this.images_.stoppedNeedle; | |
470 } else if (meter.theme == Speedometer.Themes.GREEN) { | |
471 needleImage = this.images_.greenNeedle; | |
472 } else if (meter.theme == Speedometer.Themes.RED) { | |
473 needleImage = this.images_.redNeedle; | |
474 } | |
475 if (needleImage && needleImage.complete) { | |
476 context2d.drawImage( | |
477 needleImage, | |
478 0, 0, needleImage.width, needleImage.height, | |
479 -5, -5, needleImage.width, needleImage.height); | |
480 } | |
481 context2d.restore(); | |
482 } | |
483 | |
484 /** | |
485 * Create an odometer digit dictionary. | |
486 * @return {Object} a newly-created digit dictionary. | |
487 * @private | |
488 */ | |
489 Speedometer.prototype.createOdometerDigitDict_ = function() { | |
490 var digitDict = { | |
491 rollDelta: 0, | |
492 previousOffset: 0, | |
493 offset: 0 | |
494 }; | |
495 return digitDict; | |
496 } | |
497 | |
498 /** | |
499 * Draw the odometer for a given meter. | |
500 * @param {Graphics2d} context2d The 2D canvas context. | |
501 * @param {Object} meter The meter. | |
502 * @private | |
503 */ | |
504 Speedometer.prototype.drawOdometer_ = function(context2d, meter) { | |
505 if (!this.images_.odometerTenths.complete || | |
506 !this.images_.odometerDigits.complete) { | |
507 return; | |
508 } | |
509 context2d.save(); | |
510 context2d.translate(meter.odometerLeft, meter.odometerTop); | |
511 context2d.drawImage(this.images_.odometer, 0, 0); | |
512 // Center the odometer digits in the odometer cells. | |
513 context2d.translate( | |
514 (this.OdometerDims_.COLUMN_WIDTH - this.OdometerDims_.DIGIT_WIDTH) / 2, | |
515 (this.OdometerDims_.COLUMN_HEIGHT - this.OdometerDims_.DIGIT_HEIGHT) / 2); | |
516 // Draw the tenths digit. | |
517 this.drawOdometerDigit_(context2d, meter, 6, this.images_.odometerTenths); | |
518 // Draw the integer part, up to 5 digits (max value is 999,999.9). | |
519 for (var column = 5; column >= 0; column--) { | |
520 this.drawOdometerDigit_(context2d, meter, column, | |
521 this.images_.odometerDigits); | |
522 } | |
523 context2d.restore(); | |
524 } | |
525 | |
526 /** | |
527 * Draw a single odometer digit. The digit source is a vertical strip of | |
528 * pre-rendered numbers. When drawing the last digit in the strip, the image | |
529 * source is wrapped back to the beginning of the strip. In this way, drawing | |
530 * a partial '9' will also draw part of the '0'. | |
531 * @param {Graphics2d} context2d The drawing context. | |
532 * @param {Object} meter The meter related to this odometer. | |
533 * @param {number} column The odometer column index, 0-based numbered from the | |
534 * left-most (most significant) digit. For example, column 0 is the | |
535 * 100,000's place, column 6 is the 1/10's place. | |
536 * @param {Image} digits The digit source image. | |
537 * @private | |
538 */ | |
539 Speedometer.prototype.drawOdometerDigit_ = function( | |
540 context2d, meter, column, digits) { | |
541 var digitDict = meter.odometerDigits_[column]; | |
542 var offset = digitDict.previousOffset; | |
543 var digitHeight = Math.min(digits.height - offset, | |
544 this.OdometerDims_.DIGIT_HEIGHT); | |
545 var digitWrapHeight = this.OdometerDims_.DIGIT_HEIGHT - digitHeight; | |
546 context2d.drawImage(digits, | |
547 0, offset, | |
548 this.OdometerDims_.DIGIT_WIDTH, digitHeight, | |
549 column * this.OdometerDims_.COLUMN_WIDTH, 0, | |
550 this.OdometerDims_.DIGIT_WIDTH, digitHeight); | |
551 if (digitWrapHeight > 0) { | |
552 context2d.drawImage(digits, | |
553 0, 0, | |
554 this.OdometerDims_.DIGIT_WIDTH, digitWrapHeight, | |
555 column * this.OdometerDims_.COLUMN_WIDTH, | |
556 this.OdometerDims_.DIGIT_HEIGHT - digitWrapHeight, | |
557 this.OdometerDims_.DIGIT_WIDTH, digitWrapHeight); | |
558 } | |
559 if (digitDict.rollDelta == 0) { | |
560 return; | |
561 } | |
562 if ((digitDict.rollDelta > 0 && offset >= digitDict.offset) || | |
563 (digitDict.rollDelta < 0 && offset <= digitDict.offset)) { | |
564 digitDict.previousOffset = digitDict.offset; | |
565 digitDict.rollDelta = 0; | |
566 return; | |
567 } | |
568 digitDict.previousOffset += digitDict.rollDelta; | |
569 if (digitDict.previousOffset < 0) { | |
570 // Wrap the offset around to the end of the digits image. | |
571 digitDict.previousOffset += digits.height; | |
572 } | |
573 if (digitDict.previousOffset > digits.height) { | |
574 // Wrap the offset around to the top of the digits image. | |
575 digitDict.previousOffset = digitDict.previousOffset - digits.height; | |
576 } | |
577 } | |
578 | |
579 /** | |
580 * Load all the images. | |
581 * @private | |
582 */ | |
583 Speedometer.prototype.loadImages_ = function() { | |
584 this.images_.background = new Image(); | |
585 this.images_.background.src = 'images/DialFace.png'; | |
586 this.images_.hub = new Image(); | |
587 this.images_.hub.src = 'images/hub.png'; | |
588 this.images_.odometer = new Image(); | |
589 this.images_.odometer.src = 'images/odometer.png'; | |
590 this.images_.odometerDigits = new Image(); | |
591 this.images_.odometerDigits.src = 'images/numbers_grey.png'; | |
592 this.images_.odometerTenths = new Image(); | |
593 this.images_.odometerTenths.src = 'images/numbers_red.png'; | |
594 this.images_.stoppedNeedle = new Image(); | |
595 this.images_.stoppedNeedle.src = 'images/pointer_grey.png' | |
596 this.images_.greenNeedle = new Image(); | |
597 this.images_.greenNeedle.src = 'images/pointer_green.png' | |
598 this.images_.redNeedle = new Image(); | |
599 this.images_.redNeedle.src = 'images/pointer_red.png' | |
600 } | |
601 | |
OLD | NEW |