-
Notifications
You must be signed in to change notification settings - Fork 37
Add loading bar to web template #740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,13 +62,57 @@ | |
| transform: rotate(360deg); | ||
| } | ||
| } | ||
|
|
||
| .bar-container { | ||
| width: 128px; | ||
| height: 12px; | ||
| border: 3px solid #ececec; | ||
| border-radius: 0.375em; | ||
| position: relative; | ||
| overflow: hidden; | ||
| margin-top: 24px; | ||
| margin-bottom: 12px; | ||
| } | ||
|
|
||
| .loading-bar { | ||
| position: absolute; | ||
| top: 0; | ||
| left: 0; | ||
| right: 0; | ||
| bottom: 0; | ||
| background-color: #b2b2b2; | ||
| transform-origin: center left; | ||
| transform: scaleX(0); | ||
| } | ||
|
|
||
| .progress-text { | ||
| font-size: 0.9rem; | ||
| color: #ececec; | ||
| font-family: | ||
| DejaVu Sans Mono, | ||
| monospace; | ||
| } | ||
|
|
||
| .error { | ||
| font-size: 0.9rem; | ||
| color: salmon; | ||
| font-family: | ||
| DejaVu Sans Mono, | ||
| monospace; | ||
| margin-top: 12px; | ||
| } | ||
| </style> | ||
| </head> | ||
|
|
||
| <body class="center"> | ||
| <noscript>JavaScript support is required to run this app</noscript> | ||
| <div id="loading-screen" class="center"> | ||
| <span class="spinner"></span> | ||
| <div hidden="hidden" class="bar-container"> | ||
| <div class="loading-bar"></div> | ||
| </div> | ||
| <div class="progress-text"></div> | ||
| <div class="error"></div> | ||
| </div> | ||
|
|
||
| <script type="module"> | ||
|
|
@@ -134,14 +178,94 @@ | |
| </script> | ||
|
|
||
| <script type="module"> | ||
| // Starting the game | ||
|
|
||
| // When this file is used as the default `index.html`, the CLI will automatically replace | ||
| // `bevy_app.js` with the name of the generated JS entrypoint. If you copy this file and | ||
| // customize it, you will need to manually change the name. For more information, please see | ||
| // `bevy_app.js` and `bevy_app_bg.wasm` with the name of the generated JS entrypoint and Wasm | ||
| // binary. If you copy this file and customize it, you will need to manually change | ||
| // the names. For more information, please see | ||
| // <https://thebevyflock.github.io/bevy_cli/cli/web/default-index-html.html>! | ||
| import init from "./build/bevy_app.js"; | ||
| init().catch((error) => { | ||
| const wasmPath = "./build/bevy_app_bg.wasm"; | ||
| const wasmSize = null; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reading this by itself is quite confusing. I wonder if we need a more visual indicator for the template replacement, especially so that users can use it for their custom index files. But that issue existed before, so it probably makes sense to tackle it in a different PR
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The three calls to |
||
|
|
||
| // Provide progress reporting when fetching the Wasm binary | ||
| async function fetchWithProgress(url, total, onProgress) { | ||
| const response = await fetch(url); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`request failed with status ${response.status}`); | ||
| } | ||
|
|
||
| if (total === null) { | ||
| // If the content length isn't provided, | ||
| // we'll just skip the progress reporting | ||
| return await response.arrayBuffer(); | ||
| } | ||
|
|
||
| let receivedLength = 0; | ||
| const chunks = []; | ||
|
|
||
| for await (const chunk of response.body) { | ||
| chunks.push(chunk); | ||
| receivedLength += chunk.length; | ||
|
|
||
| const progress = receivedLength / total; | ||
|
|
||
| // Interacting with the DOM or other operations | ||
| // here should not cause downloading to fail | ||
| try { | ||
| onProgress({ | ||
| loaded: receivedLength, | ||
| total: total, | ||
| progress: progress, | ||
| }); | ||
| } catch (e) { | ||
| console.error(e); | ||
| } | ||
| } | ||
|
|
||
| const chunksAll = new Uint8Array(receivedLength); | ||
| let position = 0; | ||
| for (let chunk of chunks) { | ||
| chunksAll.set(chunk, position); | ||
| position += chunk.length; | ||
| } | ||
|
|
||
| return chunksAll.buffer; | ||
| } | ||
|
|
||
| function humanReadable(number) { | ||
| // Working with Bevy on the web, we can safely | ||
| // assume this scale | ||
| return `${(number / 1e6).toFixed(2)}MB`; | ||
|
CorvusPrudens marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| async function loadWasm(url, size) { | ||
| let arrayBuffer; | ||
| try { | ||
| arrayBuffer = await fetchWithProgress(url, size, (stats) => { | ||
| const container = document.querySelector(".bar-container"); | ||
| container.removeAttribute("hidden"); | ||
|
|
||
| const bar = document.querySelector(".loading-bar"); | ||
| bar.style.transform = `scaleX(${stats.progress})`; | ||
|
|
||
| const text = document.querySelector(".progress-text"); | ||
| text.innerText = `${humanReadable(stats.loaded)} / ${humanReadable(stats.total)}`; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: I think the progress indicator is a bit busy right now. Not yet sure what I would prefer, maybe just the loaded size or a percentage? Even though the percentage is kinda redundant because of the progress bar.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I remember correctly, using the Bevy logo requires approval on a per-game basis. But I'm not entirely sure. |
||
| }); | ||
| } catch (e) { | ||
| // Here, we communicate any request failure to the user | ||
| const errorNode = document.querySelector(".error"); | ||
| if (errorNode !== null) { | ||
| errorNode.innerText = e.toString(); | ||
| } | ||
| throw e; | ||
| } | ||
|
|
||
| return await init(arrayBuffer); | ||
| } | ||
|
|
||
| // Starting the game | ||
| loadWasm(wasmPath, wasmSize).catch((error) => { | ||
| if ( | ||
| !error.message.startsWith( | ||
| "Using exceptions for control flow, don't mind me. This isn't actually an error!" | ||
|
|
||





There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a fan of semantic HTML, which is also more accessible.
Can we use the
<progress>element instead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In most cases I would absolutely agree. However, styling the
progresselement is a little nasty between browsers. It would require a bit more CSS than this PR adds already.Maybe we could compromise with a few additional aria attributes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fair! The aria attributes work for me as well :)