project page need more but nice

This commit is contained in:
nessar committed 2023-03-27 04:31:38 +02:00
1 parent 8c410cacf8
commit 77cb652917
7 files changed
+224 -131

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@
<body style="overflow-x: hidden;"> <body style="overflow-x: hidden;">
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="src/assets/ts/main.ts"></script>
</body> </body>
</html> </html>
+4
View File
@@ -39,6 +39,10 @@ export default defineComponent({
</template> </template>
<style scoped> <style scoped>
main {
display: grid;
}
.links ol { .links ol {
list-style: none; list-style: none;
display: flex; display: flex;
+23 -7
View File
@@ -1,5 +1,5 @@
{ [
"projects": { {
"name": "test1", "name": "test1",
"year": 2021, "year": 2021,
"type": "School Project", "type": "School Project",
@@ -14,9 +14,25 @@
"github": "https://www.github.com", "github": "https://www.github.com",
"release": "https://www.release.com" "release": "https://www.release.com"
}, },
"images": { "logo": "https://www.google.com",
"main": "https://www.google.com", "video": "https://www.youtube.com"
"alt": "https://www.google.com" },
} {
"name": "test2",
"year": 2010,
"type": "School Project",
"desc": "This is a super testing project",
"tech": [
"HTML",
"CSS",
"JavaScript"
],
"links": {
"title": "https://www.google.com",
"github": "https://www.github.com",
"release": "https://www.release.com"
},
"logo": "https://www.google.com",
"video": "https://www.youtube.com"
} }
} ]
+1 -1
View File
@@ -1,6 +1,6 @@
import { createApp } from "vue"; import { createApp } from "vue";
import Particles from "vue3-particles"; import Particles from "vue3-particles";
import App from "./App.vue"; import App from "@/App.vue";
import "@/assets/css/main.css"; import "@/assets/css/main.css";
+10
View File
@@ -0,0 +1,10 @@
export interface Project {
name: string;
year: number;
type: string;
desc: string;
tech: string[];
links: { [key: string]: string };
logo: string;
video: string;
}
+91 -3
View File
@@ -1,11 +1,99 @@
<template> <template>
<div> <div class="project">
<h1>{{ project.name }}</h1>
<div class="info">
<p>{{ project.desc }}</p>
<video>
<source :src="project.video" type="video/mp4" />
</video>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineComponent } from 'vue';
import type { Project } from '@/assets/ts/project';
</script>
<script lang="ts">
export default defineComponent({
name: 'project',
props: {
project: {
type: Object as () => Project,
required: true,
},
fullscreen: {
type: Boolean,
required: true,
},
color: {
type: String,
required: true,
},
},
data() {
return {
width: '80vw',
height: '80vh',
};
},
watch: {
fullscreen: function (val) {
if (val) {
this.width = '100vw';
this.height = '100vh';
} else {
this.width = '80vw';
this.height = '80vh';
}
},
},
});
</script> </script>
<style scoped></style> <style scoped>
.project {
width: v-bind(width);
height: v-bind(height);
background-color: v-bind(color);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
transition: width 0.75s, height 0.75s;
}
h1 {
color: black;
font-size: 2.5em;
}
p {
color: black;
font-size: 1.5em;
}
video {
width: 100%;
height: 100%;
}
.info {
width: 50%;
font-family: montserrat_regular;
display: flex;
align-items: center;
justify-content: center;
flex-direction: row;
}
.info p {
background-color: gray;
padding: 25px;
border-radius: 15px;
margin-right: -35px;
}
</style>
+94 -119
View File
@@ -1,67 +1,102 @@
<template> <template>
<div class="spacer timeline" /> <div class="spacer bg" />
<div class="timeline" id="timeline"> <a id="projects" />
<a id="projects" /> <div class="timeLineOuter bg" id="timeLineOuter">
<div id="animStart" /> <div id="timelineSticky">
<div class="transform" id="transform"> <div class="timeline bg" id="timeline">
<div class="circle" /> <project v-for="(p, id) in projects" :project="p" :fullscreen="fullscreen"
<div class="connector" /> :color="COLORS[id % COLORS.length]" />
<div class="project">
<div class="infos">
<h1>titre</h1>
<p>descriptiondescriptiondescriptiondescriptiondescriptiondescriptiondescriptiriptiondescriptiondescriptiondescriptiondescriptiondescriptiondescription
</p>
<div class="button">
<a href="test">test</a>
</div>
</div>
<video>
<source src="@/assets/videos/test.mp4" type="video/mp4" />
</video>
</div> </div>
</div> </div>
</div> </div>
<div id="animStop" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import type { Project } from '@/assets/ts/project';
import project from './project.vue';
</script> </script>
<script lang="ts"> <script lang="ts">
const enum STATUS {
BEFOREVIEWPORT,
INVIEWPORT,
AFTERVIEWPORT,
}
const COLORS = ["#DDF9C1", "#F8DDA4", "#F9A03F", "#D45113", "#813405"]
export default defineComponent({ export default defineComponent({
name: 'timeline', name: 'timeline',
created() { created() {
window.addEventListener('scroll', this.handleScroll); window.addEventListener('scroll', this.handleScroll);
fetch("src/assets/json/projects.json")
.then(response => response.json())
.then((data: Project[]) => {
this.projects = data;
this.nbProjects = data.length;
})
.catch((error) => {
console.error('Error:', error);
});
}, },
data() { data() {
return { return {
nbProjects: 0,
projects: [] as Project[],
fullscreen: false,
gap: "20vw"
}; };
}, },
methods: { methods: {
handleScroll(event: Event) { handleScroll(event: Event) {
if (this.activateAnimation()) { const timelineSticky = document.getElementById('timelineSticky');
const timeline = document.getElementById('timeline')!.getBoundingClientRect(); const timeline = document.getElementById('timeline');
if (!timeline) return; const timeLineOuter = document.getElementById('timeLineOuter');
const position = timeline.top; if (!timelineSticky || !timeline || !timeLineOuter) return;
document.getElementById('transform')?.setAttribute('style', `transform: translateY(calc(-50vh + ${-position}px)) rotate(${-position}deg);`); const rect = timeLineOuter.getBoundingClientRect();
const inViewPort = this.isTimelineInViewport();
switch (inViewPort) {
case STATUS.BEFOREVIEWPORT:
this.fullscreen = false
this.gap = "20vw"
timelineSticky.style.position = 'absolute';
timelineSticky.style.top = '10vh';
timelineSticky.style.bottom = '';
timeline.style.transform = `translateX(0px)`;
break;
case STATUS.INVIEWPORT:
this.fullscreen = true
this.gap = ""
timelineSticky.style.position = 'fixed';
timelineSticky.style.top = '10vh';
timelineSticky.style.bottom = '10vh';
// translate the timeline to the right by the amount of scroll
timeline.style.transform = `translateX(${-rect.top}px)`;
break;
case STATUS.AFTERVIEWPORT:
this.fullscreen = false
this.gap = "20vw"
timelineSticky.style.position = 'absolute';
timelineSticky.style.top = '';
timelineSticky.style.bottom = '10vh';
const widthByProject = timeline.getBoundingClientRect().width / this.nbProjects;
timeline.style.transform = `translateX(${widthByProject * (this.nbProjects - 1)}px)`;
break;
} }
// else { timelineSticky.style.width = '100vw';
// document.getElementById('transform')?.setAttribute('style', `transform: translateY(-50vh) rotate(0deg);`); timelineSticky.style.height = '80vh';
// }
}, },
activateAnimation() { isTimelineInViewport(): STATUS {
let start = document.getElementById('animStart')?.getBoundingClientRect(); const timeLineOuter = document.getElementById('timeLineOuter');
let stop = document.getElementById('animStop')?.getBoundingClientRect(); if (!timeLineOuter) return STATUS.BEFOREVIEWPORT;
if (!start || !stop) return false; const rect = timeLineOuter.getBoundingClientRect();
if (stop.top >= 0 && stop.bottom <= (window.innerHeight || document.documentElement.clientHeight)) if (rect.top < 0 && rect.bottom > window.innerHeight)
return false; return STATUS.INVIEWPORT;
else { if (rect.top > 0)
if (start.top >= 0) return STATUS.BEFOREVIEWPORT;
return false; return STATUS.AFTERVIEWPORT;
else
return true;
}
}, },
}, },
}) })
@@ -72,89 +107,29 @@ export default defineComponent({
height: 20vh !important; height: 20vh !important;
} }
.bg {
background-color: #292929;
}
.timeLineOuter {
position: relative;
width: 100vw;
height: calc(100vw * v-bind(nbProjects - 1) + 100vh);
}
.timeline { .timeline {
background: linear-gradient(to right, #000000, #292929, #000000); width: calc(100vw*v-bind(nbProjects));
height: 1000vh; height: 80Vh;
}
.transform {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-direction: row; flex-direction: row-reverse;
height: 200vh; flex-wrap: nowrap;
width: 200vw;
margin-left: -100vw;
transform: translateY(-50vh);
margin-bottom: -100vh;
}
.circle {
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
background-color: transparent;
position: absolute; position: absolute;
z-index: 1; left: calc(-100vw*v-bind(nbProjects - 1));
border: 5vw groove white; right: 0;
} will-change: transform;
transition: transform 0.1s;
.project { gap: v-bind(gap)
padding: 15px;
display: flex;
align-items: center;
justify-content: left;
flex-direction: column;
border: 5px solid white;
border-radius: 25px;
width: 70vw;
/* margin: 10vh 5vw 0 15vw; */
filter: drop-shadow(0px 0px 20px rgba(255, 255, 255, 0.5));
}
.connector {
margin-left: 110vw;
height: 5px;
width: 10vw;
background-color: rgb(255, 255, 255);
z-index: 3;
}
video {
width: 100%;
height: 100%;
z-index: 1;
overflow: hidden;
border-radius: 15px;
}
.project .infos {
width: 80%;
z-index: 2;
}
/* Styles for screens taller than wide */
@media screen and (orientation: portrait) {
.circle {
width: 30vw;
height: 30vw;
}
.infos {
background-color: rgb(29, 27, 41);
margin-bottom: -25px;
border-radius: 25px;
padding: 25px;
word-break: break-all;
}
}
/* Styles for screens wider than tall */
@media screen and (orientation: landscape) {
.circle {
width: 80vh;
height: 80vh;
}
} }
</style> </style>