Added about section with image carousel and dynamic scrolling.

This commit is contained in:
jme9
2024-12-03 12:43:33 -08:00
parent 5e9e9406d5
commit 3b3bba746b
6 changed files with 390 additions and 31 deletions
+117
View File
@@ -69,6 +69,8 @@ _Note_: I did this in procreate, which is not great for this sort of thing.
![Quick design sketch of the personal website.](images\WebsiteSketch.jpg)
---
## Figma Design
**11/27/2024**
@@ -77,6 +79,8 @@ I decided to go ahead and design the website using Figma so that everything I ne
![Figma design for the personal website.](images\Figma.jpg)
---
## Navbar and background
**11/30/2024**
@@ -165,6 +169,8 @@ Lookin pretty good!
![Initial navbar work for the personal website.](images\SiteUpdate1.jpg)
---
## Profile Pic
**12/01/2024**
@@ -274,3 +280,114 @@ Then we can add some initial CSS styling,
text-align: left;
}
```
---
## About Section
**12/03/2024**
There are three somewhat tricky things I want to accomplish in the about section that we may use in the other sections. First we need to split the box into two columns so that we can have text on the left and images on the right. Then, I want to load in the text using JavaScript so that our html doesn't get overloaded with text. Finally, I want to add a component that allows you to flip through images on the right side of the text.
**Columns in the box**
To Accomplish this we can use flex. Each of our "columns" will be set to flex: 1 to ensure they take up equal space in the box. Additionally we can easily use media queries to set our text and images to display vertically when the screen size is narrow.
**Load text file**
Conveniently we can use the fetch api to do this. This also allows for easy error handling in cases that the file does not load. When you pass a relative file path like 'about.txt' to fetch, it looks for the file relative to the location of our html file being served. E.g, since our site.html is in the root folder, fetch('about.txt') looks for the file at oursite.com/about.txt
```
function loadTextFromFile(filePath, targetSelector) {
fetch(filePath)
.then((response) => {
if (!response.ok) {
throw new Error("Failed to fetch file: " + response.statusText);
}
return response.text();
})
.then((text) => {
const targetElement = document.querySelector(targetSelector);
if (targetElement) {
targetElement.textContent = text;
} else {
console.error(`Target element not found: ${targetSelector}`);
}
})
.catch((error) => {
console.error("Error loading text from file:", error);
});
}
```
Now we need a way to know when to load the text so let's add an event listener. We can listen for when the page finishes loading. The browser will send a 'load' flag on the window object when everything has loaded.
```
window.addEventListener("load", () => {
loadTextFromFile("about.txt", ".about-paragraph");
});
```
**Image carousel**
I thought it would be cool to show images related to the about section. The user shoudl be able to flip through the images. To accomplish this we can code in the images to our html section under two class types, current-image and hidden-image. the carousel effect will be created by setting the class value of each image, e.g. the image we want to see is set to current and the rest are set to hidden. To flip through the images we will create a previous button and a next button. We can handle this logic in JavaScript by creating event listeners to get the images and buttons, detect button clicks and define a function to set the current and hidden images.
```
// Image carousel
document.addEventListener("DOMContentLoaded", () => {
const images = document.querySelectorAll(".about-image img");
const prevBtn = document.querySelector(".prev-btn");
const nextBtn = document.querySelector(".next-btn");
let currentIndex = 0;
// Function to update image visibility
function updateImages() {
images.forEach((img, index) => {
img.classList.toggle("current-image", index === currentIndex);
img.classList.toggle("hidden-image", index !== currentIndex);
});
}
// Event listeners for navigation buttons
prevBtn.addEventListener("click", () => {
currentIndex = (currentIndex - 1 + images.length) % images.length;
updateImages();
});
nextBtn.addEventListener("click", () => {
currentIndex = (currentIndex + 1) % images.length;
updateImages();
});
updateImages();
});
```
**Side-quest**
I had a cool idea that the title section could scroll up at a slower rate than the about section until they are touching. As it scrolls up the opacity should decrease until the component "disappears". I think this will make the site feel a little more dynamic to the user and could also be used for a parallaxing background at some point!
We need to get seom eitems and values using query selectors, the title box, about section, the window scroll Y value and the upward bound of the about section. As we scroll the view we can explicitly translate the title box Y value at a slower rate as well as decrease the opacity. This way once the title box meets the about section the title box will be fully transparent and move with the about section. Initially this worked but broke my navigation bar. After some troubleshooting I found that the I neededto explicitly set the title box translate to zero when not scrolling.
```
// dynamic scrolling
document.addEventListener("scroll", () => {
const titleBox = document.querySelector(".title-box");
const aboutSection = document.querySelector("#about");
const scrollY = window.scrollY;
const aboutTop = aboutSection.getBoundingClientRect().top + window.scrollY;
const slowScrollRate = 0.5;
// move title-box if it's above the viewport and not yet touching about-section
if (scrollY < aboutTop) {
// Adjust position adn opacity
titleBox.style.transform = `translateY(${scrollY * slowScrollRate}px)`;
const opacity = 1 - scrollY / aboutTop;
titleBox.style.opacity = Math.max(opacity, 0);
} else {
titleBox.style.transform = "translateY(0)";
titleBox.style.opacity = 0;
}
});
```
+5
View File
@@ -0,0 +1,5 @@
Hey! This section is about me!
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

+37 -9
View File
@@ -12,12 +12,17 @@
<header id="top" class="site-header">
<nav class="navbar">
<div class="nav-left">
<img
class="icon-pic"
src="images/Ravenwood Watermark .png"
alt="Ravenwood Watermark"
/>
<a href="#top" class="nav-link">The Ravenwood Arts</a>
</div>
<div class="nav-right">
<a href="#about" class="nav-link">About</a>
<a href="#work" class="nav-link">Work</a>
<a href="#projects" class="nav-link">Projects</a>
<a href="#work" class="nav-link">Work</a>
<a href="#contact" class="nav-link">Contact</a>
</div>
</nav>
@@ -39,16 +44,33 @@
<section id="about" class="about-section">
<div class="about-box">
<!-- First Column: About Text -->
<div class="about-text">
<h1 class="about-title">About Me</h1>
<p class="about-paragraph">Hey this is about me!</p>
<h1 class="about-title">About Jesse,</h1>
<p class="about-paragraph">Loading...</p>
</div>
<!-- Second Column: Images -->
<div class="about-image">
<img
src="images/Figma.jpg"
alt="About Me Images"
class="current-image"
/>
<img
src="images/Clouds.png"
alt="About me images"
class="hidden-image"
/>
<img
src="images/SiteUpdate1.jpg"
alt="About me images"
class="hidden-image"
/>
<div class="about-buttons">
<button class="prev-btn">&larr;</button>
<button class="next-btn">&rarr;</button>
</div>
</div>
</div>
</section>
<section id="work" class="work-section">
<div class="work-box">
<h2 class="section-title">Previous Work</h2>
</div>
</section>
@@ -58,6 +80,12 @@
</div>
</section>
<section id="work" class="work-section">
<div class="work-box">
<h2 class="section-title">Previous Work</h2>
</div>
</section>
<section id="contact" class="contact-section">
<div class="contact-box">
<h2 class="section-title">Contact</h2>
+83 -6
View File
@@ -1,9 +1,86 @@
document.querySelectorAll('.nav-link').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
// Scroll page
document.querySelectorAll(".nav-link").forEach((anchor) => {
anchor.addEventListener("click", function (e) {
e.preventDefault();
document.querySelector(this.getAttribute("href")).scrollIntoView({
behavior: "smooth",
});
});
});
// dynamic scrolling
document.addEventListener("scroll", () => {
const titleBox = document.querySelector(".title-box");
const aboutSection = document.querySelector("#about");
const scrollY = window.scrollY;
const aboutTop = aboutSection.getBoundingClientRect().top + window.scrollY;
const slowScrollRate = 0.5;
// move title-box if it's above the viewport and not yet touching about-section
if (scrollY < aboutTop) {
// Adjust position adn opacity
titleBox.style.transform = `translateY(${scrollY * slowScrollRate}px)`;
const opacity = 1 - scrollY / aboutTop;
titleBox.style.opacity = Math.max(opacity, 0);
} else {
titleBox.style.transform = "translateY(0)";
titleBox.style.opacity = 0;
}
});
// Function to load text from a file into an html component
function loadTextFromFile(filePath, targetSelector) {
fetch(filePath)
.then((response) => {
if (!response.ok) {
throw new Error("Failed to fetch file: " + response.statusText);
}
return response.text();
})
.then((text) => {
const targetElement = document.querySelector(targetSelector);
if (targetElement) {
targetElement.textContent = text;
} else {
console.error(`Target element not found: ${targetSelector}`);
}
})
.catch((error) => {
console.error("Error loading text from file:", error);
});
}
// Load the "About Me" text when page loads
window.addEventListener("load", () => {
loadTextFromFile("about.txt", ".about-paragraph");
});
// Image carousel
document.addEventListener("DOMContentLoaded", () => {
const images = document.querySelectorAll(".about-image img");
const prevBtn = document.querySelector(".prev-btn");
const nextBtn = document.querySelector(".next-btn");
let currentIndex = 0;
// Function to update image visibility
function updateImages() {
images.forEach((img, index) => {
img.classList.toggle("current-image", index === currentIndex);
img.classList.toggle("hidden-image", index !== currentIndex);
});
}
// Event listeners for navigation buttons
prevBtn.addEventListener("click", () => {
currentIndex = (currentIndex - 1 + images.length) % images.length;
updateImages();
});
nextBtn.addEventListener("click", () => {
currentIndex = (currentIndex + 1) % images.length;
updateImages();
});
updateImages();
});
+146 -14
View File
@@ -5,7 +5,7 @@ body {
background-size: cover;
background-position: left;
background-attachment: fixed;
min-width: 700px;
min-width: 768px;
}
.navbar {
@@ -17,12 +17,21 @@ body {
align-items: center;
background: rgba(0, 0, 0, 0.8);
padding: 2.5rem;
z-index: 10;
}
.nav-left {
display: flex;
align-items: center;
gap: 1rem;
padding-left: 2rem;
}
.icon-pic {
width: 45px;
height: 45px;
}
.nav-right {
display: flex;
gap: 4rem;
@@ -31,11 +40,27 @@ body {
.nav-link {
color: #fff;
font-size: 1rem;
margin-right: 1rem;
text-decoration: none;
padding: 10px;
border-radius: 10px;
}
.nav-link:hover {
background: rgba(24, 18, 18, 0.8);
}
.section {
display: flex;
justify-content: center;
padding: 2rem;
color: #fff;
margin-top: 2rem;
}
.title-section {
position: relative;
height: 70vh;
display: flex;
justify-content: right;
@@ -50,7 +75,8 @@ body {
justify-content: space-between;
background: rgba(0, 0, 0, 0.8);
border-radius: 100px;
padding: 1.5rem;
margin-top: 15rem;
padding: 0.75rem;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
max-width: 80%;
min-width: 400px;
@@ -71,8 +97,8 @@ body {
.title-text h1 {
margin: 0;
font-size: 1.8rem;
color: #f0caca;
font-size: 3rem;
color: #ffe8e8;
}
.title-text p {
@@ -81,14 +107,6 @@ body {
font-size: 1rem;
}
.section {
display: flex;
justify-content: center;
padding: 2rem;
color: #fff;
margin-top: 2rem;
}
.about-section,
.work-section,
.projects-section,
@@ -98,16 +116,130 @@ body {
width: 100%;
}
.about-box,
/* About box */
.about-box {
display: flex;
margin-top: 10rem;
gap: 2rem;
background: rgba(0, 0, 0, 0.9);
border-radius: 20px;
padding: 2rem;
width: 95%;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
color: #ffe8e8;
box-sizing: border-box;
height: auto;
}
.about-text {
flex: 1;
}
.about-paragraph {
line-height: 1.6;
white-space: pre-wrap;
}
/* About box image carousel */
.about-image {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
height: 520px;
width: 100%;
overflow: hidden;
border-radius: 10px;
position: relative;
}
.about-image img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
border-radius: 10px;
transition: opacity 0.5s ease;
}
.hidden-image {
display: none;
}
.current-image {
display: block;
height: calc(100% - 60px);
}
.about-buttons {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
display: flex;
justify-content: space-between;
width: 60%;
gap: 15rem;
}
button.prev-btn,
button.next-btn {
font-size: xx-large;
background: rgba(40, 49, 102, 0.5);
color: white;
border: none;
border-radius: 10px;
cursor: pointer;
flex: 1;
line-height: 1.6rem;
padding-bottom: 10px;
}
button:hover {
background: rgba(45, 38, 75, 0.5);
}
/* ----- */
.work-box,
.project-box,
.contact-box {
margin-top: 10rem;
background: rgba(0, 0, 0, 0.8);
border-radius: 20px;
padding: 1rem;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
width: 95%;
color: #fff;
color: #ffe8e8;
box-sizing: border-box;
text-align: left;
}
/* Media Queries */
@media (max-width: 850px) {
.about-box {
flex-direction: column;
text-align: center;
height: 1040px;
}
.title-text h1 {
font-size: 1.5rem;
}
.about-image {
margin-top: 1rem;
}
.nav-left {
padding-left: 0rem;
}
.nav-right {
gap: 1rem;
padding-right: 5rem;
}
}