Initial commit: Modern photography portfolio with Astro

- Built with Astro 5.16.7 and Tailwind CSS 4.1.18
- Features advanced animations (waves, 3D scroll, smoke effects)
- Fully responsive design with mobile-first approach
- SEO optimized with semantic HTML
- Interactive elements and smooth transitions
- Professional portfolio showcase
This commit is contained in:
Linus Olofsson
2026-01-28 00:05:23 +01:00
commit b0b633d32b
84 changed files with 1656 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
---
// src/components/ThreeDScroll.astro
const images = [
{ src: "/3d-scroll/img1.jpg", alt: "Architecture Dark" },
{ src: "/3d-scroll/img2.jpg", alt: "Shadows Light" },
{ src: "/3d-scroll/img3.jpg", alt: "Portrait High End" },
{ src: "/3d-scroll/img4.jpg", alt: "Minimalist Landscape" },
{ src: "/3d-scroll/img5.jpg", alt: "Foggy Forest" },
];
---
<div id="scroll-container" class="relative h-[400vh] w-full">
<div class="sticky top-0 h-screen w-full overflow-hidden perspective-container bg-transparent">
<div id="scene" class="absolute inset-0 w-full h-full transform-style-3d flex items-center justify-center">
{images.map((img, index) => (
<div
class="gallery-item absolute transform-style-3d will-change-transform flex items-center justify-center"
data-index={index}
>
<img
src={img.src}
alt={img.alt}
class="floating-animation max-w-[80vw] max-h-[70vh] w-auto h-auto object-contain rounded-lg shadow-[0_20px_60px_-15px_rgba(0,0,0,0.8)] border border-white/5 grayscale hover:grayscale-0 transition-all duration-700 ease-out"
/>
</div>
))}
</div>
</div>
</div>
<style>
.perspective-container {
perspective: 1000px;
}
.transform-style-3d {
transform-style: preserve-3d;
}
/* Svävande animation */
@keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-5px); }
}
/* Vi sätter olika delay på animationen så alla bilder inte svävar i synk */
.floating-animation {
animation: float 6s ease-in-out infinite;
}
.gallery-item:nth-child(odd) .floating-animation {
animation-duration: 7s;
animation-delay: 1s;
}
</style>
<script>
const container = document.getElementById('scroll-container');
const scene = document.getElementById('scene');
const items = document.querySelectorAll('.gallery-item');
const Z_SPACING = 1500;
const START_Z = -500;
function updateScroll() {
if (!container || !scene) return;
const rect = container.getBoundingClientRect();
const scrollProgress = -rect.top;
// Om vi inte scrollat in än, nollställ
if (scrollProgress < 0) return;
items.forEach((item, index) => {
let zPos = (scrollProgress * 1.5) - (index * Z_SPACING) + START_Z;
let opacity = 1;
if (zPos > 500) {
opacity = 1 - ((zPos - 500) / 400);
}
if (zPos < -2000) {
opacity = 1 - (Math.abs(zPos + 2000) / 2000);
}
if (opacity < 0) opacity = 0;
(item as HTMLElement).style.transform = `translateZ(${zPos}px)`;
(item as HTMLElement).style.opacity = opacity.toString();
});
requestAnimationFrame(updateScroll);
}
window.addEventListener('scroll', () => {
requestAnimationFrame(updateScroll);
});
updateScroll();
</script>