feat: añadir carta de amor interactiva (index.html)
This commit is contained in:
1 parent
8277ae08a4
commit
667eca0f46
1 file changed
+588
+588
@@ -0,0 +1,588 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Para ti, mi cielo ❤️</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Alex+Brush&family=Cormorant+Garamond:ital,wght@0,400;0,600;1,400&family=Montserrat:wght@300;400;600&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg-dark: #0f070c;
|
||||||
|
--bg-card: rgba(28, 14, 23, 0.75);
|
||||||
|
--accent-rose: #ff4d79;
|
||||||
|
--accent-gold: #f5d089;
|
||||||
|
--accent-soft: #ffb8c9;
|
||||||
|
--text-main: #fcecef;
|
||||||
|
--text-muted: #d9a8b5;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: radial-gradient(circle at 50% 20%, #2a081a 0%, var(--bg-dark) 75%);
|
||||||
|
color: var(--text-main);
|
||||||
|
font-family: 'Montserrat\, sans-serif;
|
||||||
|
min-height: 100vh;
|
||||||
|
overflow-x: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ambientCanvas {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
max-width: 860px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 40px 20px 80px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 35px;
|
||||||
|
animation: fadeInDown 1.4s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-romantic {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: rgba(255, 77, 121, 0.12);
|
||||||
|
border: 1px solid rgba(255, 77, 121, 0.3);
|
||||||
|
padding: 6px 18px;
|
||||||
|
border-radius: 30px;
|
||||||
|
font-size: 13px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--accent-soft);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-title {
|
||||||
|
font-family: 'Alex Brush\, cursive;
|
||||||
|
font-size: clamp(3rem, 8vw, 4.8rem);
|
||||||
|
color: var(--accent-gold);
|
||||||
|
text-shadow: 0 0 25px rgba(245, 208, 137, 0.4);
|
||||||
|
line-height: 1.1;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-family: 'Cormorant Garamond\, serif;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.envelope-section {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 580px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
perspective: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.envelope-card {
|
||||||
|
background: linear-gradient(145deg, #2b1120, #1a0813);
|
||||||
|
border: 1px solid rgba(255, 184, 201, 0.25);
|
||||||
|
border-radius: 20px;
|
||||||
|
box-shadow: 0 25px 60px rgba(0, 0, 0, 0.7), 0 0 40px rgba(255, 77, 121, 0.15);
|
||||||
|
padding: 45px 30px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
|
}
|
||||||
|
|
||||||
|
.envelope-card:hover {
|
||||||
|
transform: translateY(-6px) scale(1.01);
|
||||||
|
box-shadow: 0 30px 70px rgba(0, 0, 0, 0.8), 0 0 50px rgba(255, 77, 121, 0.3);
|
||||||
|
border-color: rgba(245, 208, 137, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wax-seal {
|
||||||
|
width: 82px;
|
||||||
|
height: 82px;
|
||||||
|
background: radial-gradient(circle at 35% 35%, #ff4d79, #99002a);
|
||||||
|
border-radius: 50%;
|
||||||
|
margin: 0 auto 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 8px 25px rgba(255, 0, 77, 0.4), inset 0 2px 4px rgba(255, 255, 255, 0.4);
|
||||||
|
position: relative;
|
||||||
|
animation: pulse 2.2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wax-seal svg {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
fill: #fff;
|
||||||
|
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
|
||||||
|
}
|
||||||
|
|
||||||
|
.envelope-title {
|
||||||
|
font-family: 'Cormorant Garamond\, serif;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
color: var(--accent-gold);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.envelope-hint {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-open {
|
||||||
|
margin-top: 22px;
|
||||||
|
display: inline-block;
|
||||||
|
background: linear-gradient(135deg, #ff4d79, #c7104b);
|
||||||
|
color: #fff;
|
||||||
|
padding: 12px 30px;
|
||||||
|
border-radius: 30px;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 6px 20px rgba(255, 77, 121, 0.35);
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-open:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
box-shadow: 0 8px 30px rgba(255, 77, 121, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-container {
|
||||||
|
display: none;
|
||||||
|
width: 100%;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid rgba(255, 184, 201, 0.2);
|
||||||
|
border-radius: 24px;
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
padding: clamp(30px, 6vw, 60px);
|
||||||
|
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.8), 0 0 60px rgba(255, 77, 121, 0.15);
|
||||||
|
animation: unfold 0.9s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-corner-decor {
|
||||||
|
position: absolute;
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
border: 2px solid var(--accent-gold);
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.corner-tl { top: 18px; left: 18px; border-right: none; border-bottom: none; border-top-left-radius: 8px; }
|
||||||
|
.corner-tr { top: 18px; right: 18px; border-left: none; border-bottom: none; border-top-right-radius: 8px; }
|
||||||
|
.corner-bl { bottom: 18px; left: 18px; border-right: none; border-top: none; border-bottom-left-radius: 8px; }
|
||||||
|
.corner-br { bottom: 18px; right: 18px; border-left: none; border-top: none; border-bottom-right-radius: 8px; }
|
||||||
|
|
||||||
|
.letter-salutation {
|
||||||
|
font-family: 'Alex Brush\, cursive;
|
||||||
|
font-size: 3rem;
|
||||||
|
color: var(--accent-gold);
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-body {
|
||||||
|
font-family: 'Cormorant Garamond\, serif;
|
||||||
|
font-size: 1.35rem;
|
||||||
|
line-height: 1.85;
|
||||||
|
color: #fae8ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-body p {
|
||||||
|
margin-bottom: 22px;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-body strong {
|
||||||
|
color: var(--accent-gold);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-highlight {
|
||||||
|
background: rgba(255, 77, 121, 0.1);
|
||||||
|
border-left: 3px solid var(--accent-rose);
|
||||||
|
padding: 16px 22px;
|
||||||
|
border-radius: 0 12px 12px 0;
|
||||||
|
margin: 25px 0;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.letter-farewell {
|
||||||
|
margin-top: 35px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-role {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-name {
|
||||||
|
font-family: 'Alex Brush\, cursive;
|
||||||
|
font-size: 2.8rem;
|
||||||
|
color: var(--accent-gold);
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reasons-title {
|
||||||
|
font-family: 'Cormorant Garamond\, serif;
|
||||||
|
font-size: 2.2rem;
|
||||||
|
color: var(--accent-gold);
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reasons-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reason-card {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
border: 1px solid rgba(255, 184, 201, 0.15);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 24px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reason-card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
background: rgba(255, 77, 121, 0.08);
|
||||||
|
border-color: rgba(255, 77, 121, 0.4);
|
||||||
|
box-shadow: 0 10px 30px rgba(255, 77, 121, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.reason-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reason-heading {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
color: var(--accent-gold);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-family: 'Cormorant Garamond\, serif;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reason-desc {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promise-box {
|
||||||
|
background: radial-gradient(circle at center, rgba(255, 77, 121, 0.18), transparent 70%);
|
||||||
|
border: 1px dashed rgba(245, 208, 137, 0.4);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 35px;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 650px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.promise-text {
|
||||||
|
font-family: 'Cormorant Garamond\, serif;
|
||||||
|
font-style: italic;
|
||||||
|
font-size: 1.45rem;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.interactive-heart-btn {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
font-size: 2.2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
filter: drop-shadow(0 0 10px rgba(255, 77, 121, 0.8));
|
||||||
|
transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||||
|
}
|
||||||
|
|
||||||
|
.interactive-heart-btn:hover {
|
||||||
|
transform: scale(1.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.click-hint {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-top: 6px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.06); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInDown {
|
||||||
|
from { opacity: 0; transform: translateY(-25px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes unfold {
|
||||||
|
0% { opacity: 0; transform: scaleY(0.7) translateY(-30px); }
|
||||||
|
100% { opacity: 1; transform: scaleY(1) translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.burst-heart {
|
||||||
|
position: fixed;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 9999;
|
||||||
|
animation: floatUp 1.4s ease-out forwards;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes floatUp {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(-50%, -50%) scale(0.6) rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(-50%, -150px) scale(1.4) rotate(25deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<canvas id="ambientCanvas"></canvas>
|
||||||
|
|
||||||
|
<div class="main-container">
|
||||||
|
|
||||||
|
<header class="hero-header">
|
||||||
|
<div class="tag-romantic">
|
||||||
|
<span>✨</span> Una dedicatoria infinita <span>✨</span>
|
||||||
|
</div>
|
||||||
|
<h1 class="main-title">Para Nena, con todo mi amor</h1>
|
||||||
|
<p class="subtitle">Porque no hay rincón en este universo donde no te elija a ti, mi cielo.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="envelope-section" id="envelopeWrapper">
|
||||||
|
<div class="envelope-card" id="envelopeCard" onclick="openLoveLetter()">
|
||||||
|
<div class="wax-seal">
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<h2 class="envelope-title">Tienes un mensaje guardado en el corazón</h2>
|
||||||
|
<p class="envelope-hint">Toca el sello para abrir la carta</p>
|
||||||
|
<button class="btn-open">Abrir carta, nena 💌</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<article class="letter-container" id="loveLetter">
|
||||||
|
<div class="letter-corner-decor corner-tl"></div>
|
||||||
|
<div class="letter-corner-decor corner-tr"></div>
|
||||||
|
<div class="letter-corner-decor corner-bl"></div>
|
||||||
|
<div class="letter-corner-decor corner-br"></div>
|
||||||
|
|
||||||
|
<div class="letter-salutation">Mi cielo, mi Nena...</div>
|
||||||
|
|
||||||
|
<div class="letter-body">
|
||||||
|
<p>
|
||||||
|
Hay personas que llegan a la vida de uno para cambiarlo todo sin hacer ruido, y luego estás tú, que iluminaste cada esquina de mi mundo desde el primer segundo en que te cruzaste en mi camino.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Quería escribirte esto para recordarte lo inmensamente importante que eres para mí. A veces las palabras cotidianas se quedan cortas frente a todo lo que siento cuando me miras, cuando te ríes o cuando simplemente te tengo cerquita. Eres mi refugio, mi mayor alegría y esa paz tan bonita que solo encuentro cuando estoy a tu lado.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="letter-highlight">
|
||||||
|
«Si tuviera que volver a vivir mi vida mil veces, en cada una de ellas buscaría tus ojos hasta encontrarte otra vez, nena.»
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Gracias por ser mi apoyo incondicional, por tus abrazos que reconstruyen cualquier día difícil, y por hacerme sonreír incluso cuando no me doy cuenta. Eres la compañera más dulce, inteligente, preciosa y auténtica que jamás pude soñar.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Prometo cuidarte, escucharte, hacerte reír todos los días y recordarte siempre lo increíble que eres. Eres mi cielo entero, mi hogar y mi mayor orgullo. Te amo con todo lo que soy y mucho más de lo que alcanzo a explicarte.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="letter-farewell">
|
||||||
|
<p class="signature-role">Siempre tuyo,</p>
|
||||||
|
<div class="signature-name">Tu mayor admirador ❤️</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<h3 class="reasons-title">Razones por las que eres única, cielo</h3>
|
||||||
|
|
||||||
|
<div class="reasons-grid">
|
||||||
|
<div class="reason-card">
|
||||||
|
<div class="reason-icon">✨</div>
|
||||||
|
<div class="reason-heading">Tu Sonrisa Mágica</div>
|
||||||
|
<p class="reason-desc">Tienes esa risa contagiosa que ilumina cualquier habitación y arregla en un segundo cualquier mal día.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="reason-card">
|
||||||
|
<div class="reason-icon">💫</div>
|
||||||
|
<div class="reason-heading">Tu Ternura y Fuerza</div>
|
||||||
|
<p class="reason-desc">La delicadeza con la que cuidas cada detalle y la fortaleza con la que persigues lo que sueñas me fascinan.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="reason-card">
|
||||||
|
<div class="reason-icon">🕊️</div>
|
||||||
|
<div class="reason-heading">La Paz de tu Abrazo</div>
|
||||||
|
<p class="reason-desc">No hay lugar más seguro ni más cálido en todo el planeta que estar rodeado por tus brazos, mi nena.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="reason-card">
|
||||||
|
<div class="reason-icon">🌟</div>
|
||||||
|
<div class="reason-heading">Nuestras Complicidades</div>
|
||||||
|
<p class="reason-desc">Las miradas donde nos decimos todo sin hablar, los chistes nuestros y el saber que siempre somos un equipo.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="promise-box">
|
||||||
|
<p class="promise-text">"El amor no se cuenta en horas o días, se mide en cada latido compartido contigo."</p>
|
||||||
|
<button class="interactive-heart-btn" onclick="spawnHeartBurst(event)" title="Pulsa para enviar amor">💖</button>
|
||||||
|
<div class="click-hint">Toca el corazón tantas veces como quieras para enviarle amor a Nena</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function openLoveLetter() {
|
||||||
|
const envelope = document.getElementById('envelopeWrapper');
|
||||||
|
const letter = document.getElementById('loveLetter');
|
||||||
|
|
||||||
|
envelope.style.transition = 'all 0.6s ease';
|
||||||
|
envelope.style.opacity = '0';
|
||||||
|
envelope.style.transform = 'scale(0.85) translateY(-30px)';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
envelope.style.display = 'none';
|
||||||
|
letter.style.display = 'block';
|
||||||
|
letter.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
|
||||||
|
for (let i = 0; i < 18; i++) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const x = window.innerWidth / 2 + (Math.random() * 260 - 130);
|
||||||
|
const y = window.innerHeight / 2 + (Math.random() * 200 - 100);
|
||||||
|
createFloatingHeart(x, y);
|
||||||
|
}, i * 70);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
if (!e.target.closest('#envelopeCard') && !e.target.closest('.interactive-heart-btn')) {
|
||||||
|
createFloatingHeart(e.clientX, e.clientY);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function spawnHeartBurst(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
const rect = e.target.getBoundingClientRect();
|
||||||
|
const cx = rect.left + rect.width / 2;
|
||||||
|
const cy = rect.top + rect.height / 2;
|
||||||
|
|
||||||
|
for (let i = 0; i < 12; i++) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const offsetX = (Math.random() - 0.5) * 80;
|
||||||
|
const offsetY = (Math.random() - 0.5) * 50;
|
||||||
|
createFloatingHeart(cx + offsetX, cy + offsetY);
|
||||||
|
}, i * 40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFloatingHeart(x, y) {
|
||||||
|
const heart = document.createElement('div');
|
||||||
|
heart.className = 'burst-heart';
|
||||||
|
const emojis = ['💖', '💕', '✨', '🌹', '❤️', '🥰'];
|
||||||
|
heart.textContent = emojis[Math.floor(Math.random() * emojis.length)];
|
||||||
|
heart.style.left = x + 'px';
|
||||||
|
heart.style.top = y + 'px';
|
||||||
|
document.body.appendChild(heart);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
heart.remove();
|
||||||
|
}, 1400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvas = document.getElementById('ambientCanvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
let width, height;
|
||||||
|
const particles = [];
|
||||||
|
|
||||||
|
function resizeCanvas() {
|
||||||
|
width = canvas.width = window.innerWidth;
|
||||||
|
height = canvas.height = window.innerHeight;
|
||||||
|
}
|
||||||
|
window.addEventListener('resize', resizeCanvas);
|
||||||
|
resizeCanvas();
|
||||||
|
|
||||||
|
class Particle {
|
||||||
|
constructor() {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
reset() {
|
||||||
|
this.x = Math.random() * width;
|
||||||
|
this.y = Math.random() * height + height;
|
||||||
|
this.size = Math.random() * 3 + 1.5;
|
||||||
|
this.speedY = Math.random() * 0.8 + 0.3;
|
||||||
|
this.speedX = Math.sin(Math.random() * 3) * 0.5;
|
||||||
|
this.opacity = Math.random() * 0.6 + 0.2;
|
||||||
|
this.hue = Math.random() > 0.4 ? 345 : 45;
|
||||||
|
}
|
||||||
|
update() {
|
||||||
|
this.y -= this.speedY;
|
||||||
|
this.x += Math.sin(this.y * 0.005) * 0.4;
|
||||||
|
if (this.y < -10) this.reset();
|
||||||
|
}
|
||||||
|
draw() {
|
||||||
|
ctx.fillStyle = `hsla(${this.hue}, 80%, 75%, ${this.opacity})`;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 45; i++) {
|
||||||
|
particles.push(new Particle());
|
||||||
|
}
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
particles.forEach(p => {
|
||||||
|
p.update();
|
||||||
|
p.draw();
|
||||||
|
});
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
animate();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||