|
<script lang="ts"> |
|
import { onMount, onDestroy } from "svelte"; |
|
import type { IViewer } from "./viewers/IViewer"; |
|
import { createViewer } from "./viewers/ViewerFactory"; |
|
import ArrowLeft from "carbon-icons-svelte/lib/ArrowLeft.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(() => { |
|
const maxWidth = container.clientHeight * (16 / 9); |
|
const maxHeight = container.clientWidth * (9 / 16); |
|
canvas.width = Math.min(container.clientWidth, maxWidth); |
|
canvas.height = Math.min(container.clientHeight, maxHeight); |
|
}); |
|
} |
|
|
|
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 = "screenshot.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}> |
|
|
|
|
|
<h2><span class="muted" on:click={onBack}>{modelName}/</span>{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} /> |
|
</div> |
|
|