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: '/', component: HomeView },
|
||||||
{ path: '/experience',
|
{ path: '/experience',
|
||||||
component: ExperienceView,
|
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({
|
const router = createRouter({
|
||||||
|
|||||||
@@ -4,11 +4,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||||
import * as THREE from 'three'
|
import * as THREE from "three";
|
||||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
|
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
|
||||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
|
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.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({
|
const props = defineProps({
|
||||||
modelUrl: {
|
modelUrl: {
|
||||||
@@ -19,135 +23,179 @@ const props = defineProps({
|
|||||||
// Optional – falls du die Decoder-Dateien woanders hast
|
// Optional – falls du die Decoder-Dateien woanders hast
|
||||||
dracoPath: {
|
dracoPath: {
|
||||||
type: String,
|
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)
|
watch(
|
||||||
let scene, camera, renderer, controls, model
|
() => props.modelUrl,
|
||||||
|
(newUrl) => {
|
||||||
onMounted(() => {
|
if (newUrl) loadModel(newUrl);
|
||||||
init()
|
},
|
||||||
animate()
|
);
|
||||||
})
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
dispose()
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(() => props.modelUrl, (newUrl) => {
|
|
||||||
if (newUrl) loadModel(newUrl)
|
|
||||||
})
|
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
if (!containerRef.value) return
|
if (!containerRef.value) return;
|
||||||
|
|
||||||
// Scene
|
// Scene
|
||||||
scene = new THREE.Scene()
|
scene = new THREE.Scene();
|
||||||
scene.background = new THREE.Color(0x1a1a2e)
|
scene.background = new THREE.Color(0x1a1a2e);
|
||||||
|
|
||||||
// Camera
|
// Camera
|
||||||
camera = new THREE.PerspectiveCamera(
|
camera = new THREE.PerspectiveCamera(
|
||||||
50,
|
50,
|
||||||
containerRef.value.clientWidth / containerRef.value.clientHeight,
|
containerRef.value.clientWidth / containerRef.value.clientHeight,
|
||||||
0.1,
|
0.1,
|
||||||
1000
|
1000,
|
||||||
)
|
);
|
||||||
camera.position.set(0, 1.5, 4)
|
// camera.position.set(0, 1.5, 4);
|
||||||
|
|
||||||
// Renderer
|
// Renderer
|
||||||
renderer = new THREE.WebGLRenderer({ antialias: true })
|
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||||
renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight)
|
renderer.setSize(
|
||||||
renderer.setPixelRatio(window.devicePixelRatio)
|
containerRef.value.clientWidth,
|
||||||
renderer.outputColorSpace = THREE.SRGBColorSpace
|
containerRef.value.clientHeight,
|
||||||
containerRef.value.appendChild(renderer.domElement)
|
);
|
||||||
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||||||
|
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||||
|
containerRef.value.appendChild(renderer.domElement);
|
||||||
|
|
||||||
// Controls
|
// // Controls
|
||||||
controls = new OrbitControls(camera, renderer.domElement)
|
// controls = new OrbitControls(camera, renderer.domElement);
|
||||||
controls.enableDamping = true
|
// controls.enableDamping = true;
|
||||||
controls.dampingFactor = 0.05
|
// controls.dampingFactor = 0.05;
|
||||||
controls.minDistance = 1
|
// controls.minDistance = 1;
|
||||||
controls.maxDistance = 50
|
// controls.maxDistance = 50;
|
||||||
|
|
||||||
// Licht
|
// Licht
|
||||||
const ambient = new THREE.AmbientLight(0xffffff, 0.8)
|
const ambient = new THREE.AmbientLight(0xffffff, 0.8);
|
||||||
scene.add(ambient)
|
scene.add(ambient);
|
||||||
|
|
||||||
const directional = new THREE.DirectionalLight(0xffffff, 1.2)
|
const directional = new THREE.DirectionalLight(0xffffff, 1.2);
|
||||||
directional.position.set(5, 8, 4)
|
directional.position.set(5, 8, 4);
|
||||||
scene.add(directional)
|
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) {
|
function loadModel(url) {
|
||||||
if (model) {
|
if (model) {
|
||||||
scene.remove(model)
|
scene.remove(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dracoLoader = new DRACOLoader()
|
const dracoLoader = new DRACOLoader();
|
||||||
dracoLoader.setDecoderPath(props.dracoPath)
|
dracoLoader.setDecoderPath(props.dracoPath);
|
||||||
|
|
||||||
const loader = new GLTFLoader()
|
const loader = new GLTFLoader();
|
||||||
loader.setDRACOLoader(dracoLoader)
|
loader.setDRACOLoader(dracoLoader);
|
||||||
|
|
||||||
loader.load(
|
loader.load(
|
||||||
url,
|
url,
|
||||||
(gltf) => {
|
(gltf) => {
|
||||||
model = gltf.scene
|
model = gltf.scene;
|
||||||
|
|
||||||
const box = new THREE.Box3().setFromObject(model)
|
const box = new THREE.Box3().setFromObject(model);
|
||||||
const center = box.getCenter(new THREE.Vector3())
|
const center = box.getCenter(new THREE.Vector3());
|
||||||
const size = box.getSize(new THREE.Vector3())
|
const size = box.getSize(new THREE.Vector3());
|
||||||
|
|
||||||
model.position.sub(center)
|
model.position.sub(center);
|
||||||
const maxDim = Math.max(size.x, size.y, size.z)
|
|
||||||
const scale = 2 / maxDim
|
|
||||||
model.scale.multiplyScalar(scale)
|
|
||||||
|
|
||||||
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
|
// const maxDim = Math.max(size.x, size.y, size.z);
|
||||||
},
|
// const scale = 2 / maxDim;
|
||||||
(progress) => {
|
// model.scale.multiplyScalar(scale);
|
||||||
const percent = (progress.loaded / progress.total * 100).toFixed(1)
|
|
||||||
console.log(`Loading: ${percent}%`)
|
scene.add(model);
|
||||||
document.title = `${percent}%`
|
|
||||||
},
|
document.title = "Model geladen"; // Fertigmeldung im Tab
|
||||||
(error) => {
|
},
|
||||||
console.error('Fehler beim Laden des Modells:', error)
|
(progress) => {
|
||||||
document.title = 'Fehler beim Laden!'
|
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() {
|
function onWindowResize() {
|
||||||
if (!containerRef.value || !camera || !renderer) return
|
if (!containerRef.value || !camera || !renderer) return;
|
||||||
|
|
||||||
camera.aspect = containerRef.value.clientWidth / containerRef.value.clientHeight
|
camera.aspect =
|
||||||
camera.updateProjectionMatrix()
|
containerRef.value.clientWidth / containerRef.value.clientHeight;
|
||||||
renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight)
|
camera.updateProjectionMatrix();
|
||||||
|
renderer.setSize(
|
||||||
|
containerRef.value.clientWidth,
|
||||||
|
containerRef.value.clientHeight,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function animate() {
|
function animate() {
|
||||||
requestAnimationFrame(animate)
|
requestAnimationFrame(animate);
|
||||||
if (controls) controls.update()
|
camera.position.x = camState.x;
|
||||||
if (renderer && scene && camera) renderer.render(scene, camera)
|
camera.position.y = camState.y;
|
||||||
|
camera.position.z = camState.z;
|
||||||
|
// if (controls) controls.update();
|
||||||
|
if (renderer && scene && camera) renderer.render(scene, camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
function dispose() {
|
function dispose() {
|
||||||
window.removeEventListener('resize', onWindowResize)
|
window.removeEventListener("resize", onWindowResize);
|
||||||
|
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
renderer.dispose()
|
renderer.dispose();
|
||||||
containerRef.value?.removeChild(renderer.domElement)
|
containerRef.value?.removeChild(renderer.domElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controls) controls.dispose()
|
// if (controls) controls.dispose();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -158,4 +206,4 @@ function dispose() {
|
|||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
background: #0f0f1a;
|
background: #0f0f1a;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user