debouncing #88
|
@ -3,6 +3,9 @@ import { TresCanvas } from '@tresjs/core'
|
||||||
import { Stats, OrbitControls } from '@tresjs/cientos'
|
import { Stats, OrbitControls } from '@tresjs/cientos'
|
||||||
import { degToRad } from 'three/src/math/MathUtils.js';
|
import { degToRad } from 'three/src/math/MathUtils.js';
|
||||||
|
|
||||||
|
const container = ref<HTMLElement | null>(null);
|
||||||
|
const { isIntersecting, startObserver, stopObserver } = useSceneVisibility();
|
||||||
|
|
||||||
const controlsState = reactive({
|
const controlsState = reactive({
|
||||||
position: { x: 0, y: 0, z: 0 },
|
position: { x: 0, y: 0, z: 0 },
|
||||||
enablePan: false,
|
enablePan: false,
|
||||||
|
@ -17,9 +20,21 @@ const cameraStat = reactive({
|
||||||
aspect: 1920 / 600,
|
aspect: 1920 / 600,
|
||||||
// fov: 40,
|
// fov: 40,
|
||||||
})
|
})
|
||||||
|
const renderMode = computed(() => (isIntersecting.value ? 'always' : 'manual'));
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
if (container.value) {
|
||||||
|
await nextTick()
|
||||||
|
startObserver(container.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
stopObserver();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="calc">
|
<div class="calc" ref="container">
|
||||||
<ClientOnly fallback-tag="div">
|
<ClientOnly fallback-tag="div">
|
||||||
<template #fallback>
|
<template #fallback>
|
||||||
<div class="fallback">
|
<div class="fallback">
|
||||||
|
@ -27,9 +42,12 @@ const cameraStat = reactive({
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<Loader />
|
<Loader />
|
||||||
<Scene :canvasProps="{ clearColor: '#e2e8f0' }">
|
<TresCanvas clear-color="#e2e8f0" :render-mode="renderMode" :key="renderMode">
|
||||||
<TresPerspectiveCamera v-bind="cameraStat" ref="camera" />
|
<TresPerspectiveCamera v-bind="cameraStat" ref="camera" />
|
||||||
<OrbitControls v-bind="controlsState" make-default />
|
<OrbitControls v-bind="controlsState" make-default />
|
||||||
|
<Suspense>
|
||||||
|
<ModelEnv />
|
||||||
|
</Suspense>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<ModelSmoothCamera />
|
<ModelSmoothCamera />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
@ -38,7 +56,7 @@ const cameraStat = reactive({
|
||||||
<ModelParametric />
|
<ModelParametric />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</TresGroup>
|
</TresGroup>
|
||||||
</Scene>
|
</TresCanvas>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
|
@ -3,6 +3,9 @@ import { TresCanvas } from '@tresjs/core'
|
||||||
import { OrbitControls } from '@tresjs/cientos'
|
import { OrbitControls } from '@tresjs/cientos'
|
||||||
import { Vector3 } from 'three';
|
import { Vector3 } from 'three';
|
||||||
|
|
||||||
|
const container = ref<HTMLElement | null>(null);
|
||||||
|
const { isIntersecting, startObserver, stopObserver } = useSceneVisibility();
|
||||||
|
|
||||||
const camera = ref()
|
const camera = ref()
|
||||||
const controls = ref()
|
const controls = ref()
|
||||||
const controlsState = reactive({
|
const controlsState = reactive({
|
||||||
|
@ -25,20 +28,39 @@ const changeDistance = (v = 1) => {
|
||||||
camera.value.position.normalize().multiplyScalar(r)
|
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>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="h-96 relative">
|
<Suspense>
|
||||||
|
<div ref="container" class="h-96 relative">
|
||||||
<ClientOnly fallback-tag="div">
|
<ClientOnly fallback-tag="div">
|
||||||
<template #fallback>
|
<template #fallback>
|
||||||
<div class="fallback">
|
<div class="fallback">
|
||||||
Загрузка 3D модели
|
Загрузка 3D модели
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<Scene :canvasProps="{ height: '600' }">
|
<TresCanvas height="600" :render-mode="renderMode" :key="renderMode">
|
||||||
<TresPerspectiveCamera :position="[-7, 2, 4]" ref="camera" />
|
<TresPerspectiveCamera :position="[-7, 2, 4]" ref="camera" />
|
||||||
<OrbitControls v-bind="controlsState" ref="controls" make-default />
|
<OrbitControls v-bind="controlsState" ref="controls" make-default />
|
||||||
|
<Suspense>
|
||||||
|
<ModelEnv />
|
||||||
|
</Suspense>
|
||||||
|
<Suspense>
|
||||||
<ModelDiagram />
|
<ModelDiagram />
|
||||||
</Scene>
|
</Suspense>
|
||||||
|
</TresCanvas>
|
||||||
</ClientOnly>
|
</ClientOnly>
|
||||||
<div class="canvas-icons">
|
<div class="canvas-icons">
|
||||||
<a href="#" @click.prevent="toggleExpState">
|
<a href="#" @click.prevent="toggleExpState">
|
||||||
|
@ -55,6 +77,7 @@ const changeDistance = (v = 1) => {
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Suspense>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -18,6 +18,7 @@ renderer.value.shadowMap.type = PCFSoftShadowMap
|
||||||
|
|
||||||
const pmremGenerator = new PMREMGenerator(renderer.value);
|
const pmremGenerator = new PMREMGenerator(renderer.value);
|
||||||
pmremGenerator.compileEquirectangularShader();
|
pmremGenerator.compileEquirectangularShader();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const loader = new GainMapLoader(renderer.value)
|
const loader = new GainMapLoader(renderer.value)
|
||||||
const result = await loader.loadAsync([
|
const result = await loader.loadAsync([
|
||||||
|
@ -35,7 +36,7 @@ onMounted(async () => {
|
||||||
scene.value.environmentIntensity = 1.25
|
scene.value.environmentIntensity = 1.25
|
||||||
scene.value.environmentRotation.z = 0.25
|
scene.value.environmentRotation.z = 0.25
|
||||||
result.renderTarget.texture.dispose();
|
result.renderTarget.texture.dispose();
|
||||||
})
|
|
||||||
onBeforeRender(() => {
|
onBeforeRender(() => {
|
||||||
if (camera.value) {
|
if (camera.value) {
|
||||||
const cameraDirection = new Vector3()
|
const cameraDirection = new Vector3()
|
||||||
|
@ -44,5 +45,6 @@ onBeforeRender(()=>{
|
||||||
scene.value.environmentRotation.z = angle + 0.25
|
scene.value.environmentRotation.z = angle + 0.25
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<template></template>
|
<template></template>
|
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
|
@ -51,9 +51,9 @@ export default defineNuxtConfig({
|
||||||
vite: {
|
vite: {
|
||||||
assetsInclude: ['**/*.glb', '**/*.gltf'],
|
assetsInclude: ['**/*.glb', '**/*.gltf'],
|
||||||
build: {
|
build: {
|
||||||
target: 'esnext'
|
target: 'esnext',
|
||||||
// minify: 'esbuild'
|
// minify: 'esbuild'
|
||||||
// minify: false
|
minify: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
robots: {
|
robots: {
|
||||||
|
|
|
@ -133,7 +133,9 @@ watch(openTab, () => {
|
||||||
</div>
|
</div>
|
||||||
<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">
|
||||||
|
<Suspense>
|
||||||
<ExpDiagram />
|
<ExpDiagram />
|
||||||
|
</Suspense>
|
||||||
</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="advantagesText"></span>
|
<span v-html="advantagesText"></span>
|
||||||
|
|
Loading…
Reference in New Issue