113 lines
2.8 KiB
Vue
113 lines
2.8 KiB
Vue
<!-- 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>
|