Skip to content

Commit

Permalink
Falling snow/ash effect working for about me page
Browse files Browse the repository at this point in the history
  • Loading branch information
philwing100 committed Aug 14, 2024
1 parent e76bc79 commit 9a79091
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 98 deletions.
81 changes: 81 additions & 0 deletions src/components/AboutMeComponents/AshEffect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<template>
<div class="particle-container" ref="container"></div>
</template>

<script>
export default {
mounted() {
this.particles = [];
this.$nextTick(() => {
// Ensures DOM is fully rendered before accessing the container
this.createParticles();
this.animateParticles();
});
},
methods: {
createParticles() {
for (let i = 0; i < 50; i++) {
this.addParticle();
}
},
addParticle() {
const container = this.$refs.container;
if (!container) return; // Prevents error if container isn't available
const particle = document.createElement('div');
particle.className = 'particle';
container.appendChild(particle);
const size = Math.random() * 5 + 2; // Random size between 2px and 7px
const x = Math.random() * container.clientWidth;
const y = -size;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.left = `${x}px`;
particle.style.top = `${y}px`;
this.particles.push({ element: particle, x, y, size, velocityY: Math.random() * 2 + 1 });
},
animateParticles() {
const container = this.$refs.container;
if (!container) return; // Prevents error if container isn't available
this.particles.forEach(particle => {
particle.y += particle.velocityY;
particle.element.style.top = `${particle.y}px`;
if (particle.y > container.clientHeight) {
// Reset particle to the top
particle.y = -particle.size;
particle.x = Math.random() * container.clientWidth;
particle.element.style.left = `${particle.x}px`;
}
});
// Bind 'this' to the method to ensure it has the correct context
requestAnimationFrame(this.animateParticles.bind(this));
}
}
}
</script>

<style>
.particle-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
overflow: hidden;
pointer-events: none;
z-index: 1;
}
.particle {
position: absolute;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 50%;
pointer-events: none;
}
</style>
216 changes: 121 additions & 95 deletions src/components/AboutMeComponents/FullPage.vue
Original file line number Diff line number Diff line change
@@ -1,106 +1,132 @@
/* eslint-disable */

<template>
<div class="fullpage-container" @wheel="handleScroll">
<div
class="section"
v-for="(section, index) in sections"
:key="section.id"
:ref="`section-${index}`"
>
<h2>{{ section.title }}</h2>
<p>{{ section.content }}</p>
</div>
<div class="scroll-indicator" v-if="showIndicator">
<button v-for="(section, index) in sections" :key="index" @click="scrollToSection(index)">
{{ section.title }}
</button>
</div>
<div class="fullpage-container" @wheel="handleScroll">
<div
class="section"
v-for="(section, index) in sections"
:key="section.id"
:ref="`section-${index}`"
:class="`section-${index}`"
>
<h2 class="content">{{ section.title }}</h2>
<p class="content">{{ section.content }}</p>
</div>
<div class="scroll-indicator" v-if="showIndicator">
<button v-for="(section, index) in sections" :key="index" @click="scrollToSection(index)">
{{ section.title }}
</button>
</div>
</template>

<script>
export default {
props: {
sections: {
type: Array,
required: true
},
showIndicator: {
type: Boolean,
default: true
</div>
</template>

<script>
import AshEffect from './AshEffect.vue';
export default {
components: {
},
props: {
sections: {
type: Array,
required: true
},
showIndicator: {
type: Boolean,
default: true
}
},
data() {
return {
activeSection: 0,
isScrolling: false,
};
},
methods: {
scrollToSection(index) {
const section = this.$refs[`section-${index}`][0];
if (section) {
section.scrollIntoView({ behavior: 'smooth' });
// Set a delay after the animation to prevent accepting new inputs too soon
setTimeout(() => {
this.isScrolling = false;
}, 1200); // Delay for accepting new scroll inputs
}
},
data() {
return {
activeSection: 0,
isScrolling: false,
};
handleScroll(event) {
if (this.isScrolling) return;
this.isScrolling = true;
const direction = event.deltaY > 0 ? 1 : -1;
this.activeSection += direction;
if (this.activeSection < 0) {
this.activeSection = 0;
} else if (this.activeSection >= this.sections.length) {
this.activeSection = this.sections.length - 1;
}
setTimeout(() => {
this.scrollToSection(this.activeSection);
}, 100);
},
computeGradient(index, totalSections) {
const step = 255 / (totalSections - 1);
const rgbValue = Math.round(step * index);
return `linear-gradient(rgb(${rgbValue}, ${rgbValue}, ${rgbValue}), rgb(${rgbValue + step}, ${rgbValue + step}, ${rgbValue + step}))`;
},
methods: {
scrollToSection(index) {
applyGradients() {
this.sections.forEach((_, index) => {
const section = this.$refs[`section-${index}`][0];
if (section) {
section.scrollIntoView({ behavior: 'smooth' });
// Set a delay after the animation to prevent accepting new inputs too soon
setTimeout(() => {
this.isScrolling = false;
}, 1200); // Delay for accepting new scroll inputs
}
},
handleScroll(event) {
if (this.isScrolling) return;
this.isScrolling = true;
const direction = event.deltaY > 0 ? 1 : -1;
this.activeSection += direction;
if (this.activeSection < 0) {
this.activeSection = 0;
} else if (this.activeSection >= this.sections.length) {
this.activeSection = this.sections.length - 1;
section.style.backgroundImage = this.computeGradient(index, this.sections.length);
}
setTimeout(() => {
this.scrollToSection(this.activeSection);
}, 100);
}
},
mounted() {
this.$nextTick(() => {
this.scrollToSection(this.activeSection);
});
}
};
</script>

<style scoped>
.fullpage-container {
overflow: hidden;
height: 100vh;
position: relative;
}
.section {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: lightgray;
border-bottom: 1px solid #ccc;
}
.scroll-indicator {
position: fixed;
right: 10px;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
}
.scroll-indicator button {
margin-bottom: 10px;
},
mounted() {
this.$nextTick(() => {
this.applyGradients();
this.scrollToSection(this.activeSection);
});
}
</style>

};
</script>

<style scoped>
.fullpage-container {
overflow: hidden;
height: 100vh;
position: relative;
}
.section {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.section:nth-child(1) .content {
color: white;
}
.content {
z-index: 2;
}
.scroll-indicator {
position: fixed;
right: 10px;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
}
.scroll-indicator button {
margin-bottom: 10px;
}
</style>
9 changes: 9 additions & 0 deletions src/components/AboutMeComponents/Projects.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<template>


</template>

<script>
</script>

2 changes: 1 addition & 1 deletion src/components/SidebarComponents/SideBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export default {
.sidebar {
height: 100%;
position: fixed;
z-index: 1;
z-index: 4;
top: 0;
left: 0;
overflow-x: hidden;
Expand Down
14 changes: 12 additions & 2 deletions src/views/AboutMe.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
<template>
<template class="page">
<AshEffect />
<div>
<FullPage :sections="sections" :showIndicator="true" />
</div>
</template>

<script>
import FullPage from '../components/AboutMeComponents/FullPage.vue';
import AshEffect from '../components/AboutMeComponents/AshEffect.vue';
export default {
components: {
FullPage
FullPage,
AshEffect,
},
data() {
return {
Expand All @@ -23,3 +26,10 @@ export default {
}
};
</script>

<style>
.page{
}
</style>

0 comments on commit 9a79091

Please sign in to comment.