/*export default */function getAsyncLoadingImageList(imageSourceList) {
const totalCount = imageSourceList.length;
let successCount = 0;
let failureCount = 0;
function loadImage(src) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = function() {
resolve({
image,
counts: {
total: totalCount,
success: ++successCount,
failure: failureCount
},
success: true,
});
};
image.onerror = image.onabort = function() {
reject({
image,
counts: {
total: totalCount,
success: successCount,
failure: ++failureCount
},
success: false,
});
};
image.src = src;
});
}
// return list of *image loading* promises.
return imageSourceList.map(src => loadImage(src));
}
function getImageLoadProgressRenderConfig(progressContainer) {
const elmProgress = progressContainer.querySelector('progress');
const countsContainer = progressContainer.querySelector('.image-counts');
const failuresContainer = progressContainer.querySelector('.load-failures');
const elmImageCount = countsContainer.querySelector('.count');
const elmImageTotal = countsContainer.querySelector('.total');
const elmFailureCount = failuresContainer.querySelector('.failure-count');
const elmCurrentCount = failuresContainer.querySelector('.current-count');
return {
nodes: {
progress: {
display: elmProgress,
count: elmImageCount,
total: elmImageTotal
},
failure: {
display: failuresContainer,
failureCount: elmFailureCount,
currentCount: elmCurrentCount
}
},
classNames: {
failure: 'failures-exist'
}
};
}
function renderImageLoadProgressViaBoundConfig(imageLoadData) {
const imageCounts = imageLoadData.counts;
const imageCountTotal = imageCounts.total;
const imageCountSuccess = imageCounts.success;
const imageCountFailure = imageCounts.failure;
const imageCountCurrent = (imageCountSuccess + imageCountFailure);
const renderConfig = this;
const renderNodes = renderConfig.nodes;
const progressNodes = renderNodes.progress;
const failureNodes = renderNodes.failure;
const classNameFailure = renderConfig.classNames.failure;
const isFailureOnDisplay =
failureNodes.display.classList.contains(classNameFailure);
progressNodes.display.textContent = `${ imageCountCurrent } \/ ${ imageCountTotal }`;
progressNodes.display.value = imageCountCurrent;
progressNodes.count.textContent = imageCountCurrent;
if (!imageLoadData.success) {
if (!isFailureOnDisplay) {
failureNodes.display.classList.add(classNameFailure);
}
failureNodes.failureCount.textContent = imageCountFailure;
} else if (isFailureOnDisplay) {
failureNodes.currentCount.textContent = imageCountCurrent
}
}
function preloadImagesAndRenderLoadProgress(imageSourceList, progressContainer) {
const totalImageCount = imageSourceList.length;
const renderConfig = getImageLoadProgressRenderConfig(progressContainer);
const renderNodes = renderConfig.nodes;
const progressNodes = renderNodes.progress;
const failureNodes = renderNodes.failure;
const renderImageLoadProgress =
renderImageLoadProgressViaBoundConfig.bind(renderConfig);
failureNodes.display.classList.remove(renderConfig.classNames.failure);
failureNodes.failureCount.textContent = 0;
failureNodes.currentCount.textContent = 0;
progressNodes.display.max = totalImageCount;
progressNodes.total.textContent = totalImageCount;
renderImageLoadProgress({
counts: {
success: 0,
failure: 0
},
success: true,
});
getAsyncLoadingImageList(imageSourceList).forEach(promise => promise
.then(renderImageLoadProgress)
.catch(renderImageLoadProgress)
);
}
const imageSourceListWithFailure = [
'https://picsum.photos/id/1011', // loading will fail.
'https://picsum.photos/id/1011/5472/3648',
'https://picsum.photos/id/1012/3973/2639',
'https://picsum.photos/id/1013/4256/2832',
'https://picsum.photos/id/1013', // loading will fail.
'https://picsum.photos/id/1014/6016/4000',
'https://picsum.photos/id/1015/6000/4000',
'https://picsum.photos/id/1016/3844/2563',
'https://picsum.photos/id/1018', // loading will fail.
'https://picsum.photos/id/1018/3914/2935',
'https://picsum.photos/id/1019/5472/3648',
'https://picsum.photos/id/1020/4288/2848',
'https://picsum.photos/id/1021', // loading will fail.
'https://picsum.photos/id/1021/2048/1206',
'https://picsum.photos/id/1022/6000/3376',
'https://picsum.photos/id/1023/3955/2094'
];
const imageSourceListWithoutFailure = [
'https://picsum.photos/id/1039/6945/4635',
'https://picsum.photos/id/1038/3914/5863',
'https://picsum.photos/id/1037/5760/3840',
'https://picsum.photos/id/1036/4608/3072',
'https://picsum.photos/id/1035/5854/3903',
'https://picsum.photos/id/1033/2048/1365',
'https://picsum.photos/id/1032/2880/1800',
'https://picsum.photos/id/1031/5446/3063',
'https://picsum.photos/id/1029/4887/2759',
'https://picsum.photos/id/1028/5184/3456',
'https://picsum.photos/id/1027/2848/4272',
'https://picsum.photos/id/1026/4621/3070'
];
preloadImagesAndRenderLoadProgress(
imageSourceListWithFailure,
document.querySelector('#loading-with-failure')
);
preloadImagesAndRenderLoadProgress(
imageSourceListWithoutFailure,
document.querySelector('#loading-without-failure')
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
.image-loading-progress {
margin: 20px 0;
}
.image-loading-progress,
.image-loading-progress > span,
.image-loading-progress progress {
display: block;
width: 100%;
}
.image-loading-progress > .load-failures {
display: none
}
.image-loading-progress > .load-failures.failures-exist {
display: block
}
<label class="image-loading-progress" id="loading-with-failure">
<progress value="0" max="0">0 / 0</progress>
<span class="image-counts">
...
<span class="count">0</span>
out of
<span class="total">0</span>
images finished loading ...
</span>
<span class="load-failures">
...
<span class="failure-count">0</span>
out of
<span class="current-count">0</span>
images are not available ...
</span>
</label>
<label class="image-loading-progress" id="loading-without-failure">
<progress value="0" max="0">0 / 0</progress>
<span class="image-counts">
...
<span class="count">0</span>
out of
<span class="total">0</span>
images finished loading ...
</span>
<span class="load-failures">
...
<span class="failure-count">0</span>
out of
<span class="current-count">0</span>
images are not available ...
</span>
</label>