-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
178 lines (168 loc) · 5.03 KB
/
index.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*!
* @license MIT
* Prefetch all images for your web app, especially for mobile/h5 promotion pages.
* https://github.com/JasonBoy/prefetch-image
*/
'use strict';
import joinUrl from 'url-join';
/**
* Preload all images
* @param {Array|object} images, use object if your images are on different domains
* object:
* {
* "http://domain1.com": ['/image1.png', '/image2.png'],
* "http://domain2.com": ['/image3.png', '/image4.png'],
* }
* @param {object=} options
* @return {Promise}
*/
function prefetchImages(images, options = {}) {
if(!images) {
console.error('[prefetch-image]: images not provided, pls pass images in Array or object!');
return Promise.reject({});
}
const isArray = Array.isArray(images);
if(isArray) {
return prefetchImageEachDomain(images, options);
}
const domainPromises = [];
const domainKeys = Object.keys(images);
let i =0;
for(; i < domainKeys.length; i++) {
const domain = domainKeys[i];
domainPromises.push(
prefetchImageEachDomain(
joinUrls(domain, images[domain]),
options,
domain
)
);
}
return Promise.all(domainPromises)
.then((results) => {
options.debug && console.info('[prefetch-image]: Images loaded for all domains!');
return Promise.resolve(results);
})
.catch((err) => {
console.error('[prefetch-image]: ', err);
return Promise.reject(null);
})
;
}
/**
* Preload all images in the same domain
* @param {Array} images all image urls in the same domain
* @param {object=} options
* @param {string=} domain current domain
* @return {Promise}
*/
function prefetchImageEachDomain (images, options, domain) {
const concurrency = options.concurrency || 6;
const imageLoadingInfo = {
start: 0,
end: 0,
//how many images for each iteration
//e.g. 15 images total, 6 images max each time, result: 6, 6, 3, iterateCount will be 3
concurrency,
iterations: Math.ceil(images.length / concurrency),
imagesContainer: [],
};
const bulkImagePromises = []; //length equals to "iterations"
let i = 0;
for (; i < imageLoadingInfo.iterations; i++) {
bulkImagePromises.push(loadImages(images, imageLoadingInfo));
}
// console.log('bulkImagePromisesLength: %d', bulkImagePromises.length);
return Promise.all(bulkImagePromises)
.then(() => {
addAllImagesToDOM(imageLoadingInfo.imagesContainer);
options.debug && console.info(`[prefetch-image]: Images loaded for domain [${domain || location.origin}], length [${images.length}]`);
return Promise.resolve(imageLoadingInfo.imagesContainer);
})
.catch((err) => {
console.error('[prefetch-image]: ', err);
return Promise.reject(imageLoadingInfo.imagesContainer);
});
}
/**
* Load images on an array
* @param {Array} images
* @param {object} imageLoadingInfo info about this phase of loading
* @return {Promise}
*/
function loadImages(images, imageLoadingInfo) {
const imagePromises = [];
const allImageLength = images.length;
const info = imageLoadingInfo;
if (info.start >= allImageLength) {
return Promise.resolve([]);
}
const start = info.start;
const end = start + info.concurrency;
// console.log(`${start} - ${end}`);
let i = start;
for (; i < end; i++) {
const src = images[i];
if (!src) continue;
imagePromises.push(loadImage(src, info.imagesContainer));
}
info.start = end;
info.end = end + info.concurrency;
// console.log('imagePromises: ', imagePromises.length);
return Promise.all(imagePromises);
}
/**
* Start loading every single image
* @param {string} src image src
* @param {array} container new Image instance will be added to this container
* @return {Promise}
*/
function loadImage(src, container) {
// console.log('--> start loading img: %s', src);
return new Promise((resolve) => {
const img = new Image();
img.onload = () => {
// console.log(`src: ${src}`);
resolve(src);
};
img.onerror = () => {
console.error(`[prefetch-image]: "${src}" failed`);
//still resolve even if some image failed loading
resolve(src);
};
img.src = src;
container.push(img);
});
}
/**
* Add all images loaded to dom to ensure cache
* @param {Array} imageElements Image objects in an array
*/
function addAllImagesToDOM(imageElements) {
const body = document.querySelector('body');
const imagesWrapper = document.createElement('div');
imagesWrapper.setAttribute('class', `prefetch-image-wrapper_${Math.random()}`);
imagesWrapper.style.width = 0;
imagesWrapper.style.height = 0;
imagesWrapper.style.overflow = 'hidden';
// imagesWrapper.style.opacity = 0;
imagesWrapper.style.display = 'none';
imageElements.forEach((img) => {
imagesWrapper.appendChild(img);
});
body.appendChild(imagesWrapper);
}
/**
* Join domain for urls
* @param {string} domain
* @param {array} urls url paths
* @return {Array}
*/
function joinUrls (domain, urls) {
const newUrls = [];
urls.forEach((url) => {
newUrls.push(joinUrl(domain, url));
});
return newUrls;
}
export default prefetchImages;