starting gspa with three js
This commit is contained in:
Binary file not shown.
BIN
public/models/door_room_firstVersion_materials-draco.glb
Normal file
BIN
public/models/door_room_firstVersion_materials-draco.glb
Normal file
Binary file not shown.
@@ -7,7 +7,7 @@ const routes: RouteRecordRaw[] = [
|
||||
{ path: '/', component: HomeView },
|
||||
{ path: '/experience',
|
||||
component: ExperienceView,
|
||||
props: { modelUrl: '/models/LuebeckRoom_materials-draco.glb', dracoPath: '/draco/' } }
|
||||
props: { modelUrl: '/models/door_room_firstVersion_materials-draco.glb', dracoPath: '/draco/' } }
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import * as THREE from 'three'
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
||||
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
|
||||
import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import * as THREE from "three";
|
||||
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
||||
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
|
||||
|
||||
import gsap from "gsap";
|
||||
import ScrollTrigger from "gsap/ScrollTrigger";
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
const props = defineProps({
|
||||
modelUrl: {
|
||||
@@ -19,135 +23,179 @@ const props = defineProps({
|
||||
// Optional – falls du die Decoder-Dateien woanders hast
|
||||
dracoPath: {
|
||||
type: String,
|
||||
default: '/draco/' // ← WICHTIG: Passe den Pfad an!
|
||||
}
|
||||
})
|
||||
default: "/draco/", // ← WICHTIG: Passe den Pfad an!
|
||||
},
|
||||
});
|
||||
|
||||
const containerRef = ref(null);
|
||||
let ctx;
|
||||
let scene, camera, renderer, controls, model;
|
||||
|
||||
const camState = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 10,
|
||||
};
|
||||
|
||||
const containerRef = ref(null)
|
||||
let scene, camera, renderer, controls, model
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
animate()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
dispose()
|
||||
})
|
||||
|
||||
watch(() => props.modelUrl, (newUrl) => {
|
||||
if (newUrl) loadModel(newUrl)
|
||||
})
|
||||
watch(
|
||||
() => props.modelUrl,
|
||||
(newUrl) => {
|
||||
if (newUrl) loadModel(newUrl);
|
||||
},
|
||||
);
|
||||
|
||||
function init() {
|
||||
if (!containerRef.value) return
|
||||
if (!containerRef.value) return;
|
||||
|
||||
// Scene
|
||||
scene = new THREE.Scene()
|
||||
scene.background = new THREE.Color(0x1a1a2e)
|
||||
scene = new THREE.Scene();
|
||||
scene.background = new THREE.Color(0x1a1a2e);
|
||||
|
||||
// Camera
|
||||
camera = new THREE.PerspectiveCamera(
|
||||
50,
|
||||
containerRef.value.clientWidth / containerRef.value.clientHeight,
|
||||
0.1,
|
||||
1000
|
||||
)
|
||||
camera.position.set(0, 1.5, 4)
|
||||
1000,
|
||||
);
|
||||
// camera.position.set(0, 1.5, 4);
|
||||
|
||||
// Renderer
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true })
|
||||
renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight)
|
||||
renderer.setPixelRatio(window.devicePixelRatio)
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace
|
||||
containerRef.value.appendChild(renderer.domElement)
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
renderer.setSize(
|
||||
containerRef.value.clientWidth,
|
||||
containerRef.value.clientHeight,
|
||||
);
|
||||
renderer.setPixelRatio(window.devicePixelRatio);
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||
containerRef.value.appendChild(renderer.domElement);
|
||||
|
||||
// Controls
|
||||
controls = new OrbitControls(camera, renderer.domElement)
|
||||
controls.enableDamping = true
|
||||
controls.dampingFactor = 0.05
|
||||
controls.minDistance = 1
|
||||
controls.maxDistance = 50
|
||||
// // Controls
|
||||
// controls = new OrbitControls(camera, renderer.domElement);
|
||||
// controls.enableDamping = true;
|
||||
// controls.dampingFactor = 0.05;
|
||||
// controls.minDistance = 1;
|
||||
// controls.maxDistance = 50;
|
||||
|
||||
// Licht
|
||||
const ambient = new THREE.AmbientLight(0xffffff, 0.8)
|
||||
scene.add(ambient)
|
||||
const ambient = new THREE.AmbientLight(0xffffff, 0.8);
|
||||
scene.add(ambient);
|
||||
|
||||
const directional = new THREE.DirectionalLight(0xffffff, 1.2)
|
||||
directional.position.set(5, 8, 4)
|
||||
scene.add(directional)
|
||||
const directional = new THREE.DirectionalLight(0xffffff, 1.2);
|
||||
directional.position.set(5, 8, 4);
|
||||
scene.add(directional);
|
||||
|
||||
window.addEventListener('resize', onWindowResize)
|
||||
window.addEventListener("resize", onWindowResize);
|
||||
|
||||
loadModel(props.modelUrl)
|
||||
loadModel(props.modelUrl);
|
||||
}
|
||||
function setupScroll() {
|
||||
if (!camera) return;
|
||||
ScrollTrigger.refresh();
|
||||
|
||||
gsap.to(camState, {
|
||||
z: 2, // Kamera fährt rein
|
||||
ease: "none",
|
||||
scrollTrigger: {
|
||||
trigger: document.body,
|
||||
start: "top top",
|
||||
end: "+=2000",
|
||||
scrub: true,
|
||||
invalidateOnRefresh: true,
|
||||
// markers: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
onMounted(() => {
|
||||
init();
|
||||
animate();
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
setupScroll();
|
||||
});
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
dispose();
|
||||
});
|
||||
|
||||
function loadModel(url) {
|
||||
if (model) {
|
||||
scene.remove(model)
|
||||
scene.remove(model);
|
||||
}
|
||||
|
||||
const dracoLoader = new DRACOLoader()
|
||||
dracoLoader.setDecoderPath(props.dracoPath)
|
||||
const dracoLoader = new DRACOLoader();
|
||||
dracoLoader.setDecoderPath(props.dracoPath);
|
||||
|
||||
const loader = new GLTFLoader()
|
||||
loader.setDRACOLoader(dracoLoader)
|
||||
const loader = new GLTFLoader();
|
||||
loader.setDRACOLoader(dracoLoader);
|
||||
|
||||
loader.load(
|
||||
url,
|
||||
(gltf) => {
|
||||
model = gltf.scene
|
||||
url,
|
||||
(gltf) => {
|
||||
model = gltf.scene;
|
||||
|
||||
const box = new THREE.Box3().setFromObject(model)
|
||||
const center = box.getCenter(new THREE.Vector3())
|
||||
const size = box.getSize(new THREE.Vector3())
|
||||
const box = new THREE.Box3().setFromObject(model);
|
||||
const center = box.getCenter(new THREE.Vector3());
|
||||
const size = box.getSize(new THREE.Vector3());
|
||||
|
||||
model.position.sub(center)
|
||||
const maxDim = Math.max(size.x, size.y, size.z)
|
||||
const scale = 2 / maxDim
|
||||
model.scale.multiplyScalar(scale)
|
||||
model.position.sub(center);
|
||||
|
||||
scene.add(model)
|
||||
camera.position.set(0, 0, 10);
|
||||
camera.near = 0.01;
|
||||
camera.far = 1000;
|
||||
camera.updateProjectionMatrix();
|
||||
|
||||
document.title = 'Model geladen' // Fertigmeldung im Tab
|
||||
},
|
||||
(progress) => {
|
||||
const percent = (progress.loaded / progress.total * 100).toFixed(1)
|
||||
console.log(`Loading: ${percent}%`)
|
||||
document.title = `${percent}%`
|
||||
},
|
||||
(error) => {
|
||||
console.error('Fehler beim Laden des Modells:', error)
|
||||
document.title = 'Fehler beim Laden!'
|
||||
}
|
||||
)
|
||||
// const maxDim = Math.max(size.x, size.y, size.z);
|
||||
// const scale = 2 / maxDim;
|
||||
// model.scale.multiplyScalar(scale);
|
||||
|
||||
scene.add(model);
|
||||
|
||||
document.title = "Model geladen"; // Fertigmeldung im Tab
|
||||
},
|
||||
(progress) => {
|
||||
const percent = ((progress.loaded / progress.total) * 100).toFixed(1);
|
||||
console.log(`Loading: ${percent}%`);
|
||||
document.title = `${percent}%`;
|
||||
},
|
||||
(error) => {
|
||||
console.error("Fehler beim Laden des Modells:", error);
|
||||
document.title = "Fehler beim Laden!";
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function onWindowResize() {
|
||||
if (!containerRef.value || !camera || !renderer) return
|
||||
if (!containerRef.value || !camera || !renderer) return;
|
||||
|
||||
camera.aspect = containerRef.value.clientWidth / containerRef.value.clientHeight
|
||||
camera.updateProjectionMatrix()
|
||||
renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight)
|
||||
camera.aspect =
|
||||
containerRef.value.clientWidth / containerRef.value.clientHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(
|
||||
containerRef.value.clientWidth,
|
||||
containerRef.value.clientHeight,
|
||||
);
|
||||
}
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame(animate)
|
||||
if (controls) controls.update()
|
||||
if (renderer && scene && camera) renderer.render(scene, camera)
|
||||
requestAnimationFrame(animate);
|
||||
camera.position.x = camState.x;
|
||||
camera.position.y = camState.y;
|
||||
camera.position.z = camState.z;
|
||||
// if (controls) controls.update();
|
||||
if (renderer && scene && camera) renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
function dispose() {
|
||||
window.removeEventListener('resize', onWindowResize)
|
||||
window.removeEventListener("resize", onWindowResize);
|
||||
|
||||
if (renderer) {
|
||||
renderer.dispose()
|
||||
containerRef.value?.removeChild(renderer.domElement)
|
||||
renderer.dispose();
|
||||
containerRef.value?.removeChild(renderer.domElement);
|
||||
}
|
||||
|
||||
if (controls) controls.dispose()
|
||||
// if (controls) controls.dispose();
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -158,4 +206,4 @@ function dispose() {
|
||||
min-height: 400px;
|
||||
background: #0f0f1a;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user