Compare commits
6 Commits
8daec5666e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4978670fc4 | ||
|
|
0c91356320 | ||
|
|
c3075328a3 | ||
|
|
6e69ae9cfe | ||
|
|
c861603b06 | ||
|
|
c032578bc6 |
3
.vscode/extensions.json
vendored
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": ["Vue.volar"]
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>th</title>
|
<title>th</title>
|
||||||
|
|||||||
BIN
public/models/room-draco.glb
Normal file
BIN
public/vids/Aus_dem_wasser.mp4
Normal file
BIN
public/vids/TH-Video_Abgabe.mp4
Executable file
BIN
public/vids/googleOR.mp4
Normal file
|
Before Width: | Height: | Size: 8.4 MiB After Width: | Height: | Size: 7.9 MiB |
BIN
public/vids/skitom.mp4
Executable file
|
Before Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 14 MiB |
|
Before Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 273 KiB |
|
Before Width: | Height: | Size: 180 KiB |
|
Before Width: | Height: | Size: 380 KiB |
BIN
src/assets/fonts/IBMPlexMono-Medium.woff2
Normal file
@@ -101,78 +101,59 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<h2>Here is some content</h2>
|
<h2>Was ist Kunst?</h2>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
Nach Schelling ist die Kunst das Erzeugnis oder die Folge jener
|
||||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
Weltanschauung, nach der das Subjekt sich in sein Objekt verwandelt oder
|
||||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
das Objekt selbst 45 zum Subjekt wird. Die Schönheit ist die Darstellung
|
||||||
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
des Unendli- chen im Endlichen. Und der Grundcharakter eines Kunstwerkes
|
||||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
|
ist die bewußtlose Unendlichkeit. Die Kunst ist die Vereinigung des
|
||||||
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
|
Subjektiven mit dem Objektiven, – der Natur mit der Vernunft, der
|
||||||
mollit anim id est laborum.
|
Bewußtlosigkeit mit dem Bewußten. Und deshalb ist die Kunst das höchste
|
||||||
|
Mittel der Erkenntnis. Die Schönheit ist die Betrachtung der Dinge an
|
||||||
|
sich, wie sie in Urbildern sind. Das Schöne erzeugt nicht der Künstler
|
||||||
|
vermöge seines Wissens oder seines Willens, sondern die Idee der
|
||||||
|
Schönheit selbst schafft in ihm. (Schaßler)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
In der Welt erblicken wir nur die Entartung der Grundidee, die Kunst
|
||||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
jedoch kann ver- möge der Phantasie sich bis zur Höhe der Grundidee
|
||||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
erheben. Und deshalb ist die Kunst das Ebenbild der Schöpfung
|
||||||
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
(Schaßler).
|
||||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
|
|
||||||
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
|
|
||||||
mollit anim id est laborum.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
Nach einem anderen Anhänger Schellings, Krause (1781–1832), ist die
|
||||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
wahre reale Schönheit die Erzeugung der Idee in der indivi- duellen
|
||||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
Form; die Kunst ist aber die Verwirklichung der Schönheit in der Sphäre
|
||||||
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
des freien menschlichen Geistes. Die höchste Stufe der Kunst ist die
|
||||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
|
Kunst des Lebens, die ihre Thätigkeit auf die Verschö- nerung des Lebens
|
||||||
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
|
richtet, damit dies ein schön belebter Wohnort für die selber in
|
||||||
mollit anim id est laborum.
|
Schönheit vollendete Menschheit sei. (Ebd.)
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
Nach Hegel (1770–1831) offenbart sich Gott in Natur und Kunst unter der
|
||||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
Form der Schönheit. Gott äußert sich auf zweierlei Weise: in dem Objekt
|
||||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
und in dem Subjekt, – in der Natur und im Geiste. Die Schönheit ist
|
||||||
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
jedoch das Durch- scheinen der Idee durch den Stoff. Das wahrhaft Schöne
|
||||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
|
ist nur der Geist und alles das, was dem Geiste teilhaftig ist. Deshalb
|
||||||
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
|
ist die Schönheit der Natur nur der Schein der dem Geiste eigenen Schön-
|
||||||
mollit anim id est laborum.
|
heit: das Schöne hat nur als Geistiges wahrhaften Gehalt. Aber das
|
||||||
|
Geistige muß in der sinnlichen Form erscheinen. Die sinnliche Äu- ßerung
|
||||||
|
des Geistes jedoch ist nur der Schein. Und dieser Schein ist die
|
||||||
|
eigentliche Wirklichkeit des Schönen. Sodaß die Kunst die 46
|
||||||
|
Verwirklichung dieses Scheines der Idee und zugleich mit der Reli- gion
|
||||||
|
und Philosophie das Mittel ist, die tiefsten Interessen der Men- schen
|
||||||
|
und die umfassendsten Wahrheiten des Geistes zum Bewußt- sein zu bringen
|
||||||
|
und auszusprechen. Wahrheit und Schönheit sind nach Hegel ein und
|
||||||
|
dasselbe: der Unterschied besteht nur darin, daß die Wahrheit die Idee
|
||||||
|
ist, wie diese an sich und dem allgemeinen Prinzip nach ist und als
|
||||||
|
solches gedacht wird. Die Idee jedoch, die äußerlich existiert, wird für
|
||||||
|
das Bewußtsein nicht blos wahr, son- dern auch schön. Das Schöne ist das
|
||||||
|
Scheinen der Idee
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
--- Was ist Kunst?, Leo N. Tolstoi, Aus dem Russischen von Michail
|
||||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
Feofanov (1902) ---
|
||||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
|
||||||
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
|
||||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
|
|
||||||
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
|
|
||||||
mollit anim id est laborum.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
|
||||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
|
||||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
|
||||||
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
|
||||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
|
|
||||||
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
|
|
||||||
mollit anim id est laborum.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
|
||||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
|
||||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
|
||||||
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
|
||||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
|
|
||||||
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
|
|
||||||
mollit anim id est laborum.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
|
||||||
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
|
|
||||||
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
|
|
||||||
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
|
|
||||||
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
|
|
||||||
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
|
|
||||||
mollit anim id est laborum.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -261,6 +242,7 @@ p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
|
font-family: Courier New;
|
||||||
padding: 2rem 5rem;
|
padding: 2rem 5rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
196
src/components/sections/IntroductionSection.vue
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
<script setup lang="js">
|
||||||
|
import { onMounted, onUnmounted, ref } from "vue";
|
||||||
|
import gsap from "gsap";
|
||||||
|
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||||
|
import SplitText from "gsap/SplitText";
|
||||||
|
|
||||||
|
gsap.registerPlugin(ScrollTrigger, SplitText);
|
||||||
|
|
||||||
|
const section = ref(null);
|
||||||
|
|
||||||
|
let ctx;
|
||||||
|
let split;
|
||||||
|
let animation;
|
||||||
|
const date = ref("--:--");
|
||||||
|
const time = ref("");
|
||||||
|
let interval;
|
||||||
|
|
||||||
|
function createTween() {
|
||||||
|
animation?.kill();
|
||||||
|
split = SplitText.create(".text", {
|
||||||
|
type: "words",
|
||||||
|
});
|
||||||
|
|
||||||
|
animation = gsap.from(split.words, {
|
||||||
|
y: 30,
|
||||||
|
opacity: 0,
|
||||||
|
duration: 0.9,
|
||||||
|
ease: "power2.out",
|
||||||
|
stagger: 0.05,
|
||||||
|
|
||||||
|
scrollTrigger: {
|
||||||
|
trigger: section.value,
|
||||||
|
start: "top 10%",
|
||||||
|
toggleActions: "play none none none",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
ctx = gsap.context(() => {
|
||||||
|
createTween();
|
||||||
|
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
}, section.value);
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
ScrollTrigger.refresh();
|
||||||
|
});
|
||||||
|
getTimeDate();
|
||||||
|
interval = setInterval(() => {
|
||||||
|
getTimeDate();
|
||||||
|
}, 1000);
|
||||||
|
console.log(date.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleResize() {
|
||||||
|
split && split.revert();
|
||||||
|
split = SplitText.create(".text", {
|
||||||
|
type: "words",
|
||||||
|
});
|
||||||
|
ScrollTrigger.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener("resize", handleResize);
|
||||||
|
ctx?.revert();
|
||||||
|
clearInterval(interval);
|
||||||
|
});
|
||||||
|
|
||||||
|
function getTimeDate() {
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
time.value = now.toLocaleTimeString("de-DE", {
|
||||||
|
timeZone: "Europe/Berlin",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
});
|
||||||
|
|
||||||
|
date.value = now.toLocaleDateString("de-DE", {
|
||||||
|
timeZone: "Europe/Berlin",
|
||||||
|
weekday: "long",
|
||||||
|
day: "2-digit",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="poster">
|
||||||
|
<div class="b-one"></div>
|
||||||
|
<div class="b-two"></div>
|
||||||
|
<div class="b-three"></div>
|
||||||
|
<div class="b-four"></div>
|
||||||
|
<div class="b-five"></div>
|
||||||
|
|
||||||
|
<div class="textblock text">
|
||||||
|
<h4 class="textblock__subheader text">Tom Alexander Herpel</h4>
|
||||||
|
|
||||||
|
<h1 class="textblock__header text">
|
||||||
|
Welcome to my page. <br />
|
||||||
|
This space is currently under construction.
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h4 class="textblock__subheader text">
|
||||||
|
Here you’ll find creative projects and ideas that inspire me at the
|
||||||
|
moment.
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<p class="textblock__caption">Oldenburg, Germany <br /></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.poster {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(33, 1fr);
|
||||||
|
grid-template-rows: repeat(16, 1fr);
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
background: var(--bg-clr);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Schwarze Blöcke */
|
||||||
|
.b-one,
|
||||||
|
.b-two,
|
||||||
|
.b-three,
|
||||||
|
.b-four,
|
||||||
|
.b-five {
|
||||||
|
background: var(--black-clr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.b-one {
|
||||||
|
grid-column: 1 / 4;
|
||||||
|
grid-row: 11 / 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.b-two {
|
||||||
|
grid-column: 4 / 12;
|
||||||
|
grid-row: 6 / 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.b-three {
|
||||||
|
grid-column: 16 / 18;
|
||||||
|
grid-row: 8 / 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
.b-four {
|
||||||
|
grid-column: 11 / 30;
|
||||||
|
grid-row: 15 / 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
.b-five {
|
||||||
|
grid-column: 30 / 34;
|
||||||
|
grid-row: 1 / 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Textbereich */
|
||||||
|
.textblock {
|
||||||
|
grid-column: 6 / 28;
|
||||||
|
grid-row-end: 14;
|
||||||
|
|
||||||
|
align-self: center;
|
||||||
|
color: var(--black-clr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.textblock__subheader {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textblock__header {
|
||||||
|
font-size: 4rem;
|
||||||
|
line-height: 1;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
max-width: 900px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textblock__caption {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
font-family: Courier New;
|
||||||
|
font-size: clamp(1.5rem, 3vw, 5rem);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
252
src/components/sections/MonitorModel.vue
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
<script setup lang="js">
|
||||||
|
import { onMounted, onUnmounted, ref } from "vue";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import gsap from "gsap";
|
||||||
|
import ScrollTrigger from "gsap/ScrollTrigger";
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
import surf_video from "/vids/Aus_dem_wasser.mp4";
|
||||||
|
import ski_video from "/vids/ski.mp4";
|
||||||
|
|
||||||
|
const canvasRef = ref(null);
|
||||||
|
let renderer, scene, camera, model, controls, gsapCtx;
|
||||||
|
let screen1, screen3, screen4;
|
||||||
|
|
||||||
|
const video_surf = document.createElement("video");
|
||||||
|
const video_ski = document.createElement("video");
|
||||||
|
|
||||||
|
video_surf.src = surf_video;
|
||||||
|
video_surf.loop = true;
|
||||||
|
video_surf.muted = true;
|
||||||
|
|
||||||
|
video_ski.src = ski_video;
|
||||||
|
video_ski.loop = true;
|
||||||
|
video_ski.muted = true;
|
||||||
|
|
||||||
|
function initThree(canvas) {
|
||||||
|
renderer = new THREE.WebGLRenderer({
|
||||||
|
canvas,
|
||||||
|
antialias: true,
|
||||||
|
alpha: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
renderer.setPixelRatio(1); //TODO
|
||||||
|
|
||||||
|
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||||
|
scene = new THREE.Scene();
|
||||||
|
camera = new THREE.PerspectiveCamera(45, 2, 0.1, 100);
|
||||||
|
|
||||||
|
const light = new THREE.DirectionalLight(0xffe0b0, 0.9);
|
||||||
|
light.position.set(-1, 4, 5);
|
||||||
|
scene.add(light);
|
||||||
|
|
||||||
|
controls = new OrbitControls(camera, renderer.domElement);
|
||||||
|
controls.enableDamping = true;
|
||||||
|
controls.enabled = false;
|
||||||
|
|
||||||
|
// controls.minDistance = 5;
|
||||||
|
// controls.maxDistance = 5;
|
||||||
|
controls.maxPolarAngle = Math.PI / 2;
|
||||||
|
controls.minAzimuthAngle = -Math.PI / 2;
|
||||||
|
controls.maxAzimuthAngle = Math.PI / 2;
|
||||||
|
|
||||||
|
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
|
||||||
|
|
||||||
|
loadModel();
|
||||||
|
|
||||||
|
gsap.ticker.add(render);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadModel() {
|
||||||
|
if (model) {
|
||||||
|
scene.remove(model);
|
||||||
|
}
|
||||||
|
let modelUrl = "/models/room-draco.glb";
|
||||||
|
let dracoPath = "/draco/";
|
||||||
|
|
||||||
|
const dracoLoader = new DRACOLoader();
|
||||||
|
dracoLoader.setDecoderPath(dracoPath);
|
||||||
|
|
||||||
|
const loader = new GLTFLoader();
|
||||||
|
loader.setDRACOLoader(dracoLoader);
|
||||||
|
|
||||||
|
loader.load(
|
||||||
|
modelUrl,
|
||||||
|
(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 texture_surf_video = new THREE.VideoTexture(video_surf);
|
||||||
|
const texure_ski = new THREE.VideoTexture(video_ski);
|
||||||
|
screen1 = model.getObjectByName("fernseher1Bild");
|
||||||
|
screen4 = model.getObjectByName("fernseher4Bild");
|
||||||
|
screen3 = model.getObjectByName("fernseher3Bild");
|
||||||
|
|
||||||
|
texture_surf_video.flipY = false;
|
||||||
|
texure_ski.flipY = false;
|
||||||
|
texure_ski.offset.set(-0.3, -0.2);
|
||||||
|
|
||||||
|
screen1.material = new THREE.MeshBasicMaterial({
|
||||||
|
map: texture_surf_video,
|
||||||
|
});
|
||||||
|
screen4.material = new THREE.MeshBasicMaterial({
|
||||||
|
map: texure_ski,
|
||||||
|
});
|
||||||
|
screen3.material = new THREE.MeshBasicMaterial({
|
||||||
|
map: texure_ski,
|
||||||
|
});
|
||||||
|
const maxSize = Math.max(size.x, size.y, size.z);
|
||||||
|
const distance = maxSize * 2;
|
||||||
|
|
||||||
|
camera.position.set(center.x, center.y, center.z + distance);
|
||||||
|
controls.target.copy(center);
|
||||||
|
controls.update();
|
||||||
|
|
||||||
|
console.log(scene);
|
||||||
|
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);
|
||||||
|
console.log(`Loading: ${percent}%`);
|
||||||
|
document.title = `${percent}%`;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error("Fehler beim Laden des Modells:", error);
|
||||||
|
document.title = "Fehler beim Laden!";
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
if (!renderer) return;
|
||||||
|
|
||||||
|
controls.update();
|
||||||
|
|
||||||
|
renderer.render(scene, camera);
|
||||||
|
}
|
||||||
|
function onResize() {
|
||||||
|
if (!renderer || !canvasRef.value) return;
|
||||||
|
const r = canvasRef.value.getBoundingClientRect();
|
||||||
|
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
||||||
|
|
||||||
|
const width = canvasRef.value.clientWidth;
|
||||||
|
const height = canvasRef.value.clientHeight;
|
||||||
|
|
||||||
|
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
||||||
|
renderer.setSize(width, height, false);
|
||||||
|
|
||||||
|
camera.aspect = (r.width || 1) / (r.height || 1);
|
||||||
|
camera.updateProjectionMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollCamreaGsap(center) {
|
||||||
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
|
const ctx = gsap.context(() => {
|
||||||
|
gsap.to(camera.position, {
|
||||||
|
z: center.z + 5,
|
||||||
|
ease: "none",
|
||||||
|
scrollTrigger: {
|
||||||
|
trigger: canvasRef.value,
|
||||||
|
start: "top top",
|
||||||
|
end: "+=2000",
|
||||||
|
scrub: 1,
|
||||||
|
pin: true,
|
||||||
|
anticipatePin: 1,
|
||||||
|
onUpdate: (self) => {
|
||||||
|
if (self.progress >= 0.8) {
|
||||||
|
controls.enabled = true;
|
||||||
|
|
||||||
|
controls.enableRotate = true;
|
||||||
|
controls.enableZoom = false;
|
||||||
|
controls.enablePan = false;
|
||||||
|
|
||||||
|
screen1.visible = true;
|
||||||
|
screen3.visible = true;
|
||||||
|
screen4.visible = true;
|
||||||
|
setVideoActive(true);
|
||||||
|
} else {
|
||||||
|
controls.enabled = false;
|
||||||
|
screen1.visible = false;
|
||||||
|
screen3.visible = false;
|
||||||
|
screen4.visible = false;
|
||||||
|
setVideoActive(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}, canvasRef.value);
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setVideoActive(active) {
|
||||||
|
if (active) {
|
||||||
|
video_surf.play();
|
||||||
|
video_ski.play();
|
||||||
|
} else {
|
||||||
|
video_surf.pause();
|
||||||
|
video_ski.pause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const resizeObserver = new ResizeObserver(onResize);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initThree(canvasRef.value);
|
||||||
|
onResize();
|
||||||
|
|
||||||
|
resizeObserver.observe(canvasRef.value);
|
||||||
|
window.addEventListener("resize", onResize);
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
gsap.ticker.remove(render);
|
||||||
|
window.removeEventListener("resize", onResize);
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
renderer?.dispose();
|
||||||
|
gsapCtx?.revert();
|
||||||
|
ScrollTrigger.getAll().forEach((t) => t.kill());
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="wrapper">
|
||||||
|
<!-- <div class="spacer"></div> -->
|
||||||
|
|
||||||
|
<canvas class="canvas" ref="canvasRef"></canvas>
|
||||||
|
</div>
|
||||||
|
<div class="scroll-buffer"></div>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.spacer {
|
||||||
|
width: 100%;
|
||||||
|
height: 200vh;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.scroll-buffer {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -48,45 +48,54 @@ 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>Spotify recommendation project</h2>
|
||||||
|
</div>
|
||||||
|
<div class="marker"></div>
|
||||||
|
<router-link to="/recommSpotify">
|
||||||
|
<button class="btn third-btn">Zur recommSpotify</button>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container fourth">
|
||||||
|
<div class="experience-text">
|
||||||
|
<h2>Here is the final step!</h2>
|
||||||
|
</div>
|
||||||
|
<div class="marker"></div>
|
||||||
|
<button class="btn fourth-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>
|
||||||
@@ -228,4 +237,14 @@ body {
|
|||||||
width: 100px;
|
width: 100px;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fourth {
|
||||||
|
left: 10%;
|
||||||
|
top: 750%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fourth .marker {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<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 gsap from "gsap";
|
import gsap from "gsap";
|
||||||
import ScrollTrigger from "gsap/ScrollTrigger";
|
import ScrollTrigger from "gsap/ScrollTrigger";
|
||||||
|
|
||||||
@@ -9,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 },
|
||||||
@@ -19,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,
|
||||||
@@ -43,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,
|
||||||
@@ -58,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">
|
||||||
@@ -88,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" />
|
||||||
@@ -98,7 +115,11 @@ onUnmounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="parallax__content"></section>
|
<section class="parallax__content">
|
||||||
|
<!-- <div ref="childRef" class="model__wrapper">
|
||||||
|
<MonitorModel />
|
||||||
|
</div> -->
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -146,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: 50svh;
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -208,6 +229,7 @@ onUnmounted(() => {
|
|||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
/* color: #e84b3d; 633a34 */
|
||||||
}
|
}
|
||||||
|
|
||||||
.parallax__radial-gradient {
|
.parallax__radial-gradient {
|
||||||
@@ -244,6 +266,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");
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import ExperienceView from '../views/ExperienceView.vue'
|
|||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
{ path: '/', component: HomeView },
|
{ path: '/', component: HomeView },
|
||||||
{ path: '/experience',
|
{ path: '/recommSpotify',
|
||||||
component: ExperienceView,
|
component: ExperienceView,
|
||||||
props: { modelUrl: '/models/door_room_firstVersion_materials-draco.glb', dracoPath: '/draco/' } }
|
props: { modelUrl: '/models/door_room_firstVersion_materials-draco.glb', dracoPath: '/draco/' } }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ onUnmounted(() => {
|
|||||||
dispose();
|
dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
function loadModel(url) {
|
function loadModel() {
|
||||||
if (model) {
|
if (model) {
|
||||||
scene.remove(model);
|
scene.remove(model);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import { onMounted, nextTick } from "vue";
|
|||||||
import gsap from "gsap";
|
import gsap from "gsap";
|
||||||
import ScrollTrigger from "gsap/ScrollTrigger";
|
import ScrollTrigger from "gsap/ScrollTrigger";
|
||||||
import ScrollSmoother from "gsap/ScrollSmoother";
|
import ScrollSmoother from "gsap/ScrollSmoother";
|
||||||
|
import MonitorModel from "../components/sections/MonitorModel.vue";
|
||||||
|
import IntroductionSection from "../components/sections/IntroductionSection.vue";
|
||||||
|
|
||||||
gsap.registerPlugin(ScrollTrigger, ScrollSmoother);
|
gsap.registerPlugin(ScrollTrigger, ScrollSmoother);
|
||||||
|
|
||||||
@@ -30,6 +32,8 @@ onMounted(async () => {
|
|||||||
<div id="smooth-wrapper">
|
<div id="smooth-wrapper">
|
||||||
<div id="smooth-content">
|
<div id="smooth-content">
|
||||||
<WelcomeSection id="welcome" />
|
<WelcomeSection id="welcome" />
|
||||||
|
<IntroductionSection id="welcome" />
|
||||||
|
<MonitorModel id="model" />
|
||||||
<!-- <HeroSection id="hero" /> -->
|
<!-- <HeroSection id="hero" /> -->
|
||||||
<ImageSection id="image" />
|
<ImageSection id="image" />
|
||||||
<TextSection id="text" />
|
<TextSection id="text" />
|
||||||
|
|||||||