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);
// 提升性能