feat(interaction): implement sophisticated hover effects and parallax
- Add moving shine effect and glow on card hover - Implement coffee drip animation for link underlines - Add JS-driven parallax scroll effect for floating elements - Enhance button micro-interactions - Refine animations for smoothness
This commit is contained in:
27
main.js
27
main.js
@@ -154,6 +154,32 @@ function initTestimonials() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== PARALLAX EFFECT FOR DECORATIVE ELEMENTS =====
|
||||||
|
function initParallax() {
|
||||||
|
const floatingElements = document.querySelectorAll('.floating-bean, .floating-cup');
|
||||||
|
if (!floatingElements.length) return;
|
||||||
|
|
||||||
|
let ticking = false;
|
||||||
|
|
||||||
|
window.addEventListener('scroll', () => {
|
||||||
|
if (!ticking) {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
const scrolled = window.scrollY;
|
||||||
|
|
||||||
|
floatingElements.forEach((el, index) => {
|
||||||
|
const speed = 0.05 + (index * 0.02); // Vary speed per element
|
||||||
|
const yPos = -(scrolled * speed);
|
||||||
|
// Use CSS variable to avoid overwriting the float animation transform
|
||||||
|
el.style.transform = `translateY(${yPos}px)`;
|
||||||
|
});
|
||||||
|
|
||||||
|
ticking = false;
|
||||||
|
});
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}, { passive: true });
|
||||||
|
}
|
||||||
|
|
||||||
// ===== INITIALIZE ALL =====
|
// ===== INITIALIZE ALL =====
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
initTypewriter();
|
initTypewriter();
|
||||||
@@ -161,4 +187,5 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
initSkillBars();
|
initSkillBars();
|
||||||
initParticles();
|
initParticles();
|
||||||
initTestimonials();
|
initTestimonials();
|
||||||
|
initParallax();
|
||||||
});
|
});
|
||||||
|
|||||||
71
styles.css
71
styles.css
@@ -139,7 +139,8 @@ body {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
opacity: 0.04;
|
opacity: 0.04;
|
||||||
color: var(--color-coffee-dark);
|
color: var(--color-coffee-dark);
|
||||||
animation: float-cup 18s infinite ease-in-out;
|
animation: float-rotate 18s infinite ease-in-out;
|
||||||
|
transition: transform 0.1s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
@@ -151,10 +152,17 @@ body {
|
|||||||
|
|
||||||
.cup-1 { width: 120px; top: 25%; right: 10%; animation-delay: -2s; }
|
.cup-1 { width: 120px; top: 25%; right: 10%; animation-delay: -2s; }
|
||||||
|
|
||||||
@keyframes float-cup {
|
@keyframes float-rotate {
|
||||||
0% { transform: translate(0, 0) rotate(-5deg); }
|
0% { transform: rotate(-5deg); }
|
||||||
50% { transform: translate(-20px, 30px) rotate(5deg); }
|
50% { transform: rotate(5deg); }
|
||||||
100% { transform: translate(0, 0) rotate(-5deg); }
|
100% { transform: rotate(-5deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes float-rotate {
|
||||||
|
0% { transform: rotate(-5deg); }
|
||||||
|
50% { transform: rotate(5deg); }
|
||||||
|
100% { transform: rotate(-5deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Coffee Stains */
|
/* Coffee Stains */
|
||||||
@@ -257,12 +265,25 @@ a {
|
|||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: text-decoration-color 0.2s;
|
position: relative;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
|
/* Coffee Drip Underline Animation */
|
||||||
|
background-image: linear-gradient(to right, var(--color-coffee-medium) 0%, var(--color-gold) 100%);
|
||||||
|
background-size: 0% 2px;
|
||||||
|
background-position: left bottom;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-decoration: underline;
|
background-size: 100% 2px;
|
||||||
text-decoration-thickness: 2px;
|
color: var(--color-coffee-dark);
|
||||||
text-underline-offset: 4px;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
a:hover {
|
||||||
|
color: var(--color-gold);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,10 +419,11 @@ section {
|
|||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
-webkit-backdrop-filter: blur(12px);
|
-webkit-backdrop-filter: blur(12px);
|
||||||
border: 1px solid var(--border-subtle);
|
border: 1px solid var(--border-subtle);
|
||||||
border-radius: 16px; /* Increased radius */
|
border-radius: 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
/* Scroll Animation Entry */
|
/* Scroll Animation Entry */
|
||||||
view-timeline-name: --card-entry;
|
view-timeline-name: --card-entry;
|
||||||
@@ -413,15 +435,38 @@ section {
|
|||||||
&:hover {
|
&:hover {
|
||||||
transform: translateY(-8px) scale(1.02);
|
transform: translateY(-8px) scale(1.02);
|
||||||
box-shadow: var(--shadow-glass);
|
box-shadow: var(--shadow-glass);
|
||||||
border-color: var(--color-gold); /* Gold Accent */
|
border-color: var(--color-gold);
|
||||||
background-color: color-mix(in srgb, var(--bg-surface), var(--color-cream) 10%);
|
background-color: color-mix(in srgb, var(--bg-surface), var(--color-cream) 10%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Shine Effect */
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
transparent,
|
||||||
|
rgba(255, 255, 255, 0.2),
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
transition: 0.5s;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::after {
|
||||||
|
left: 100%;
|
||||||
|
transition: 0.7s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
.card-img-top {
|
.card-img-top {
|
||||||
aspect-ratio: 16/9;
|
aspect-ratio: 16/9;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
transition: transform 0.6s ease;
|
transition: transform 0.6s ease;
|
||||||
filter: sepia(20%) contrast(1.1); /* Subtle coffee tint */
|
filter: sepia(20%) contrast(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover .card-img-top {
|
&:hover .card-img-top {
|
||||||
@@ -435,7 +480,7 @@ section {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
h5.card-title {
|
h5.card-title {
|
||||||
font-size: 1.5rem; /* Larger */
|
font-size: 1.5rem;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
letter-spacing: -0.02em;
|
letter-spacing: -0.02em;
|
||||||
|
|||||||
Reference in New Issue
Block a user