-
Notifications
You must be signed in to change notification settings - Fork 0
/
imagesPreload.js
executable file
·130 lines (104 loc) · 4.33 KB
/
imagesPreload.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Multi-Image Preloader
"use strict";
/*jslint browser: true, devel: true, white: true */
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
/*
0 1 2 3 4 5 6 7 8
12345678901234567890123456789012345678901234567890123456789012345678901234567890
*/
// Extend the Image prototype (aka augment the "class")
// with my asyncLoad wrapper.
//
// I prefer this approach to setting onload/onerror/src directly.
//
Image.prototype.asyncLoad = function(src, asyncCallback) {
// Must assign the callback handlers before setting `this.src`,
// for safety (and caching-tolerance).
//
// Uses the same handler for success *and* failure,
// because they share a lot of the same logic.
//
// NB: The failure case can be identified by the "degenerate" nature
// of the resulting "loaded" image e.g. test for this.width === 0
//
this.onload = asyncCallback;
this.onerror = asyncCallback;
// NB: The load operation can be triggered from any point
// after setting `this.src`.
//
// It *may* happen immediately (on some browsers) if the image is already
// in-cache, but will most likely happen some time later when the load has
// occurred and the resulting event is processesd in the queue.
console.log("requesting image src of ", src);
this.src = src;
};
// imagePreload
//
// Horrible stuff to deal with the asynchronous nature of image-loading
// in the browser...
//
// It requires setting-up a bunch of handler callbacks and then waiting for them
// *all* to be exectued before finally triggering our own `completionCallback`.
//
// Makes use of "closures" to handle the necessary state-tracking between the
// intermediate callback handlers without resorting to global variables.
//
// IN : `requiredImages` - an object of <name:uri> pairs for each image
// OUT : `loadedImages` - object to which our <name:Image> pairs will be added
// IN : `completionCallback` - will be executed when everything is done
//
function imagesPreload(requiredImages,
loadedImages,
completionCallback) {
var numImagesRequired,
numImagesHandled = 0,
currentName,
currentImage,
preloadHandler;
// Count our `requiredImages` by using `Object.keys` to get all
// "*OWN* enumerable properties" i.e. doesn't traverse the prototype chain
numImagesRequired = Object.keys(requiredImages).length;
// A handler which will be called when our required images are finally
// loaded (or when the fail to load).
//
// At the time of the call, `this` will point to an Image object,
// whose `name` property will have been set appropriately.
//
preloadHandler = function () {
console.log("preloadHandler called with this=", this);
loadedImages[this.name] = this;
if (0 === this.width) {
console.log("loading failed for", this.name);
}
// Allow this handler closure to eventually be GC'd (!)
this.onload = null;
this.onerror = null;
numImagesHandled += 1;
if (numImagesHandled === numImagesRequired) {
console.log("all preload images handled");
console.log("loadedImages=", loadedImages);
console.log("");
console.log("performing completion callback");
completionCallback();
console.log("completion callback done");
console.log("");
}
};
// The "for..in" construct "iterates over the enumerable properties
// of an object, in arbitrary order."
// -- unlike `Object.keys`, it traverses the prototype chain
//
for (currentName in requiredImages) {
// Skip inherited properties from the prototype chain,
// just to be safe, although there shouldn't be any...
// I prefer this approach, but JSLint doesn't like "continue" :-(
//if (!requiredImages.hasOwnProperty(currentName)) { continue; }
if (requiredImages.hasOwnProperty(currentName)) {
console.log("preloading image", currentName);
currentImage = new Image();
currentImage.name = currentName;
currentImage.asyncLoad(requiredImages[currentName], preloadHandler);
}
}
}