mini-skamja/components/model/scene.vue

89 lines
2.3 KiB
Vue

<script setup lang="ts">
import { Vector3 } from "three";
const { openModal } = useModalState();
const types = {
bench: defineAsyncComponent(() => import("./bench.vue")),
table: defineAsyncComponent(() => import("./bench-table.vue")),
};
const props = defineProps({
type: {
type: String as PropType<keyof typeof types>,
default: "bench",
required: true,
},
});
const camera = ref();
const controls = ref();
const controlsState = reactive({
minDistance: 0.5,
maxDistance: 5,
enablePan: false,
enableZoom: false,
// maxPolarAngle: Math.PI / 2 - 0.2
});
const changeDistance = (v = 1) => {
if (camera.value && controls.value) {
const distance = camera.value.position.distanceTo(new Vector3(0, 0, 0));
const r = distance + v;
camera.value.position.normalize().multiplyScalar(r);
}
};
</script>
<template>
<div class="relative h-[600px] max-h-screen">
<ClientOnly fallback-tag="div">
<template #fallback>
<div class="fallback">Загрузка 3D модели</div>
</template>
<TresCanvas height="600" clear-color="#e2e8f0">
<TresPerspectiveCamera :position="new Vector3(-7, 2, 4)" ref="camera" />
<OrbitControls v-bind="controlsState" ref="controls" make-default />
<ModelEnv />
<Suspense>
<component :is="types[props.type]" />
</Suspense>
</TresCanvas>
<div class="canvas-icons top-4 left-4">
<a
href="#"
@click.prevent="changeDistance(-0.5)"
:class="[
{
disabled: camera
? camera.position.distanceTo(new Vector3(0, 0, 0)) <=
controlsState.minDistance
: null,
},
]"
>
<Icon name="mdi:plus-circle-outline" />
</a>
<a
href="#"
@click.prevent="changeDistance(0.5)"
:class="[
{
disabled: camera
? camera.position.distanceTo(new Vector3(0, 0, 0)) >=
controlsState.maxDistance
: null,
},
]"
>
<Icon name="mdi:minus-circle-outline" />
</a>
</div>
</ClientOnly>
<div class="absolute bottom-4 left-1/2 -translate-x-1/2">
<button @click.prevent="openModal">Заказать</button>
</div>
</div>
</template>