parent
dd30640eef
commit
373885732a
@ -0,0 +1,79 @@ |
||||
const ProgressBar=require('progress'); |
||||
|
||||
const assetSpecs=[ |
||||
{ name: 'fruit', filename: 'apple32.png', type: 'image' }, |
||||
{ name: 'wall', filename: 'wall32.png', type: 'image' }, |
||||
{ name: 'snake', filename: 'snake.json', type: 'json' } |
||||
]; |
||||
|
||||
const cvs=document.createElement('canvas'); |
||||
cvs.width=400; |
||||
cvs.height=50; |
||||
cvs.classList.add('progressBar'); |
||||
cvs.classList.add('hiddenBottom'); |
||||
|
||||
const bar=new ProgressBar(assetSpecs.length*2); |
||||
bar.addUpdateListener(() => bar.draw(cvs)); |
||||
bar.draw(cvs); |
||||
|
||||
document.body.appendChild(cvs); |
||||
setTimeout(() => cvs.classList.remove('hiddenBottom'), 0); |
||||
|
||||
bar.addReadyListener(() => { |
||||
cvs.classList.add('hiddenBottom'); |
||||
setTimeout(() => document.body.removeChild(cvs), 1000); |
||||
}); |
||||
|
||||
//XXX purposefully slow down asset loading
|
||||
const sleep=(ms) => new Promise(ok => setTimeout(ok, ms)); |
||||
|
||||
const loadAsset=async (asset) => { |
||||
const response=await fetch('assets/'+asset.filename); |
||||
await sleep(1000*Math.random()); |
||||
bar.update(); |
||||
let result; |
||||
switch(asset.type) { |
||||
case 'json': |
||||
result=await response.json(); |
||||
break; |
||||
|
||||
case 'image': |
||||
result=await createImageBitmap(await response.blob()); |
||||
break; |
||||
} |
||||
await sleep(1000*Math.random()); |
||||
bar.update(); |
||||
return [asset.name, result]; |
||||
}; |
||||
|
||||
let assets=Object.create(null); |
||||
let ready=false; |
||||
let readyListeners=[]; |
||||
|
||||
Promise |
||||
.all( |
||||
assetSpecs.map(a => loadAsset(a)) |
||||
).then(arr => { |
||||
arr.forEach(([name, value]) => { |
||||
assets[name]=value; |
||||
}); |
||||
ready=true; |
||||
readyListeners.forEach(fn => fn.bind(fn)()); |
||||
readyListeners=null; |
||||
}); |
||||
|
||||
const onReady=(fn) => { |
||||
if(ready) fn.bind(fn)(); |
||||
else readyListeners.push(ready); |
||||
}; |
||||
|
||||
const get=(name) => { |
||||
let asset=assets[name]; |
||||
if(!asset) throw new Error("Unknown asset: "+name); |
||||
return asset; |
||||
}; |
||||
|
||||
return { |
||||
onReady, get |
||||
}; |
||||
|
@ -0,0 +1,44 @@ |
||||
class ProgressBar { |
||||
constructor(taskCount) { |
||||
this.taskCount=taskCount; |
||||
this.completeCount=0; |
||||
this.updateListeneres=[]; |
||||
} |
||||
|
||||
get percent() { |
||||
return Math.floor(this.completeCount/this.taskCount*100); |
||||
} |
||||
|
||||
get ready() { |
||||
return this.completeCount==this.taskCount; |
||||
} |
||||
|
||||
addUpdateListener(fn) { |
||||
this.updateListeneres.push(fn.bind(this)); |
||||
} |
||||
addReadyListener(fn) { |
||||
this.updateListeneres.push(() => { |
||||
if(this.ready) fn.bind(this)(); |
||||
}); |
||||
} |
||||
|
||||
update() { |
||||
this.completeCount++; |
||||
this.updateListeneres.forEach(l => l(this)); |
||||
} |
||||
|
||||
draw(canvas, bgColor='red', textColor='black') { |
||||
let ctx=canvas.getContext('2d'); |
||||
ctx.clearRect(0, 0, canvas.width, canvas.height); |
||||
ctx.fillStyle=bgColor; |
||||
ctx.fillRect(0, 0, canvas.width*this.completeCount/this.taskCount, canvas.height); |
||||
ctx.fillStyle=textColor; |
||||
ctx.textAlign='center'; |
||||
ctx.textBaseline='center'; |
||||
ctx.font=`${canvas.height/2}px 'Fira Code'`; |
||||
ctx.fillText(this.percent+'%', canvas.width/2, canvas.height/2); |
||||
} |
||||
} |
||||
|
||||
return ProgressBar; |
||||
|
@ -0,0 +1,15 @@ |
||||
.progressBar { |
||||
position: fixed; |
||||
top: 50%; |
||||
left: 50%; |
||||
background: white; |
||||
box-shadow: black 0 0 2rem; |
||||
border-radius: 100vh; |
||||
transform: translate(-50%, -50%); |
||||
transition: top .5s ease-in-out; |
||||
|
||||
&.hiddenBottom { |
||||
top: 200vh; |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue