Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions src/lib/components/FloatingOrbs.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { browser } from '$app/environment';

let canvas: HTMLCanvasElement;
let ctx: CanvasRenderingContext2D | null;
let animationId: number;

interface Orb {
x: number;
y: number;
vx: number;
vy: number;
radius: number;
color: string;
glowIntensity: number;
pulseSpeed: number;
pulseOffset: number;
opacity: number;
}

let orbs: Orb[] = [];
const orbCount = 10;
const connectionDistance = 180;

const colors = [
{ h: 180, s: 60, l: 50 }, // Teal (primary)
{ h: 200, s: 50, l: 55 }, // Light blue
{ h: 280, s: 45, l: 60 }, // Purple
{ h: 160, s: 55, l: 48 }, // Green-teal
{ h: 220, s: 40, l: 58 }, // Blue
];

function initOrbs() {
if (!canvas) return;
orbs = [];
const width = window.innerWidth;
const height = window.innerHeight;

for (let i = 0; i < orbCount; i++) {
const colorData = colors[Math.floor(Math.random() * colors.length)];
const speed = 0.1 + Math.random() * 0.15;
const angle = Math.random() * Math.PI * 2;

orbs.push({
x: Math.random() * width,
y: Math.random() * height,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
radius: 15 + Math.random() * 25,
color: `hsl(${colorData.h}, ${colorData.s}%, ${colorData.l}%)`,
glowIntensity: 0.08 + Math.random() * 0.05,
pulseSpeed: 0.4 + Math.random() * 0.5,
pulseOffset: Math.random() * Math.PI * 2,
opacity: 0.15 + Math.random() * 0.1
});
}
}

function draw(time: number) {
if (!ctx || !canvas) return;

ctx.clearRect(0, 0, canvas.width, canvas.height);

// Draw connection lines between nearby orbs
for (let i = 0; i < orbs.length; i++) {
for (let j = i + 1; j < orbs.length; j++) {
const dx = orbs[i].x - orbs[j].x;
const dy = orbs[i].y - orbs[j].y;
const distance = Math.sqrt(dx * dx + dy * dy);

if (distance < connectionDistance) {
const opacity = (1 - distance / connectionDistance) * 0.04;
ctx.beginPath();
ctx.moveTo(orbs[i].x, orbs[i].y);
ctx.lineTo(orbs[j].x, orbs[j].y);
ctx.strokeStyle = `hsla(180, 50%, 60%, ${opacity})`;
ctx.lineWidth = 0.5;
ctx.stroke();
}
}
}

// Update and draw orbs
orbs.forEach(orb => {
// Update position with drift
orb.x += orb.vx;
orb.y += orb.vy;

// Bounce off edges with smooth transition
if (orb.x < -orb.radius) orb.x = canvas.width + orb.radius;
if (orb.x > canvas.width + orb.radius) orb.x = -orb.radius;
if (orb.y < -orb.radius) orb.y = canvas.height + orb.radius;
if (orb.y > canvas.height + orb.radius) orb.y = -orb.radius;

// Pulsing effect
const pulse = Math.sin(time * 0.001 * orb.pulseSpeed + orb.pulseOffset);
const currentRadius = orb.radius * (1 + pulse * 0.1);
const currentOpacity = orb.opacity * (0.8 + pulse * 0.2);

// Draw glow
const gradient = ctx.createRadialGradient(
orb.x, orb.y, 0,
orb.x, orb.y, currentRadius * 2
);
gradient.addColorStop(0, orb.color.replace(')', `, ${currentOpacity * 0.3})`).replace('hsl', 'hsla'));
gradient.addColorStop(0.5, orb.color.replace(')', `, ${currentOpacity * 0.1})`).replace('hsl', 'hsla'));
gradient.addColorStop(1, orb.color.replace(')', ', 0)').replace('hsl', 'hsla'));

ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(orb.x, orb.y, currentRadius * 2, 0, Math.PI * 2);
ctx.fill();

// Draw core orb
const coreGradient = ctx.createRadialGradient(
orb.x, orb.y, 0,
orb.x, orb.y, currentRadius
);
coreGradient.addColorStop(0, orb.color.replace(')', `, ${currentOpacity * 0.5})`).replace('hsl', 'hsla'));
coreGradient.addColorStop(1, orb.color.replace(')', `, ${currentOpacity * 0.15})`).replace('hsl', 'hsla'));

ctx.fillStyle = coreGradient;
ctx.beginPath();
ctx.arc(orb.x, orb.y, currentRadius, 0, Math.PI * 2);
ctx.fill();
});

animationId = requestAnimationFrame(draw);
}

