Created
December 27, 2024 18:57
-
-
Save akrolsmir/84fbe363e23b40a877cc0a9bf89dfe1e to your computer and use it in GitHub Desktop.
christmas card but too much code to include
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useState, useEffect } from 'react' | |
type Snowflake = { | |
id: number | |
x: number | |
y: number | |
size: number | |
speed: number | |
opacity: number | |
rotation: number | |
path: string | |
} | |
const generateSnowflakePath = (size: number) => { | |
const points = [] | |
const pattern = Math.floor(Math.random() * 4) // 4 different pattern types | |
const branchCount = pattern === 3 ? 8 : 6 // Some snowflakes have 8 branches | |
const hasInnerBranches = Math.random() > 0.3 | |
for (let i = 0; i < branchCount; i++) { | |
const angle = (i * 2 * Math.PI) / branchCount | |
const mainX = Math.cos(angle) * size | |
const mainY = Math.sin(angle) * size | |
points.push(`M 0 0 L ${mainX} ${mainY}`) | |
// Vary branch styles based on pattern | |
const branchLength = size * (0.2 + Math.random() * 0.4) | |
const branchAngle1 = angle + Math.PI / (3 + Math.random() * 3) | |
const branchAngle2 = angle - Math.PI / (3 + Math.random() * 3) | |
const midX = Math.cos(angle) * (size * 0.5) | |
const midY = Math.sin(angle) * (size * 0.5) | |
if (pattern === 0 || pattern === 2) { | |
points.push(`M ${midX} ${midY} L ${midX + Math.cos(branchAngle1) * branchLength} ${midY + Math.sin(branchAngle1) * branchLength}`) | |
points.push(`M ${midX} ${midY} L ${midX + Math.cos(branchAngle2) * branchLength} ${midY + Math.sin(branchAngle2) * branchLength}`) | |
} | |
if (hasInnerBranches && (pattern === 1 || pattern === 2)) { | |
const innerX = Math.cos(angle) * (size * 0.3) | |
const innerY = Math.sin(angle) * (size * 0.3) | |
points.push(`M ${innerX} ${innerY} L ${innerX + Math.cos(angle + Math.PI/3) * branchLength*0.5} ${innerY + Math.sin(angle + Math.PI/3) * branchLength*0.5}`) | |
points.push(`M ${innerX} ${innerY} L ${innerX + Math.cos(angle - Math.PI/3) * branchLength*0.5} ${innerY + Math.sin(angle - Math.PI/3) * branchLength*0.5}`) | |
} | |
} | |
return points.join(' ') | |
} | |
export default function Component() { | |
const [snowflakes, setSnowflakes] = useState<Snowflake[]>([]) | |
useEffect(() => { | |
const createSnowflakes = () => { | |
const flakes: Snowflake[] = [] | |
for (let i = 0; i < 150; i++) { | |
const size = Math.random() * 8 + 6 | |
flakes.push({ | |
id: i, | |
x: Math.random() * window.innerWidth, | |
y: Math.random() * window.innerHeight, | |
size: size, | |
speed: Math.random() * 2 + 1, | |
opacity: Math.random() * 0.8 + 0.5, | |
rotation: Math.random() * 360, | |
path: generateSnowflakePath(size / 2) | |
}) | |
} | |
return flakes | |
} | |
let animationFrameId: number | |
const updateSnowflakes = () => { | |
setSnowflakes(prev => prev.map(flake => ({ | |
...flake, | |
y: flake.y > window.innerHeight ? -10 : flake.y + flake.speed, | |
x: flake.x + Math.sin(flake.y / 30) * 0.3, | |
rotation: flake.rotation + 0.5 | |
}))) | |
animationFrameId = requestAnimationFrame(updateSnowflakes) | |
} | |
setSnowflakes(createSnowflakes()) | |
updateSnowflakes() | |
return () => cancelAnimationFrame(animationFrameId) | |
}, []) | |
return ( | |
<div className="min-h-screen bg-red-800 p-4 flex items-center justify-center"> | |
{/* Snow overlay */} | |
<div className="fixed inset-0 pointer-events-none" style={{ zIndex: 0 }}> | |
{snowflakes.map(flake => ( | |
<svg | |
key={flake.id} | |
className="absolute" | |
style={{ | |
left: `${flake.x}px`, | |
top: `${flake.y}px`, | |
width: `${flake.size}px`, | |
height: `${flake.size}px`, | |
opacity: flake.opacity, | |
transform: `translateZ(0) rotate(${flake.rotation}deg)`, | |
filter: 'blur(0.3px)' | |
}}> | |
<path | |
d={flake.path} | |
stroke="white" | |
strokeWidth="1" | |
fill="none" | |
transform={`translate(${flake.size/2},${flake.size/2})`} | |
/> | |
</svg> | |
))} | |
</div> | |
<div className="relative w-full max-w-2xl bg-white rounded-lg shadow-xl overflow-hidden" style={{ zIndex: 1 }}> | |
{/* Card content */} | |
<div className="relative p-8 text-center"> | |
<div className="text-red-800 font-bold text-4xl mb-4" | |
style={{ fontFamily: 'cursive' }}> | |
Merry Christmas | |
</div> | |
<div className="text-3xl text-green-700 mb-6" | |
style={{ fontFamily: 'cursive' }}> | |
from Austin, Rachel & baby Ada | |
</div> | |
{/* Retro border decoration */} | |
<div className="border-4 border-double border-red-800 p-6 mb-6"> | |
<h2 className="text-2xl font-bold text-red-800 mb-4"> | |
Our 2024 Highlights | |
</h2> | |
<div className="text-left space-y-3 text-gray-700"> | |
<p>π Welcomed our precious Ada into the world</p> | |
<p>π Settled into our new home</p> | |
<p>βοΈ Baby's first trip to visit grandparents</p> | |
<p>π Ada's first Christmas!</p> | |
</div> | |
</div> | |
<div className="mt-8 text-2xl text-green-700" | |
style={{ fontFamily: 'cursive' }}> | |
Wishing you joy and peace in 2025! | |
</div> | |
{/* Retro decorative elements */} | |
<div className="absolute top-4 left-4"> | |
<div className="text-4xl">π</div> | |
</div> | |
<div className="absolute top-4 right-4"> | |
<div className="text-4xl">π</div> | |
</div> | |
<div className="absolute bottom-4 left-4"> | |
<div className="text-4xl">β</div> | |
</div> | |
<div className="absolute bottom-4 right-4"> | |
<div className="text-4xl">β</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment