added progress bar
This commit is contained in:
@@ -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;
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
const Assets=require('assets');
|
||||
const assets=require('assets');
|
||||
|
||||
const [EMPTY, FOOD, WALL, SNAKE]=Array(4).keys();
|
||||
|
||||
@@ -47,7 +47,7 @@ class SnekGame {
|
||||
const offsetY=(this.canvas.height-cellSize*this.dimensions[1])/2;
|
||||
|
||||
// draw our walls
|
||||
const wall=Assets.get('wall');
|
||||
const wall=assets.get('wall');
|
||||
for(let x=0; x<this.dimensions[0]; x++) {
|
||||
for(let y=0; x<this.dimensions[1]; y++) {
|
||||
switch(this.world[x][y]) {
|
||||
@@ -65,7 +65,7 @@ class SnekGame {
|
||||
}
|
||||
|
||||
// draw our snake
|
||||
const snake=Assets.get('snake');
|
||||
const snake=assets.get('snake');
|
||||
this.ctx.fillStyle=snake.color;
|
||||
this.ctx.strokeStyle=snake.color;
|
||||
this.ctx.lineCap=snake.cap;
|
||||
@@ -96,7 +96,7 @@ class SnekGame {
|
||||
// our fruit has a nice animation to it between .8 and 1.2 scale
|
||||
const ms=Date.now();
|
||||
const fruitScale=Math.sin(ms/1000*Math.PI)*.2+1
|
||||
const fruit=Assets.get('fruit');
|
||||
const fruit=assets.get('fruit');
|
||||
this.fruits.forEach(([x, y]) => {
|
||||
this.ctx.drawImage(
|
||||
fruit,
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
html5doctor.com Reset Stylesheet
|
||||
v1.6.1
|
||||
Last Updated: 2010-09-17
|
||||
Author: Richard Clark - http://richclarkdesign.com
|
||||
Twitter: @rich_clark
|
||||
*/
|
||||
|
||||
html, body, div, span, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
abbr, address, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, samp,
|
||||
small, strong, sub, sup, var,
|
||||
b, i,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin:0;
|
||||
padding:0;
|
||||
border:0;
|
||||
outline:0;
|
||||
font-size:100%;
|
||||
vertical-align:baseline;
|
||||
background:transparent;
|
||||
}
|
||||
|
||||
body {
|
||||
line-height:1;
|
||||
}
|
||||
|
||||
article,aside,details,figcaption,figure,
|
||||
footer,header,hgroup,menu,nav,section {
|
||||
display:block;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style:none;
|
||||
}
|
||||
|
||||
blockquote, q {
|
||||
quotes:none;
|
||||
}
|
||||
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content:'';
|
||||
content:none;
|
||||
}
|
||||
|
||||
a {
|
||||
margin:0;
|
||||
padding:0;
|
||||
font-size:100%;
|
||||
vertical-align:baseline;
|
||||
background:transparent;
|
||||
}
|
||||
|
||||
/* change colours to suit your needs */
|
||||
ins {
|
||||
background-color:#ff9;
|
||||
color:#000;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
/* change colours to suit your needs */
|
||||
mark {
|
||||
background-color:#ff9;
|
||||
color:#000;
|
||||
font-style:italic;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
abbr[title], dfn[title] {
|
||||
border-bottom:1px dotted;
|
||||
cursor:help;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse:collapse;
|
||||
border-spacing:0;
|
||||
}
|
||||
|
||||
/* change border colour to suit your needs */
|
||||
hr {
|
||||
display:block;
|
||||
height:1px;
|
||||
border:0;
|
||||
border-top:1px solid #cccccc;
|
||||
margin:1em 0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
input, select {
|
||||
vertical-align:middle;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,3 +79,6 @@ h2 {
|
||||
p {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
// setup the progress bar
|
||||
@import 'progressBar';
|
||||
|
||||
Reference in New Issue
Block a user