|
1 | 1 | import { |
| 2 | + computed, |
2 | 3 | inject, |
3 | 4 | ref, |
| 5 | + reactive, |
4 | 6 | unref, |
5 | 7 | onMounted, |
6 | 8 | onBeforeUnmount, |
@@ -34,6 +36,7 @@ export default { |
34 | 36 | "memory-arrays", |
35 | 37 | "camera", |
36 | 38 | "invoke-response", |
| 39 | + "progress", |
37 | 40 | ], |
38 | 41 | props: { |
39 | 42 | useHandler: { |
@@ -105,6 +108,62 @@ export default { |
105 | 108 | ? WASM_HANDLERS[props.useHandler] |
106 | 109 | : new RemoteSession(); |
107 | 110 | let subscription = null; |
| 111 | + const progress = reactive({ |
| 112 | + active: false, |
| 113 | + state: { |
| 114 | + current: 0, |
| 115 | + total: 0, |
| 116 | + }, |
| 117 | + hash: { |
| 118 | + current: 0, |
| 119 | + total: 0, |
| 120 | + }, |
| 121 | + }); |
| 122 | + const wasmLoading = ref(!wasmManager.loaded); |
| 123 | + const showLoading = computed(() => wasmLoading.value || progress.active); |
| 124 | + const statePercent = computed(() => { |
| 125 | + if (!progress.state.total) { |
| 126 | + return 0; |
| 127 | + } |
| 128 | + return Math.min( |
| 129 | + 100, |
| 130 | + Math.floor((progress.state.current / progress.state.total) * 100), |
| 131 | + ); |
| 132 | + }); |
| 133 | + const hashPercent = computed(() => { |
| 134 | + if (!progress.hash.total) { |
| 135 | + return 0; |
| 136 | + } |
| 137 | + return Math.min( |
| 138 | + 100, |
| 139 | + Math.floor((progress.hash.current / progress.hash.total) * 100), |
| 140 | + ); |
| 141 | + }); |
| 142 | + function updateProgress(payload) { |
| 143 | + if (!payload) { |
| 144 | + return; |
| 145 | + } |
| 146 | + progress.active = !!payload.active; |
| 147 | + progress.state.current = payload.state?.current || 0; |
| 148 | + progress.state.total = payload.state?.total || 0; |
| 149 | + progress.hash.current = payload.hash?.current || 0; |
| 150 | + progress.hash.total = payload.hash?.total || 0; |
| 151 | + emit("progress", { |
| 152 | + active: progress.active, |
| 153 | + state: { |
| 154 | + current: progress.state.current, |
| 155 | + total: progress.state.total, |
| 156 | + }, |
| 157 | + hash: { |
| 158 | + current: progress.hash.current, |
| 159 | + total: progress.hash.total, |
| 160 | + }, |
| 161 | + }); |
| 162 | + } |
| 163 | + let removeProgressCallback = null; |
| 164 | + if (wasmManager.addProgressCallback) { |
| 165 | + removeProgressCallback = wasmManager.addProgressCallback(updateProgress); |
| 166 | + } |
108 | 167 |
|
109 | 168 | // network connector ------------------------------------------------------ |
110 | 169 |
|
@@ -227,7 +286,12 @@ export default { |
227 | 286 | // console.log("vtkLocal::mounted"); |
228 | 287 | wasmManager.bindNetwork(netFetchState, netFetchBlob, netFetchStatus); |
229 | 288 | if (!wasmManager.loaded) { |
230 | | - await wasmManager.load(wasmURL, props.config, wasmBaseName); |
| 289 | + wasmLoading.value = true; |
| 290 | + try { |
| 291 | + await wasmManager.load(wasmURL, props.config, wasmBaseName); |
| 292 | + } finally { |
| 293 | + wasmLoading.value = false; |
| 294 | + } |
231 | 295 | } |
232 | 296 | const selector = wasmManager.bindCanvasToDOM( |
233 | 297 | props.renderWindow, |
@@ -371,6 +435,10 @@ export default { |
371 | 435 | if (subscription) { |
372 | 436 | unsubscribe(); |
373 | 437 | } |
| 438 | + if (removeProgressCallback) { |
| 439 | + removeProgressCallback(); |
| 440 | + removeProgressCallback = null; |
| 441 | + } |
374 | 442 |
|
375 | 443 | // Old/New API - detection |
376 | 444 | const removeObserverMethodName = wasmManager.sceneManager.removeObserver |
@@ -429,7 +497,53 @@ export default { |
429 | 497 | printSceneManagerInformation, |
430 | 498 | detachHandler, |
431 | 499 | getVtkObject, |
| 500 | + progress, |
| 501 | + showLoading, |
| 502 | + statePercent, |
| 503 | + hashPercent, |
| 504 | + wasmLoading, |
432 | 505 | }; |
433 | 506 | }, |
434 | | - template: `<div ref="container" style="position: relative; width: 100%; height: 100%;"></div>`, |
| 507 | + template: `<div ref="container" style="position: relative; width: 100%; height: 100%;"> |
| 508 | + <slot |
| 509 | + v-if="showLoading" |
| 510 | + name="loader" |
| 511 | + :progress="progress" |
| 512 | + :wasm-loading="wasmLoading" |
| 513 | + :state-percent="statePercent" |
| 514 | + :hash-percent="hashPercent" |
| 515 | + :show-loading="showLoading" |
| 516 | + > |
| 517 | + <div style="position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(10, 10, 10, 0.45); z-index: 2;"> |
| 518 | + <div style="min-width: 220px; max-width: 320px; padding: 12px 14px; border-radius: 8px; background: rgba(20, 20, 20, 0.9); color: #f5f5f5;"> |
| 519 | + <div style="font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 8px;"> |
| 520 | + {{ wasmLoading ? "Loading VTK WASM" : "Syncing VTK Data" }} |
| 521 | + </div> |
| 522 | + <div v-if="wasmLoading" style="font-size: 12px; opacity: 0.85;"> |
| 523 | + Fetching WebAssembly bundle... |
| 524 | + </div> |
| 525 | + <div v-else> |
| 526 | + <div style="margin-bottom: 10px;"> |
| 527 | + <div style="display: flex; justify-content: space-between; font-size: 11px; margin-bottom: 4px; opacity: 0.8;"> |
| 528 | + <span>States</span> |
| 529 | + <span>{{ progress.state.current }}/{{ progress.state.total }}</span> |
| 530 | + </div> |
| 531 | + <div style="height: 6px; background: rgba(255, 255, 255, 0.15); border-radius: 4px; overflow: hidden;"> |
| 532 | + <div :style="{ width: statePercent + '%', height: '100%', background: '#4aa3ff' }"></div> |
| 533 | + </div> |
| 534 | + </div> |
| 535 | + <div> |
| 536 | + <div style="display: flex; justify-content: space-between; font-size: 11px; margin-bottom: 4px; opacity: 0.8;"> |
| 537 | + <span>Blobs</span> |
| 538 | + <span>{{ progress.hash.current }}/{{ progress.hash.total }}</span> |
| 539 | + </div> |
| 540 | + <div style="height: 6px; background: rgba(255, 255, 255, 0.15); border-radius: 4px; overflow: hidden;"> |
| 541 | + <div :style="{ width: hashPercent + '%', height: '100%', background: '#f5c542' }"></div> |
| 542 | + </div> |
| 543 | + </div> |
| 544 | + </div> |
| 545 | + </div> |
| 546 | + </div> |
| 547 | + </slot> |
| 548 | + </div>`, |
435 | 549 | }; |
0 commit comments