diff --git a/public/computer/OsWebseite.vue b/public/computer/OsWebseite.vue new file mode 100644 index 0000000..4a85786 --- /dev/null +++ b/public/computer/OsWebseite.vue @@ -0,0 +1,376 @@ + + + + + diff --git a/public/models/book_shelf-v1.glb b/public/models/book_shelf-v1.glb new file mode 100644 index 0000000..6a54fd1 Binary files /dev/null and b/public/models/book_shelf-v1.glb differ diff --git a/public/models/book_shelf_pc_draco-v1.glb b/public/models/book_shelf_pc_draco-v1.glb deleted file mode 100644 index 3aaea83..0000000 Binary files a/public/models/book_shelf_pc_draco-v1.glb and /dev/null differ diff --git a/src/components/big/MonitorModel.vue b/src/components/big/MonitorModel.vue index a76dbac..84006fb 100644 --- a/src/components/big/MonitorModel.vue +++ b/src/components/big/MonitorModel.vue @@ -111,8 +111,6 @@ function loadModel() { model.position.set(0, 0, 0); scene.add(model); gsapCtx = scrollCamreaGsap(center); - - document.title = "Model geladen"; }, (progress) => { const percent = ((progress.loaded / progress.total) * 100).toFixed(1); diff --git a/src/components/big/PcBooks.vue b/src/components/big/PcBooks.vue index ee8f85d..68c1216 100644 --- a/src/components/big/PcBooks.vue +++ b/src/components/big/PcBooks.vue @@ -3,8 +3,6 @@ import { onMounted, onUnmounted, ref } from "vue"; import * as THREE from "three"; import gsap from "gsap"; -import OsWebseite from "../computer/OsWebseite.vue"; - import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; @@ -38,6 +36,9 @@ let iframeObject; let computerMode = false; let cameraAnimation = null; +let iframeInteractionReady = false; +let iframePointerWasInside = false; +let iframeReadyTimer = null; const originalCameraPosition = new THREE.Vector3(); const originalControlsTarget = new THREE.Vector3(); @@ -75,14 +76,14 @@ function initThree(canvas, cssContainer) { cssContainer.appendChild(cssRenderer.domElement); - // scene.add(new THREE.AmbientLight(0xffffff, 0.02)); + // scene.add(new THREE.AmbientLight(0xffffff, 0.102)); loadModel(); gsap.ticker.add(render); } function loadModel() { - const modelUrl = "/models/book_shelf_pc_draco-v1.glb"; + const modelUrl = "/models/book_shelf-v1.glb"; const dracoLoader = new DRACOLoader(); dracoLoader.setDecoderPath("/draco/"); @@ -122,7 +123,7 @@ function loadModel() { // camera.lookAt(center); camera.position.set( center.x, - center.y + distance * 0.35, // etwas von oben + center.y + distance * 0.35, center.z + distance, ); @@ -147,8 +148,6 @@ function loadModel() { bulb.add(pointLight); } - - document.title = "Model geladen"; }, (progress) => { if (progress.total) { @@ -186,6 +185,9 @@ function enterComputerMode() { if (!screenMesh || computerMode) return; computerMode = true; + iframeInteractionReady = false; + iframePointerWasInside = false; + window.clearTimeout(iframeReadyTimer); originalCameraPosition.copy(camera.position); originalControlsTarget.copy(controls.target); @@ -198,7 +200,7 @@ function enterComputerMode() { const screenQuaternion = new THREE.Quaternion(); screenMesh.getWorldQuaternion(screenQuaternion); - const cameraOffset = new THREE.Vector3(0, 0.3, 2.9); + const cameraOffset = new THREE.Vector3(0, 2, 0); cameraOffset.applyQuaternion(screenQuaternion); @@ -216,10 +218,15 @@ function enterComputerMode() { iframe.focus(); try { - iframe.contentDocument?.querySelector("#password")?.focus(); + iframe.contentDocument?.querySelector("input")?.focus(); } catch (error) { console.warn("iframe konnte nicht fokussiert werden", error); } + + // Verhindert, dass ein Lade-/Layout-event sofort wieder herauszoomt. + iframeReadyTimer = window.setTimeout(() => { + iframeInteractionReady = true; + }, 300); }, }); @@ -247,16 +254,20 @@ function enterComputerMode() { 0, ); } + function leaveComputerMode() { if (!computerMode) return; computerMode = false; + iframeInteractionReady = false; + iframePointerWasInside = false; + window.clearTimeout(iframeReadyTimer); iframe.style.pointerEvents = "none"; iframe.blur(); - // Beim Herauszoomen wieder ausschließlich das GLTF-Display zeigen. - iframeObject.visible = false; + // Noch sichtbar lassen während der Kamera-Animation + iframeObject.visible = true; screenMesh.visible = true; cameraAnimation?.kill(); @@ -268,6 +279,9 @@ function leaveComputerMode() { onComplete() { controls.enabled = true; controls.update(); + + // Erst nach dem Herauszoomen ausblenden + iframeObject.visible = false; }, }); @@ -295,11 +309,13 @@ function leaveComputerMode() { 0, ); } + function onIframeMessage(event) { if (event.source !== iframe?.contentWindow) return; if (event.origin !== window.location.origin) return; if (event.data?.type === "leave-computer") { + if (!computerMode || !iframeInteractionReady) return; leaveComputerMode(); } } @@ -336,8 +352,8 @@ function onResize() { } function createComputerScreen(mesh) { - const iframeWidth = 1280; - const iframeHeight = 720; + const iframeWidth = 1480; + const iframeHeight = 920; const container = document.createElement("div"); @@ -347,10 +363,8 @@ function createComputerScreen(mesh) { container.style.overflow = "hidden"; iframe = document.createElement("iframe"); - const parentOrigin = JSON.stringify(window.location.origin); - // Eigenständige Oberfläche: keine zusätzliche Vue-Route/HTML-Datei nötig. - iframe.src = "/computer"; + iframe.src = `${window.location.origin}/motion/computer`; iframe.title = "Computer"; iframe.tabIndex = 0; @@ -361,15 +375,24 @@ function createComputerScreen(mesh) { container.appendChild(iframe); - // Zusätzlicher Fallback am iframe-Element selbst. - iframe.addEventListener("mouseleave", () => { - if (computerMode) leaveComputerMode(); + // Erst ein echtes Betreten und anschließendes Verlassen schließt den Modus. + iframe.addEventListener("pointerenter", () => { + if (!computerMode) return; + iframePointerWasInside = true; + }); + + iframe.addEventListener("pointerleave", () => { + if (!computerMode || !iframeInteractionReady || !iframePointerWasInside) { + return; + } + + leaveComputerMode(); }); iframe.addEventListener("load", () => { try { iframe.contentWindow.addEventListener("keydown", (event) => { - if (event.key === "Escape") { + if (event.key === "Escape" && computerMode && iframeInteractionReady) { leaveComputerMode(); } }); @@ -386,7 +409,6 @@ function createComputerScreen(mesh) { updateIframeTransform(mesh, iframeWidth, iframeHeight); } - function updateIframeTransform(mesh, iframeWidth, iframeHeight) { mesh.updateWorldMatrix(true, false); @@ -406,19 +428,34 @@ function updateIframeTransform(mesh, iframeWidth, iframeHeight) { iframeObject.position.copy(worldPosition); iframeObject.quaternion.copy(worldQuaternion); + /* + * Korrektur für das neue Screen-Mesh. + * Bei falscher Richtung -90 durch +90 ersetzen. + */ + iframeObject.rotateX(THREE.MathUtils.degToRad(-90)); + iframeObject.rotateZ(THREE.MathUtils.degToRad(-90)); + iframeObject.translateX(-0.04); + iframeObject.translateY(+0.013); + iframeObject.translateZ(-0.1); + + const screenWidth = Math.abs(meshSize.x * worldScale.x) + 0.03; + + const screenHeight = Math.abs(meshSize.y * worldScale.y) + 0.61; + iframeObject.scale.set( - (meshSize.x * worldScale.x + 0.03) / iframeWidth, - (meshSize.y * worldScale.y + 0.03) / iframeHeight, + screenWidth / iframeWidth, + screenHeight / iframeHeight, 1, ); - // Etwas vor den eigentlichen Bildschirm setzen, - // damit es nicht genau auf derselben Ebene liegt. - const normal = new THREE.Vector3(0, 0, -1.4) - .applyQuaternion(worldQuaternion) + /* + * Vorderseite nach der Korrekturrotation berechnen. + */ + const iframeNormal = new THREE.Vector3(0, 0, 1) + .applyQuaternion(iframeObject.quaternion) .normalize(); - iframeObject.position.addScaledVector(normal, -0.2); + iframeObject.position.addScaledVector(iframeNormal, 0.1); } let resizeObserver; @@ -460,6 +497,7 @@ onUnmounted(() => { resizeObserver?.disconnect(); cameraAnimation?.kill(); + window.clearTimeout(iframeReadyTimer); controls?.dispose(); renderer?.dispose(); diff --git a/src/components/computer/OsWebseite.vue b/src/components/computer/OsWebseite.vue index 4a85786..a4fa46d 100644 --- a/src/components/computer/OsWebseite.vue +++ b/src/components/computer/OsWebseite.vue @@ -1,10 +1,23 @@ diff --git a/src/router/index.ts b/src/router/index.ts index 1f4993e..d978359 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -15,7 +15,7 @@ const routes: RouteRecordRaw[] = [ {path: '/motion/me', component: BigMeImg}, {path: '/books', component: BooksShow}, { - path: "/computer", component: OsWebseite}, + path: "/motion/computer", component: OsWebseite}, // { path: '/ducks', // component: DucksInfo,