function handleResize() {
if (!canvas) return;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
initOrbs();
}

onMount(() => {
ctx = canvas.getContext('2d');
handleResize();

window.addEventListener('resize', handleResize);

animationId = requestAnimationFrame(draw);
});

onDestroy(() => {
if (browser) {
if (animationId) window.cancelAnimationFrame(animationId);
window.removeEventListener('resize', handleResize);
}
});
</script>

<canvas
bind:this={canvas}
class="fixed inset-0 pointer-events-none z-0"
aria-hidden="true"
></canvas>

<style>
canvas {
background: transparent;
}
</style>
175 changes: 114 additions & 61 deletions src/routes/about/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,91 +1,144 @@
<script lang="ts">
import { Card, CardHeader, CardTitle, CardContent } from "$lib/components/ui/card/index.js"; //
import { flyAndScale } from "$lib/utils"; //
import { UserCircle, Briefcase, Terminal } from "lucide-svelte"; // Using UserCircle and Briefcase for semantic icons
import { Card, CardHeader, CardTitle, CardContent } from "$lib/components/ui/card/index.js";
import { Badge } from "$lib/components/ui/badge";
import { flyAndScale } from "$lib/utils";
import FloatingOrbs from "$lib/components/FloatingOrbs.svelte";
import {
UserCircle,
Laptop,
Server,
Wrench,
Music,
Lightbulb,
Zap
} from "lucide-svelte";

const startDate = new Date('2003-06-11');

const yearsSince = (): number => {
const now = new Date();
let years = now.getFullYear() - startDate.getFullYear();

const monthDiff = now.getMonth() - startDate.getMonth();
const dayDiff = now.getDate() - startDate.getDate();

if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) {
years--;
}

return years;
};

const interests = [
{
category: "Technologie",
icon: Laptop,
color: "text-blue-500",
items: ["Développement web", "Serveurs", "Réseaux", "Automatisation"]
},
{
category: "Hardware",
icon: Wrench,
color: "text-orange-500",
items: ["Architecture serveur", "Assemblage", "Optimisation", "Diagnostic"]
},
{
category: "Électronique",
icon: Zap,
color: "text-yellow-500",
items: ["Circuits", "IoT", "Domotique", "Prototypage"]
},
{
category: "Créativité",
icon: Music,
color: "text-purple-500",
items: ["Musique", "Conception 3D", "Projets DIY", "Expérimentation"]
}
];
</script>

<svelte:head>
<title>Az' - À propos de moi</title>
<meta name="description" content="Apprenez-en plus sur moi" />
<title>Az' - À propos de moi</title>
<meta name="description" content="Passionné de technologie et créateur autodidacte" />
</svelte:head>

<style lang="postcss">
/* Adjust min-height to account for your global header/navbar and this page's footer if they have fixed heights */
.min-h-screen_minus_header_footer {
min-height: calc(100vh - theme(spacing.20) - theme(spacing.10)); /* Example: 5rem header, 2.5rem footer */
/* You might need to adjust these values based on your actual header and footer heights */
}
.blinking-cursor {
animation: blink 1s step-end infinite;
min-height: calc(100vh - theme(spacing.20) - theme(spacing.10));
}

