3d tv
This commit is contained in:
@@ -5,9 +5,10 @@ import gsap from "gsap";
|
|||||||
|
|
||||||
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 { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
||||||
|
|
||||||
const canvasRef = ref(null);
|
const canvasRef = ref(null);
|
||||||
let renderer, scene, camera, model;
|
let renderer, scene, camera, model, controls;
|
||||||
|
|
||||||
function initThree(canvas) {
|
function initThree(canvas) {
|
||||||
renderer = new THREE.WebGLRenderer({
|
renderer = new THREE.WebGLRenderer({
|
||||||
@@ -18,13 +19,20 @@ function initThree(canvas) {
|
|||||||
|
|
||||||
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||||
scene = new THREE.Scene();
|
scene = new THREE.Scene();
|
||||||
camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
|
camera = new THREE.PerspectiveCamera(45, 2, 0.1, 100);
|
||||||
camera.position.set(0, 0, 5);
|
camera.position.set(0, 0, 5);
|
||||||
|
|
||||||
const light = new THREE.DirectionalLight(0xffffff, 1);
|
const light = new THREE.DirectionalLight(0xffffff, 1);
|
||||||
light.position.set(2, 2, 5);
|
light.position.set(2, 2, 5);
|
||||||
scene.add(light);
|
scene.add(light);
|
||||||
|
|
||||||
|
controls = new OrbitControls(camera, renderer.domElement);
|
||||||
|
controls.enableDamping = true;
|
||||||
|
|
||||||
|
controls.minDistance = 5;
|
||||||
|
controls.maxDistance = 5;
|
||||||
|
controls.maxPolarAngle = Math.PI / 2;
|
||||||
|
|
||||||
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
|
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
|
||||||
|
|
||||||
loadModel();
|
loadModel();
|
||||||
@@ -53,6 +61,10 @@ function loadModel() {
|
|||||||
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());
|
||||||
|
|
||||||
|
controls.target.copy(center);
|
||||||
|
controls.update();
|
||||||
|
|
||||||
model.position.set(0, 0, 0);
|
model.position.set(0, 0, 0);
|
||||||
scene.add(model);
|
scene.add(model);
|
||||||
|
|
||||||
@@ -72,11 +84,14 @@ function loadModel() {
|
|||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
if (!renderer) return;
|
if (!renderer) return;
|
||||||
|
|
||||||
|
controls.update();
|
||||||
|
|
||||||
renderer.render(scene, camera);
|
renderer.render(scene, camera);
|
||||||
}
|
}
|
||||||
function onResize() {
|
function onResize() {
|
||||||
if (!renderer || !canvasRef.value) return;
|
if (!renderer || !canvasRef.value) return;
|
||||||
const r = canvasRef.getBoundingClientRect();
|
const r = canvasRef.value.getBoundingClientRect();
|
||||||
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
||||||
|
|
||||||
const width = canvasRef.value.clientWidth;
|
const width = canvasRef.value.clientWidth;
|
||||||
@@ -89,21 +104,26 @@ function onResize() {
|
|||||||
camera.updateProjectionMatrix();
|
camera.updateProjectionMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const resizeObserver = new ResizeObserver(onResize);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initThree(canvasRef.value);
|
initThree(canvasRef.value);
|
||||||
onResize();
|
onResize();
|
||||||
|
|
||||||
|
resizeObserver.observe(canvasRef.value);
|
||||||
window.addEventListener("resize", onResize);
|
window.addEventListener("resize", onResize);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
gsap.ticker.remove(render);
|
gsap.ticker.remove(render);
|
||||||
window.removeEventListener("resize", onResize);
|
window.removeEventListener("resize", onResize);
|
||||||
|
resizeObserver.disconnect();
|
||||||
renderer?.dispose();
|
renderer?.dispose();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="spacer">Scroll Down</div>
|
<div class="spacer"></div>
|
||||||
|
|
||||||
<canvas class="canvas" ref="canvasRef"></canvas>
|
<canvas class="canvas" ref="canvasRef"></canvas>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,7 +137,10 @@ onUnmounted(() => {
|
|||||||
place-items: center;
|
place-items: center;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.canvas {
|
.canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -48,45 +48,45 @@ const createTimeline = () => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
createTimeline();
|
createTimeline();
|
||||||
|
window.addEventListener("resize", createTimeline);
|
||||||
window.addEventListener("resize", createTween);
|
|
||||||
ScrollTrigger.refresh();
|
ScrollTrigger.refresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
flipCtx?.revert();
|
flipCtx?.revert();
|
||||||
window.removeEventListener("resize", createTween);
|
window.removeEventListener("resize", createTimeline);
|
||||||
gsap.ticker.remove(updateMorph);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("resize", createTimeline);
|
window.addEventListener("resize", createTimeline);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="main">
|
<section class="text-section">
|
||||||
<div class="container initial">
|
<div class="main">
|
||||||
<div class="circle"></div>
|
<div class="container initial">
|
||||||
</div>
|
<div class="circle"></div>
|
||||||
|
|
||||||
<div class="container second">
|
|
||||||
<div class="experience-text">
|
|
||||||
<h2>Do you want to see where I used to study?</h2>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="marker"></div>
|
|
||||||
<router-link to="/experience">
|
|
||||||
<button class="btn second-btn">Zur Experience</button>
|
|
||||||
</router-link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container third">
|
<div class="container second">
|
||||||
<div class="experience-text">
|
<div class="experience-text">
|
||||||
<h2>Here is the final step!</h2>
|
<h2>Do you want to see where I used to study?</h2>
|
||||||
|
</div>
|
||||||
|
<div class="marker"></div>
|
||||||
|
<router-link to="/experience">
|
||||||
|
<button class="btn second-btn">Zur Experience</button>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container third">
|
||||||
|
<div class="experience-text">
|
||||||
|
<h2>Here is the final step!</h2>
|
||||||
|
</div>
|
||||||
|
<div class="marker"></div>
|
||||||
|
<button class="btn third-btn">Next</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="marker"></div>
|
|
||||||
<button class="btn third-btn">Next</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="spacer final"></div>
|
||||||
<div class="spacer final"></div>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, onUnmounted } from "vue";
|
import { onMounted, onUnmounted, ref, nextTick } from "vue";
|
||||||
|
|
||||||
import MonitorModel from "/src/components/sections/MonitorModel.vue";
|
import MonitorModel from "/src/components/sections/MonitorModel.vue";
|
||||||
|
|
||||||
@@ -12,9 +12,11 @@ import Lenis from "lenis";
|
|||||||
|
|
||||||
gsap.registerPlugin(ScrollTrigger);
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
let ctx;
|
let ctx, lenis;
|
||||||
|
const childRef = ref(null);
|
||||||
|
const parallaxRef = ref(null);
|
||||||
|
|
||||||
function initParallax() {
|
function initParallax(triggerElement) {
|
||||||
const layers = [
|
const layers = [
|
||||||
{ layer: "1", yPercent: 70 },
|
{ layer: "1", yPercent: 70 },
|
||||||
{ layer: "2", yPercent: 50 },
|
{ layer: "2", yPercent: 50 },
|
||||||
@@ -22,8 +24,6 @@ function initParallax() {
|
|||||||
{ layer: "4", yPercent: 10 },
|
{ layer: "4", yPercent: 10 },
|
||||||
];
|
];
|
||||||
|
|
||||||
const triggerElement = document.querySelector(".parallax");
|
|
||||||
|
|
||||||
const tl = gsap.timeline({
|
const tl = gsap.timeline({
|
||||||
scrollTrigger: {
|
scrollTrigger: {
|
||||||
trigger: triggerElement,
|
trigger: triggerElement,
|
||||||
@@ -46,6 +46,17 @@ function initParallax() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initChild() {
|
||||||
|
gsap.to(childRef.value, {
|
||||||
|
scrollTrigger: {
|
||||||
|
trigger: childRef.value,
|
||||||
|
start: "top center",
|
||||||
|
},
|
||||||
|
scale,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function initLenis() {
|
function initLenis() {
|
||||||
lenis = new Lenis({
|
lenis = new Lenis({
|
||||||
duration: 1.1,
|
duration: 1.1,
|
||||||
@@ -61,20 +72,23 @@ function initLenis() {
|
|||||||
|
|
||||||
gsap.ticker.lagSmoothing(0);
|
gsap.ticker.lagSmoothing(0);
|
||||||
}
|
}
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
ctx = gsap.context(() => {
|
ctx = gsap.context(() => {
|
||||||
initParallax();
|
initParallax(parallaxRef.value);
|
||||||
|
// initChild();
|
||||||
initLenis();
|
initLenis();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
ctx?.revert();
|
ctx?.revert();
|
||||||
|
|
||||||
lenis?.destroy();
|
lenis?.destroy();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="parallax">
|
<div ref="parallaxRef" class="parallax">
|
||||||
<section class="parallax__header">
|
<section class="parallax__header">
|
||||||
<div class="parallax__visuals">
|
<div class="parallax__visuals">
|
||||||
<div class="parallax__layers">
|
<div class="parallax__layers">
|
||||||
@@ -91,7 +105,7 @@ onUnmounted(() => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div data-parallax-layer="3" class="parallax__layer-title">
|
<div data-parallax-layer="3" class="parallax__layer-title">
|
||||||
<h2 class="parallax__title">WELCOM</h2>
|
<h2 class="parallax__title">TOM HERPEL</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img data-parallax-layer="4" class="parallax__layer-img" />
|
<img data-parallax-layer="4" class="parallax__layer-img" />
|
||||||
@@ -102,7 +116,9 @@ onUnmounted(() => {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="parallax__content">
|
<section class="parallax__content">
|
||||||
<MonitorModel />
|
<div ref="childRef" class="model__wrapper">
|
||||||
|
<MonitorModel />
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -151,7 +167,7 @@ onUnmounted(() => {
|
|||||||
.parallax__content {
|
.parallax__content {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-height: 100svh;
|
min-height: 200svh;
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -249,6 +265,7 @@ onUnmounted(() => {
|
|||||||
top: -17.5%;
|
top: -17.5%;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "ArtDystopia";
|
font-family: "ArtDystopia";
|
||||||
src: url("/src/assets/fonts/Art_Dystopia.woff2") format("opentype");
|
src: url("/src/assets/fonts/Art_Dystopia.woff2") format("opentype");
|
||||||
|
|||||||
Reference in New Issue
Block a user