project page need more but nice
This commit is contained in:
1 parent
8c410cacf8
commit
77cb652917
7 files changed
+224
-131
No files matched your search
+1
-1
@@ -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>
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,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";
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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>
|
||||||
Reference in new issue
Block a user