This commit is contained in:
Kseninia Mikhaylova 2025-03-13 16:48:51 +03:00
parent 3ba9649952
commit 2faf0979fd
6 changed files with 164 additions and 60 deletions

View File

@ -174,7 +174,10 @@ a[href^="#"] {
@apply py-10; @apply py-10;
&_imgbg { &_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 { &_calc {

View File

@ -1,14 +1,38 @@
<script setup lang="ts"> <script setup lang="ts">
//@ts-ignore //@ts-ignore
import { useGLTF } from '@tresjs/cientos' 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> </script>
<template> <template>
<Suspense> <Suspense>
<TresGroup :position-y="0" :scale="new Vector3(2, 2, 2)"> <TresGroup :position-y="0" :scale="new Vector3(scale, scale, scale)">
<ModelItem :model="bench1" :target="[0, 0, 0]" /> <ModelItem :model="obj" :target="[0, 0, 0]" />
</TresGroup> </TresGroup>
</Suspense> </Suspense>
</template> </template>

View File

@ -1,14 +1,38 @@
<script setup lang="ts"> <script setup lang="ts">
//@ts-ignore //@ts-ignore
import { useGLTF } from '@tresjs/cientos' 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> </script>
<template> <template>
<Suspense> <Suspense>
<TresGroup :position-y="0" :scale="new Vector3(2, 2, 2)"> <TresGroup :position-y="0" :scale="new Vector3(scale, scale, scale)">
<ModelItem :model="bench1" :target="[0, 0, 0]" /> <ModelItem :model="obj" :target="[0, 0, 0]" />
</TresGroup> </TresGroup>
</Suspense> </Suspense>
</template> </template>

View File

@ -14,27 +14,27 @@ const props = defineProps({
} }
}) })
const camera = ref()
const controls = ref()
const controlsState = reactive({ const controlsState = reactive({
minDistance: 2, // minDistance: 2,
maxDistance: 10, // maxDistance: 10,
enablePan: false, enablePan: false,
enableZoom: false, enableZoom: false,
maxPolarAngle: Math.PI / 2 - 0.2 maxPolarAngle: Math.PI / 2 - 0.2
}) })
const toggleModal = () => {}
</script> </script>
<template> <template>
<div>
<div class="relative h-[600px] max-h-screen"> <div class="relative h-[600px] max-h-screen">
<ClientOnly fallback-tag="div"> <ClientOnly fallback-tag="div">
<template #fallback> <template #fallback>
<div class="fallback">Загрузка 3D модели</div> <div class="fallback">Загрузка 3D модели</div>
</template> </template>
<TresCanvas height="600"> <TresCanvas height="600">
<TresPerspectiveCamera :position="new Vector3(-7, 2, 4)" ref="camera" /> <TresPerspectiveCamera />
<OrbitControls v-bind="controlsState" ref="controls" make-default /> <OrbitControls v-bind="controlsState" make-default />
<ModelEnv /> <ModelEnv />
<Suspense> <Suspense>
<component :is="types[props.type]" /> <component :is="types[props.type]" />
@ -42,4 +42,6 @@ const controlsState = reactive({
</TresCanvas> </TresCanvas>
</ClientOnly> </ClientOnly>
</div> </div>
<button @click.prevent="toggleModal">Рассчитать</button>
</div>
</template> </template>

View File

@ -46,7 +46,7 @@ const delivery = computed(() =>
<div class="siteblock-image"> <div class="siteblock-image">
<NuxtImg <NuxtImg
:src="[imgBase, page.image].join('/')" :src="[imgBase, page.image].join('/')"
alt="разные цвета забора" :alt="page.title"
title="" title=""
format="webp" format="webp"
/> />
@ -63,13 +63,12 @@ const delivery = computed(() =>
> >
<NuxtImg <NuxtImg
:src="[imgBase, page.image].join('/')" :src="[imgBase, page.image].join('/')"
class="invisible" :alt="page.title"
alt="отзыв"
title=""
format="webp" format="webp"
loading="lazy" loading="lazy"
/> />
</div> </div>
<div class="siteblock bg-white">
<div class="container" :id="page.slug"> <div class="container" :id="page.slug">
<template v-for="item in review?.slice(0, 3)"> <template v-for="item in review?.slice(0, 3)">
<div class="review"> <div class="review">
@ -91,12 +90,13 @@ const delivery = computed(() =>
</div> </div>
</template> </template>
</div> </div>
</div>
</template> </template>
<template v-if="page.slug == 'advantages'"> <template v-if="page.slug == 'advantages'">
<div class="siteblock bg-white" :id="page.slug"> <div class="siteblock bg-white" :id="page.slug">
<div class="container gap-4"> <div class="container gap-4">
<div class="col-span-full xl:col-span-8"> <div class="col-span-full xl:col-span-8">
<ModelScene /> <ModelScene type="bench" />
</div> </div>
<div class="col-span-full xl:col-span-4 prose"> <div class="col-span-full xl:col-span-4 prose">
<span v-html="marked.parse(page.content || '')"></span> <span v-html="marked.parse(page.content || '')"></span>
@ -113,9 +113,6 @@ const delivery = computed(() =>
> >
<NuxtImg <NuxtImg
:src="[imgBase, page.image].join('/')" :src="[imgBase, page.image].join('/')"
class="invisible"
alt="коричневый забор"
title=""
format="webp" format="webp"
loading="lazy" loading="lazy"
/> />
@ -140,9 +137,6 @@ const delivery = computed(() =>
> >
<NuxtImg <NuxtImg
:src="[imgBase, page.image].join('/')" :src="[imgBase, page.image].join('/')"
class="invisible"
alt="коричневый забор"
title=""
format="webp" format="webp"
loading="lazy" loading="lazy"
/> />

View File

@ -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 }
}