feat: 添加 InspiraUI 组件和模式背景功能,更新依赖项
This commit is contained in:
112
src/components/InspiraUI/spline/Spline.vue
Normal file
112
src/components/InspiraUI/spline/Spline.vue
Normal file
@ -0,0 +1,112 @@
|
||||
<!-- Spline.vue -->
|
||||
<template>
|
||||
<ParentSize
|
||||
:parent-size-styles="parentSizeStyles"
|
||||
:debounce-time="50"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<template #default>
|
||||
<canvas
|
||||
ref="canvasRef"
|
||||
:style="canvasStyle"
|
||||
/>
|
||||
<slot v-if="isLoading" />
|
||||
</template>
|
||||
</ParentSize>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { ref, onMounted, onUnmounted, computed } from "vue";
|
||||
import { Application, type SplineEventName } from "@splinetool/runtime";
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
import ParentSize from "./ParentSize.vue";
|
||||
|
||||
const props = defineProps({
|
||||
scene: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
onLoad: Function,
|
||||
renderOnDemand: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
style: Object,
|
||||
});
|
||||
|
||||
let cleanUpFns: any[] = [];
|
||||
|
||||
const emit = defineEmits([
|
||||
"error",
|
||||
...[
|
||||
"spline-mouse-down",
|
||||
"spline-mouse-up",
|
||||
"spline-mouse-hover",
|
||||
"spline-key-down",
|
||||
"spline-key-up",
|
||||
"spline-start",
|
||||
"spline-look-at",
|
||||
"spline-follow",
|
||||
"spline-scroll",
|
||||
],
|
||||
]);
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
||||
const isLoading = ref(true);
|
||||
const splineApp = ref<Application | null>(null);
|
||||
|
||||
const parentSizeStyles = computed(() => ({
|
||||
overflow: "hidden",
|
||||
...props.style,
|
||||
}));
|
||||
|
||||
const canvasStyle = computed(() => ({
|
||||
display: isLoading.value ? "none" : "block",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}));
|
||||
|
||||
function eventHandler(name: SplineEventName, handler?: (e: any) => void) {
|
||||
if (!handler || !splineApp.value) return;
|
||||
const debouncedHandler = useDebounceFn(handler, 50, {
|
||||
maxWait: 100,
|
||||
});
|
||||
splineApp.value.addEventListener(name, debouncedHandler);
|
||||
return () => splineApp.value?.removeEventListener(name, debouncedHandler);
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (!canvasRef.value) return;
|
||||
|
||||
try {
|
||||
splineApp.value = new Application(canvasRef.value, {
|
||||
renderOnDemand: props.renderOnDemand,
|
||||
});
|
||||
|
||||
await splineApp.value.load(props.scene);
|
||||
|
||||
cleanUpFns = [
|
||||
eventHandler("mouseDown", (e: any) => emit("spline-mouse-down", e)),
|
||||
eventHandler("mouseUp", (e: any) => emit("spline-mouse-up", e)),
|
||||
eventHandler("mouseHover", (e: any) => emit("spline-mouse-hover", e)),
|
||||
eventHandler("keyDown", (e: any) => emit("spline-key-down", e)),
|
||||
eventHandler("keyUp", (e: any) => emit("spline-key-up", e)),
|
||||
eventHandler("start", (e: any) => emit("spline-start", e)),
|
||||
eventHandler("lookAt", (e: any) => emit("spline-look-at", e)),
|
||||
eventHandler("follow", (e: any) => emit("spline-follow", e)),
|
||||
eventHandler("scroll", (e: any) => emit("spline-scroll", e)),
|
||||
].filter(Boolean);
|
||||
|
||||
isLoading.value = false;
|
||||
props.onLoad?.(splineApp.value);
|
||||
} catch (err) {
|
||||
emit("error", err);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
cleanUpFns.forEach((fn) => fn?.());
|
||||
splineApp.value?.dispose();
|
||||
});
|
||||
</script>
|
Reference in New Issue
Block a user