Merge pull request 'debouncing' (#88) from bx-2376-new_type into dev
Deploy / build_and_push_images (push) Waiting to run Details
Deploy / deploy_to_server_dev (push) Blocked by required conditions Details

Reviewed-on: #88
This commit is contained in:
ksenia_mikhailova 2025-03-20 12:16:16 +03:00
commit 01cb9440b2
6 changed files with 144 additions and 42 deletions

View File

@ -3,6 +3,9 @@ import { TresCanvas } from '@tresjs/core'
import { Stats, OrbitControls } from '@tresjs/cientos'
import { degToRad } from 'three/src/math/MathUtils.js';
const container = ref<HTMLElement | null>(null);
const { isIntersecting, startObserver, stopObserver } = useSceneVisibility();
const controlsState = reactive({
position: { x: 0, y: 0, z: 0 },
enablePan: false,
@ -17,9 +20,21 @@ const cameraStat = reactive({
aspect: 1920 / 600,
// fov: 40,
})
const renderMode = computed(() => (isIntersecting.value ? 'always' : 'manual'));
onMounted(async () => {
if (container.value) {
await nextTick()
startObserver(container.value);
}
});
onBeforeUnmount(() => {
stopObserver();
});
</script>
<template>
<div class="calc">
<div class="calc" ref="container">
<ClientOnly fallback-tag="div">
<template #fallback>
<div class="fallback">
@ -27,9 +42,12 @@ const cameraStat = reactive({
</div>
</template>
<Loader />
<Scene :canvasProps="{ clearColor: '#e2e8f0' }">
<TresCanvas clear-color="#e2e8f0" :render-mode="renderMode" :key="renderMode">
<TresPerspectiveCamera v-bind="cameraStat" ref="camera" />
<OrbitControls v-bind="controlsState" make-default />
<Suspense>
<ModelEnv />
</Suspense>
<Suspense>
<ModelSmoothCamera />
</Suspense>
@ -38,7 +56,7 @@ const cameraStat = reactive({
<ModelParametric />
</Suspense>
</TresGroup>
</Scene>
</TresCanvas>
</ClientOnly>
</div>
</template>

View File

@ -3,6 +3,9 @@ import { TresCanvas } from '@tresjs/core'
import { OrbitControls } from '@tresjs/cientos'
import { Vector3 } from 'three';
const container = ref<HTMLElement | null>(null);
const { isIntersecting, startObserver, stopObserver } = useSceneVisibility();
const camera = ref()
const controls = ref()
const controlsState = reactive({
@ -25,36 +28,56 @@ const changeDistance = (v = 1) => {
camera.value.position.normalize().multiplyScalar(r)
}
}
const renderMode = computed(() => (isIntersecting.value ? 'always' : 'manual'));
onMounted(async () => {
if (container.value) {
await nextTick()
startObserver(container.value);
}
});
onBeforeUnmount(() => {
stopObserver();
});
</script>
<template>
<div class="h-96 relative">
<ClientOnly fallback-tag="div">
<template #fallback>
<div class="fallback">
Загрузка 3D модели
</div>
</template>
<Scene :canvasProps="{ height: '600' }">
<TresPerspectiveCamera :position="[-7, 2, 4]" ref="camera" />
<OrbitControls v-bind="controlsState" ref="controls" make-default />
<ModelDiagram />
</Scene>
</ClientOnly>
<div class="canvas-icons">
<a href="#" @click.prevent="toggleExpState">
<Icon name="mdi:arrow-collapse-horizontal" v-if="explosion_state" />
<Icon name="mdi:arrow-expand-horizontal" v-else />
</a>
<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>
<Suspense>
<div ref="container" class="h-96 relative">
<ClientOnly fallback-tag="div">
<template #fallback>
<div class="fallback">
Загрузка 3D модели
</div>
</template>
<TresCanvas height="600" :render-mode="renderMode" :key="renderMode">
<TresPerspectiveCamera :position="[-7, 2, 4]" ref="camera" />
<OrbitControls v-bind="controlsState" ref="controls" make-default />
<Suspense>
<ModelEnv />
</Suspense>
<Suspense>
<ModelDiagram />
</Suspense>
</TresCanvas>
</ClientOnly>
<div class="canvas-icons">
<a href="#" @click.prevent="toggleExpState">
<Icon name="mdi:arrow-collapse-horizontal" v-if="explosion_state" />
<Icon name="mdi:arrow-expand-horizontal" v-else />
</a>
<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>
</div>
</div>
</Suspense>
</template>
<style scoped>

View File

@ -8,7 +8,7 @@ import {
import { GainMapLoader, } from '@monogrid/gainmap-js'
import { useLoop } from '@tresjs/core';
const { scene, renderer, camera } = useTresContext()
const {onBeforeRender} = useLoop()
const { onBeforeRender } = useLoop()
renderer.value.toneMapping = CineonToneMapping
renderer.value.toneMappingExposure = 1
@ -18,6 +18,7 @@ renderer.value.shadowMap.type = PCFSoftShadowMap
const pmremGenerator = new PMREMGenerator(renderer.value);
pmremGenerator.compileEquirectangularShader();
onMounted(async () => {
const loader = new GainMapLoader(renderer.value)
const result = await loader.loadAsync([
@ -35,14 +36,15 @@ onMounted(async () => {
scene.value.environmentIntensity = 1.25
scene.value.environmentRotation.z = 0.25
result.renderTarget.texture.dispose();
})
onBeforeRender(()=>{
if(camera.value) {
const cameraDirection = new Vector3()
camera.value.getWorldDirection(cameraDirection);
const angle = Math.atan2(cameraDirection.z, cameraDirection.x);
scene.value.environmentRotation.z = angle + 0.25
}
onBeforeRender(() => {
if (camera.value) {
const cameraDirection = new Vector3()
camera.value.getWorldDirection(cameraDirection);
const angle = Math.atan2(cameraDirection.z, cameraDirection.x);
scene.value.environmentRotation.z = angle + 0.25
}
})
})
</script>
<template></template>

View File

@ -0,0 +1,57 @@
// useSceneVisibility.ts
import { ref, onMounted, onBeforeUnmount } from 'vue';
export function useSceneVisibility() {
const isIntersecting = ref(false); // Состояние видимости
let observer: IntersectionObserver | null = null;
let debounceTimeout: ReturnType<typeof setTimeout> | null = null;
const startObserver = (element: HTMLElement) => {
observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (debounceTimeout) {
clearTimeout(debounceTimeout); // Очищаем предыдущий таймер
}
// Устанавливаем новый таймер
debounceTimeout = setTimeout(() => {
isIntersecting.value = entry.isIntersecting;
}, 100); // Задержка в 300 мс
});
},
{ threshold: 0.05 } // Порог видимости
);
if (element) {
observer.observe(element);
}
};
const stopObserver = () => {
if (observer) {
observer.disconnect();
observer = null;
}
if (debounceTimeout) {
clearTimeout(debounceTimeout); // Очищаем таймер при остановке наблюдателя
debounceTimeout = null;
}
};
onMounted(() => {
// Инициализация наблюдателя при монтировании
});
onBeforeUnmount(() => {
// Очистка наблюдателя при размонтировании
stopObserver();
});
return {
isIntersecting,
startObserver,
stopObserver,
};
}

View File

@ -51,9 +51,9 @@ export default defineNuxtConfig({
vite: {
assetsInclude: ['**/*.glb', '**/*.gltf'],
build: {
target: 'esnext'
target: 'esnext',
// minify: 'esbuild'
// minify: false
minify: false,
},
},
robots: {

View File

@ -133,7 +133,9 @@ watch(openTab, () => {
</div>
<div class="container gap-4">
<div class="col-span-full xl:col-span-8">
<ExpDiagram />
<Suspense>
<ExpDiagram />
</Suspense>
</div>
<div class="col-span-full xl:col-span-4 prose">
<span v-html="advantagesText"></span>