diff --git a/src/features/AgentViewer/index.tsx b/src/features/AgentViewer/index.tsx index a3a40022..8f71dea5 100644 --- a/src/features/AgentViewer/index.tsx +++ b/src/features/AgentViewer/index.tsx @@ -32,9 +32,25 @@ function AgentViewer(props: Props) { (canvas: HTMLCanvasElement) => { if (canvas) { viewer.setup(canvas); - fetchModelUrl(agent.agentId, agent.meta.model!).then((modelUrl) => { + fetchModelUrl(agent.agentId, agent.meta.model!).then(async (modelUrl) => { if (modelUrl) { - viewer.loadVrm(modelUrl); + // add loading dom + const agentViewer = document.querySelector('#agent-viewer')!; + const loadingScreen = document.createElement('div'); + loadingScreen.setAttribute('id', 'loading-screen'); + const loader = document.createElement('div'); + loader.setAttribute('id', 'loader'); + loadingScreen.append(loader); + agentViewer.append(loadingScreen); + + // load + await viewer.loadVrm(modelUrl); + + // remove loading dom + loadingScreen.classList.add('fade-out'); + loadingScreen.addEventListener('transitionend', () => { + loadingScreen.remove(); + }); } }); @@ -90,6 +106,7 @@ function AgentViewer(props: Props) {
diff --git a/src/features/AgentViewer/style.ts b/src/features/AgentViewer/style.ts index 32e5099a..0beb23a4 100644 --- a/src/features/AgentViewer/style.ts +++ b/src/features/AgentViewer/style.ts @@ -6,6 +6,99 @@ export const useStyles = createStyles(({ css, token }) => ({ width: 100%; height: 100%; min-height: 0; + + #loading-screen { + position: absolute; + top: 0; + left: 0; + + width: 100%; + height: 100%; + + opacity: 1; + background-color: #000; + + transition: 1s opacity; + } + + #loading-screen.fade-out { + opacity: 0; + } + + #loader { + position: relative; + top: 50%; + left: 50%; + + display: block; + + width: 150px; + height: 150px; + margin: -75px 0 0 -75px; + + border: 3px solid transparent; + border-top-color: #9370db; + border-radius: 50%; + + animation: spin 2s linear infinite; + animation: spin 2s linear infinite; + } + + #loader::before { + content: ''; + + position: absolute; + inset: 5px; + + border: 3px solid transparent; + border-top-color: #ba55d3; + border-radius: 50%; + + animation: spin 3s linear infinite; + animation: spin 3s linear infinite; + } + + #loader::after { + content: ''; + + position: absolute; + inset: 15px; + + border: 3px solid transparent; + border-top-color: #f0f; + border-radius: 50%; + + animation: spin 1.5s linear infinite; + animation: spin 1.5s linear infinite; + } + + @keyframes spin { + 0% { + transform: rotate(0deg); + transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + transform: rotate(360deg); + transform: rotate(360deg); + } + } + + @keyframes spin { + 0% { + transform: rotate(0deg); + transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + transform: rotate(360deg); + transform: rotate(360deg); + } + } `, toolbar: css` position: absolute; diff --git a/src/libs/vrmViewer/model.ts b/src/libs/vrmViewer/model.ts index 9ee027e5..bee285e8 100644 --- a/src/libs/vrmViewer/model.ts +++ b/src/libs/vrmViewer/model.ts @@ -1,6 +1,6 @@ import { VRM, VRMLoaderPlugin, VRMUtils } from '@pixiv/three-vrm'; import * as THREE from 'three'; -import { AnimationAction, AnimationClip } from 'three'; +import { AnimationAction, AnimationClip, LoadingManager } from 'three'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; import { LoopOnce } from 'three/src/constants'; @@ -50,7 +50,6 @@ export class Model { autoUpdateHumanBones: true, }), ); - const gltf = await loader.loadAsync(url); // 提升性能