@keyframes blink {
from,
to {
color: transparent;
}
50% {
color: inherit;
}
.gradient-text {
background: linear-gradient(135deg, hsl(var(--primary)) 0%, hsl(var(--primary) / 0.6) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
</style>

<div class="container mx-auto px-4 py-8 md:py-12 min-h-screen_minus_header_footer">
<div class="text-center mb-12 md:mb-16" in:flyAndScale={{ y: -40, duration: 450, start: 0.7 }}>
<h1 class="text-4xl sm:text-5xl md:text-6xl font-bold tracking-tight text-foreground flex items-center justify-center">
<UserCircle class="w-10 h-10 md:w-12 md:h-12 mr-3 text-primary" />
À propos de moi
<FloatingOrbs />

<div class="container mx-auto px-4 py-8 md:py-12 min-h-screen_minus_header_footer max-w-4xl relative z-10">
<!-- Header -->
<div class="text-center mb-12" in:flyAndScale={{ y: -40, duration: 450, start: 0.7 }}>
<div class="inline-flex items-center justify-center w-20 h-20 rounded-full bg-primary/10 mb-4">
<UserCircle class="w-12 h-12 text-primary" />
</div>
<h1 class="text-4xl md:text-5xl font-bold tracking-tight mb-3">
<span class="gradient-text">Dylan &ldquo;Az'&rdquo; R.</span>
</h1>
<p class="mt-3 text-lg md:text-xl text-muted-foreground">
Un peu plus d'informations sur qui je suis et ce qui me passionne.
<p class="text-lg md:text-xl text-muted-foreground">
{yearsSince()} ans • Belgique
</p>
</div>

<div class="space-y-16 md:space-y-24">
<section in:flyAndScale|global={{ y: 50, duration: 400, start: 0.75 }}>
<div class="flex items-center mb-6">
<Terminal class="h-10 w-10 mr-3 text-primary" />
<h2 class="text-4xl md:text-5xl font-bold text-foreground">Qui je suis</h2>
<span class="text-4xl md:text-5xl font-bold text-primary blinking-cursor ml-1">_</span>
</div>
<Card class="shadow-lg transition-all duration-300 hover:shadow-xl hover:-translate-y-1 hover:scale-[1.005]">
<CardContent class="p-6 md:p-8">
<p class="text-xl md:text-2xl text-muted-foreground leading-relaxed">
Je suis Dylan R. (ou Az' sur internet), {yearsSince()} ans, passionné de technologie. Je passe mon temps à cultiver ma passion en tant qu'autodidacte au travers de plus en plus de domaines.
</p>
</CardContent>
</Card>
</section>
<!-- Bio -->
<section class="mb-12" in:flyAndScale|global={{ y: 50, duration: 400, start: 0.75, delay: 120 }}>
<Card class="shadow-lg transition-all duration-300 hover:shadow-xl hover:-translate-y-1">
<CardContent class="p-6 md:p-8">
<div class="flex items-start gap-3 mb-4">
<Lightbulb class="w-6 h-6 text-primary flex-shrink-0 mt-1" />
<div class="space-y-3 text-base md:text-lg text-muted-foreground leading-relaxed">
<p>
Passionné de technologie depuis toujours, j'ai construit mon expertise en autodidacte.
Ce qui me motive, c'est comprendre comment les choses fonctionnent et créer des solutions.
</p>
<p>
Je gère des infrastructures serveurs, développe des applications web, et expérimente avec l'électronique et la domotique.
Chaque projet est une occasion d'apprendre et de repousser mes limites.
</p>
<p>
Au-delà de la technique, j'aime la musique et les projets créatifs qui mélangent différents domaines.
</p>
</div>
</div>
</CardContent>
</Card>
</section>

<section in:flyAndScale|global={{ y: 50, duration: 400, start: 0.75 }}>
<div class="flex items-center mb-6">
<Terminal class="h-10 w-10 mr-3 text-primary" />
<h2 class="text-4xl md:text-5xl font-bold text-foreground">Ce que je fais</h2>
<span class="text-4xl md:text-5xl font-bold text-primary blinking-cursor ml-1">_</span>
</div>
<Card class="shadow-lg transition-all duration-300 hover:shadow-xl hover:-translate-y-1 hover:scale-[1.005]">
<CardContent class="p-6 md:p-8">
<p class="text-xl md:text-2xl text-muted-foreground leading-relaxed">
Je cultive mes connaissances depuis de nombreuses années dans plusieurs domaines tels que le développement, le "hardware", ou la gestion de serveurs
informatiques, mais aussi dans d'autres domaines plus éloignés de l'informatique comme l'électronique ou la musique.
</p>
</CardContent>
</Card>
</section>
</div>
<!-- Interests -->
<section in:flyAndScale|global={{ y: 50, duration: 400, start: 0.75, delay: 240 }}>
<h2 class="text-2xl md:text-3xl font-bold text-foreground mb-6 flex items-center gap-3">
<Server class="h-7 w-7 text-primary" />
Centres d'intérêt
</h2>

<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
{#each interests as interest}
<Card class="shadow-lg transition-all duration-300 hover:shadow-xl hover:-translate-y-1 hover:border-primary/30">
<CardHeader class="pb-3">
<CardTitle class="flex items-center gap-2 text-lg">
<svelte:component this={interest.icon} class="w-5 h-5 {interest.color}" />
<span>{interest.category}</span>
</CardTitle>
</CardHeader>
<CardContent>
<div class="flex flex-wrap gap-2">
{#each interest.items as item}
<Badge variant="secondary" class="text-sm">
{item}
</Badge>
{/each}
</div>
</CardContent>
</Card>
{/each}
</div>
</section>
</div>