forked from mns/mini-skamja
threejs
This commit is contained in:
parent
3ba9649952
commit
2faf0979fd
|
@ -174,7 +174,10 @@ a[href^="#"] {
|
|||
@apply py-10;
|
||||
|
||||
&_imgbg {
|
||||
@apply py-0 bg-no-repeat bg-cover bg-bottom h-[50vh] min-h-[600px];
|
||||
@apply relative py-0 bg-no-repeat bg-[auto_100%] bg-center h-[50vh] min-h-[300px] bg-transparent overflow-hidden;
|
||||
img {
|
||||
@apply absolute top-0 right-0 bottom-0 left-0 blur-lg w-full -z-10 scale-110;
|
||||
}
|
||||
}
|
||||
|
||||
&_calc {
|
||||
|
|
|
@ -1,14 +1,38 @@
|
|||
<script setup lang="ts">
|
||||
//@ts-ignore
|
||||
import { useGLTF } from '@tresjs/cientos'
|
||||
import { Vector3 } from 'three';
|
||||
import { useTresContext } from '@tresjs/core'
|
||||
import { Vector3 } from 'three'
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const { scene: bench1 } = await useGLTF('/models/bench2_export-v1.glb')
|
||||
const { camera, controls } = useTresContext()
|
||||
|
||||
const { scene: obj } = await useGLTF('/models/bench2_export-v1.glb')
|
||||
const scale = ref(1) // Масштаб объекта
|
||||
|
||||
onMounted(() => {
|
||||
if (obj && camera.value && controls.value) {
|
||||
const distance = 10
|
||||
const cameraOffset = { x: 1, y: 0.75, z: -1.25 } // Смещение камеры
|
||||
|
||||
// Вычисляем масштаб и позицию камеры
|
||||
const { scale: objectScale } = calculateScaleToFit(
|
||||
obj,
|
||||
camera.value as any,
|
||||
controls.value as any,
|
||||
distance,
|
||||
cameraOffset
|
||||
)
|
||||
|
||||
scale.value = objectScale
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Suspense>
|
||||
<TresGroup :position-y="0" :scale="new Vector3(2, 2, 2)">
|
||||
<ModelItem :model="bench1" :target="[0, 0, 0]" />
|
||||
<TresGroup :position-y="0" :scale="new Vector3(scale, scale, scale)">
|
||||
<ModelItem :model="obj" :target="[0, 0, 0]" />
|
||||
</TresGroup>
|
||||
</Suspense>
|
||||
</template>
|
||||
</template>
|
|
@ -1,14 +1,38 @@
|
|||
<script setup lang="ts">
|
||||
//@ts-ignore
|
||||
import { useGLTF } from '@tresjs/cientos'
|
||||
import { Vector3 } from 'three';
|
||||
import { useTresContext } from '@tresjs/core'
|
||||
import { Vector3 } from 'three'
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const { scene: bench1 } = await useGLTF('/models/bench_export-v1.glb')
|
||||
const { camera, controls } = useTresContext()
|
||||
|
||||
const { scene: obj } = await useGLTF('/models/bench_export-v1.glb')
|
||||
const scale = ref(1) // Масштаб объекта
|
||||
|
||||
onMounted(() => {
|
||||
if (obj && camera.value && controls.value) {
|
||||
const distance = 10
|
||||
const cameraOffset = { x: -1, y: 0.75, z: -1.25 } // Смещение камеры
|
||||
|
||||
// Вычисляем масштаб и позицию камеры
|
||||
const { scale: objectScale } = calculateScaleToFit(
|
||||
obj,
|
||||
camera.value as any,
|
||||
controls.value as any,
|
||||
distance,
|
||||
cameraOffset
|
||||
)
|
||||
|
||||
scale.value = objectScale
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Suspense>
|
||||
<TresGroup :position-y="0" :scale="new Vector3(2, 2, 2)">
|
||||
<ModelItem :model="bench1" :target="[0, 0, 0]" />
|
||||
<TresGroup :position-y="0" :scale="new Vector3(scale, scale, scale)">
|
||||
<ModelItem :model="obj" :target="[0, 0, 0]" />
|
||||
</TresGroup>
|
||||
</Suspense>
|
||||
</template>
|
||||
</template>
|
|
@ -14,32 +14,34 @@ const props = defineProps({
|
|||
}
|
||||
})
|
||||
|
||||
const camera = ref()
|
||||
const controls = ref()
|
||||
|
||||
const controlsState = reactive({
|
||||
minDistance: 2,
|
||||
maxDistance: 10,
|
||||
// minDistance: 2,
|
||||
// maxDistance: 10,
|
||||
enablePan: false,
|
||||
enableZoom: false,
|
||||
maxPolarAngle: Math.PI / 2 - 0.2
|
||||
})
|
||||
|
||||
const toggleModal = () => {}
|
||||
</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">
|
||||
<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>
|
||||
</ClientOnly>
|
||||
<div>
|
||||
<div class="relative h-[600px] max-h-screen">
|
||||
<ClientOnly fallback-tag="div">
|
||||
<template #fallback>
|
||||
<div class="fallback">Загрузка 3D модели</div>
|
||||
</template>
|
||||
<TresCanvas height="600">
|
||||
<TresPerspectiveCamera />
|
||||
<OrbitControls v-bind="controlsState" make-default />
|
||||
<ModelEnv />
|
||||
<Suspense>
|
||||
<component :is="types[props.type]" />
|
||||
</Suspense>
|
||||
</TresCanvas>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
<button @click.prevent="toggleModal">Рассчитать</button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -46,7 +46,7 @@ const delivery = computed(() =>
|
|||
<div class="siteblock-image">
|
||||
<NuxtImg
|
||||
:src="[imgBase, page.image].join('/')"
|
||||
alt="разные цвета забора"
|
||||
:alt="page.title"
|
||||
title=""
|
||||
format="webp"
|
||||
/>
|
||||
|
@ -63,40 +63,40 @@ const delivery = computed(() =>
|
|||
>
|
||||
<NuxtImg
|
||||
:src="[imgBase, page.image].join('/')"
|
||||
class="invisible"
|
||||
alt="отзыв"
|
||||
title=""
|
||||
:alt="page.title"
|
||||
format="webp"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div class="container" :id="page.slug">
|
||||
<template v-for="item in review?.slice(0, 3)">
|
||||
<div class="review">
|
||||
<div class="review-image">
|
||||
<NuxtImg
|
||||
:src="[imgBase, item.image].join('/')"
|
||||
:alt="item.text"
|
||||
title=""
|
||||
format="webp"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div class="siteblock bg-white">
|
||||
<div class="container" :id="page.slug">
|
||||
<template v-for="item in review?.slice(0, 3)">
|
||||
<div class="review">
|
||||
<div class="review-image">
|
||||
<NuxtImg
|
||||
:src="[imgBase, item.image].join('/')"
|
||||
:alt="item.text"
|
||||
title=""
|
||||
format="webp"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div class="review-content">
|
||||
{{ item.comment }}
|
||||
</div>
|
||||
<div class="review-title">
|
||||
{{ item.text }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="review-content">
|
||||
{{ item.comment }}
|
||||
</div>
|
||||
<div class="review-title">
|
||||
{{ item.text }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="page.slug == 'advantages'">
|
||||
<div class="siteblock bg-white" :id="page.slug">
|
||||
<div class="container gap-4">
|
||||
<div class="col-span-full xl:col-span-8">
|
||||
<ModelScene />
|
||||
<ModelScene type="bench" />
|
||||
</div>
|
||||
<div class="col-span-full xl:col-span-4 prose">
|
||||
<span v-html="marked.parse(page.content || '')"></span>
|
||||
|
@ -113,9 +113,6 @@ const delivery = computed(() =>
|
|||
>
|
||||
<NuxtImg
|
||||
:src="[imgBase, page.image].join('/')"
|
||||
class="invisible"
|
||||
alt="коричневый забор"
|
||||
title=""
|
||||
format="webp"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
@ -140,9 +137,6 @@ const delivery = computed(() =>
|
|||
>
|
||||
<NuxtImg
|
||||
:src="[imgBase, page.image].join('/')"
|
||||
class="invisible"
|
||||
alt="коричневый забор"
|
||||
title=""
|
||||
format="webp"
|
||||
loading="lazy"
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import { Box3, PerspectiveCamera, Vector3 } from 'three'
|
||||
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||
|
||||
/**
|
||||
* Вычисляет масштаб объекта и позицию камеры, чтобы объект полностью помещался в видимой области.
|
||||
* @param object - Объект Three.js (например, загруженная модель).
|
||||
* @param camera - Экземпляр камеры.
|
||||
* @param controls - Экземпляр OrbitControls.
|
||||
* @param distance - Расстояние от камеры до объекта.
|
||||
* @param cameraOffset - Коэффициенты смещения камеры (x, y, z).
|
||||
*/
|
||||
export default function (
|
||||
object: any,
|
||||
camera: PerspectiveCamera,
|
||||
controls: any,
|
||||
distance: number,
|
||||
cameraOffset: { x: number; y: number; z: number }
|
||||
) {
|
||||
// Обновляем мировую матрицу объекта
|
||||
object.updateWorldMatrix(true, true)
|
||||
|
||||
const fov = camera.fov // Угол обзора камеры
|
||||
const aspect = camera.aspect // Соотношение сторон
|
||||
|
||||
const bbox = new Box3().setFromObject(object) // Вычисляем bounding box
|
||||
const center = new Vector3()
|
||||
bbox.getCenter(center) // Центр объекта
|
||||
|
||||
const size = new Vector3()
|
||||
bbox.getSize(size) // Размеры объекта
|
||||
|
||||
// Рассчитываем масштаб, чтобы объект полностью помещался в видимой области
|
||||
const vFOV = degToRad(fov) // Угол обзора в радианах
|
||||
const visibleHeight = 2 * Math.tan(vFOV / 2) * distance // Видимая высота
|
||||
const visibleWidth = visibleHeight * aspect // Видимая ширина
|
||||
|
||||
const scaleX = visibleWidth / size.x
|
||||
const scaleY = visibleHeight / size.y
|
||||
const scaleZ = visibleWidth / size.z
|
||||
|
||||
const scale = Math.min(scaleX, scaleY, scaleZ)
|
||||
|
||||
// Позиционируем камеру с учетом смещения
|
||||
camera.position.set(
|
||||
center.x + distance * cameraOffset.x,
|
||||
center.y + distance * cameraOffset.y,
|
||||
center.z + distance * cameraOffset.z
|
||||
)
|
||||
|
||||
// Устанавливаем цель для OrbitControls
|
||||
if (controls) {
|
||||
controls.target.set(center.x, center.y, center.z)
|
||||
controls.update() // Обновляем контроллы
|
||||
}
|
||||
|
||||
return { scale, center }
|
||||
}
|
Loading…
Reference in New Issue