Spaces:
Running
Running
<script lang="ts"> | |
import { onMount, onDestroy } from "svelte"; | |
import type { IViewer } from "./viewers/IViewer"; | |
import { createViewer } from "./viewers/ViewerFactory"; | |
import { ArrowLeft, Cube, WatsonHealth3DPrintMesh } from "carbon-icons-svelte"; | |
interface Scene { | |
name: string; | |
url: string; | |
thumbnail: string; | |
} | |
export let modelName: string; | |
export let scene: Scene; | |
export let onBack: () => void; | |
let container: HTMLDivElement; | |
let canvas: HTMLCanvasElement; | |
let overlay: HTMLDivElement; | |
let loadingBarFill: HTMLDivElement; | |
let viewer: IViewer; | |
async function loadScene() { | |
overlay.style.display = "flex"; | |
viewer = await createViewer(scene.url, canvas, (progress) => { | |
loadingBarFill.style.width = `${progress * 100}%`; | |
}); | |
window.addEventListener("resize", handleResize); | |
window.addEventListener("keydown", handleKeyDown); | |
handleResize(); | |
overlay.style.display = "none"; | |
} | |
function handleResize() { | |
if (!canvas || !container) return; | |
requestAnimationFrame(() => { | |
canvas.width = container.clientWidth; | |
canvas.height = container.clientHeight; | |
}); | |
} | |
function handleKeyDown(e: KeyboardEvent) { | |
if (e.code === "KeyP") { | |
capture(); | |
} | |
} | |
async function capture() { | |
const data = await viewer.capture(); | |
if (!data) { | |
console.error("Failed to capture screenshot"); | |
return; | |
} | |
const a = document.createElement("a"); | |
a.href = data; | |
a.download = `${scene.name}.png`; | |
a.click(); | |
} | |
onMount(loadScene); | |
onDestroy(() => { | |
viewer?.dispose(); | |
if (typeof window !== "undefined") { | |
window.removeEventListener("resize", handleResize); | |
window.removeEventListener("keydown", handleKeyDown); | |
} | |
}); | |
</script> | |
<div class="header"> | |
<div class="back" aria-label="Back" aria-hidden="true" on:click={onBack}> | |
<ArrowLeft size={24} /> | |
</div> | |
<div class="spacer" /> | |
<button class="title-button" on:click={loadScene}> | |
<!-- svelte-ignore a11y-click-events-have-key-events --> | |
<!-- svelte-ignore a11y-no-static-element-interactions --> | |
<h2> | |
<span class="muted" on:click={onBack}>{modelName}/</span>{scene.name.length > 16 | |
? `${scene.name.slice(0, 16)}...` | |
: scene.name} | |
</h2> | |
</button> | |
<div class="desktop-spacer" /> | |
</div> | |
<div class="canvas-container" bind:this={container}> | |
<div bind:this={overlay} class="loading-overlay"> | |
<div class="loading-bar"> | |
<div bind:this={loadingBarFill} class="loading-bar-fill" /> | |
</div> | |
</div> | |
<canvas class="viewer-canvas" bind:this={canvas} width={800} height={600}> </canvas> | |
<div class="stats"> | |
{#if viewer} | |
<p>vertex count: {viewer.vertexCount}</p> | |
{/if} | |
</div> | |
{#if viewer && !viewer.topoOnly} | |
<div class="mode-toggle"> | |
<label> | |
<input | |
type="radio" | |
name="modeB" | |
value="default" | |
checked | |
on:change={() => viewer.setRenderMode("default")} | |
/> | |
<Cube class="mode-toggle-icon" /> | |
</label> | |
<label> | |
<input | |
type="radio" | |
name="modeB" | |
value="wireframe" | |
on:change={() => viewer.setRenderMode("wireframe")} | |
/> | |
<WatsonHealth3DPrintMesh class="mode-toggle-icon" /> | |
</label> | |
</div> | |
{/if} | |
</div> | |