diff --git a/next.config.mjs b/next.config.mjs
index 62a752a..962044b 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -6,8 +6,7 @@ const nextConfig = {
remotePatterns: [
{
protocol: 'https',
- hostname: 'media.tenor.com',
- pathname: '/1BCeG1aTiBAAAAAd/temptation-stairway-ena.gif'
+ hostname: 'media.tenor.com'
}
]
},
diff --git a/public/images/event_santahat01.png b/public/images/event_santahat01.png
deleted file mode 100644
index 02d94ec..0000000
Binary files a/public/images/event_santahat01.png and /dev/null differ
diff --git a/public/images/event_santahat1.png b/public/images/event_santahat1.png
new file mode 100644
index 0000000..4705618
Binary files /dev/null and b/public/images/event_santahat1.png differ
diff --git a/public/images/event_snowflake.png b/public/images/event_snowflake.png
new file mode 100644
index 0000000..2d60ac5
Binary files /dev/null and b/public/images/event_snowflake.png differ
diff --git a/src/components/client-windows.tsx b/src/components/client-windows.tsx
index 47c30de..06c23f6 100644
--- a/src/components/client-windows.tsx
+++ b/src/components/client-windows.tsx
@@ -191,7 +191,7 @@ export const FakeRelativeWindow = ({
{/* Main window */}
{
return null;
}
-export const ChristmasHat: React.FC = ({
+export const ChristmasProperty: React.FC = ({
left,
top,
+ img,
size,
className,
flip
@@ -46,12 +50,13 @@ export const ChristmasHat: React.FC = ({
left, top
}}
>
-
@@ -63,8 +68,8 @@ function SnowfallRawBackground() {
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const snowflakesRef = useRef([]);
const animationFrameRef = useRef(0);
+ const imageRef = useRef(null);
- // Update dimensions on resize
useEffect(() => {
const updateDimensions = () => {
setDimensions({
@@ -80,76 +85,105 @@ function SnowfallRawBackground() {
}, []);
useEffect(() => {
- if (!canvasRef.current) return;
-
const canvas = canvasRef.current;
+ if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
canvas.width = dimensions.width;
canvas.height = dimensions.height;
- const createSnowflake = (init?: boolean): Snowflake => {
- let posy = -10;
- let radius = Math.random() * 4 + 2;
- let speed = Math.random() * 1 + 0.5;
- let sway = Math.random() * 0.5 + 0.2;
+ // Load the snowflake image using a plain Image so we can draw it to canvas reliably
+ const img = new Image();
+ img.src = '/images/event_snowflake.png';
+ let mounted = true;
- if (init) {
- const initHeight = (dimensions.height / 1.1);
- posy = Math.random() * initHeight + posy;
- radius = (posy / (initHeight + posy) - 1) * -radius;
- }
+ const start = () => {
+ if (!mounted) return;
+ imageRef.current = img;
- if (window.innerWidth < 768) {
- radius /= 1.4;
- }
+ const createSnowflake = (init?: boolean): Snowflake => {
+ let posy = -10;
+ let radius = Math.random() * 18 + 10;
+ let speed = Math.random() * 1 + 0.5;
+ let sway = Math.random() * 0.5 + 0.2;
- return {
- x: Math.random() * dimensions.width,
- y: posy,
- radius,
- speed,
- sway,
- swayOffset: Math.random() * Math.PI * 2,
- opacity: Math.random() * 0.5 + 0.5,
- }
- };
-
-
- const snowflakes: Snowflake[] = [];
- for (let i = 0; i < 200; i++) {
- snowflakes.push(createSnowflake(true));
- }
- snowflakesRef.current = snowflakes;
-
- const animate = () => {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
-
- snowflakesRef.current.forEach((flake, index) => {
- flake.y += flake.speed;
- flake.x += Math.sin(flake.swayOffset + flake.y * 0.02) * flake.sway;
-
- const shrink = dimensions.height < 768 ? 0.001 : 0.005
- flake.radius = Math.max(0, flake.radius - shrink);
-
- if (flake.y > dimensions.height || flake.radius <= 0.5) {
- snowflakesRef.current[index] = createSnowflake();
- return;
+ if (init) {
+ const initHeight = (dimensions.height / 1.1);
+ posy = Math.random() * initHeight + posy;
+ radius = (posy / (initHeight + posy) - 1) * -radius;
}
- ctx.beginPath();
- ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2);
- ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity})`;
- ctx.fill();
- });
+ if (window.innerWidth < 768) {
+ radius /= 1.4;
+ }
+
+ return {
+ x: Math.random() * dimensions.width,
+ y: posy,
+ radius,
+ speed,
+ sway,
+ swayOffset: Math.random() * Math.PI * 2,
+ opacity: Math.random() * 0.5 + 0.2,
+ rotation: Math.random() * Math.PI * 2,
+ rotationSpeed: (Math.random() - 0.5) * 0.02,
+ }
+ };
+
+ const snowflakes: Snowflake[] = [];
+ for (let i = 0; i < 80; i++) {
+ snowflakes.push(createSnowflake(true));
+ }
+ snowflakesRef.current = snowflakes;
+
+ const animate = () => {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ snowflakesRef.current.forEach((flake, index) => {
+ flake.y += flake.speed;
+ flake.x += Math.sin(flake.swayOffset + flake.y * 0.02) * flake.sway;
+ flake.rotation += flake.rotationSpeed;
+
+ const shrink = dimensions.height < 768 ? 0.005 : 0.01;
+ flake.radius = Math.max(2, flake.radius - shrink);
+
+ if (flake.y > dimensions.height || flake.radius <= 0.5) {
+ snowflakesRef.current[index] = createSnowflake();
+ return;
+ }
+
+ ctx.save();
+ ctx.globalAlpha = flake.opacity;
+ ctx.translate(flake.x, flake.y);
+ ctx.rotate(flake.rotation);
+
+ ctx.drawImage(
+ img,
+ -flake.radius / 2,
+ -flake.radius / 2,
+ flake.radius,
+ flake.radius
+ );
+
+ ctx.restore();
+ });
+
+ animationFrameRef.current = requestAnimationFrame(animate);
+ };
animationFrameRef.current = requestAnimationFrame(animate);
};
- animationFrameRef.current = requestAnimationFrame(animate);
+ if (img.complete) start();
+ else img.onload = start;
- return () => cancelAnimationFrame(animationFrameRef.current);
+ return () => {
+ mounted = false;
+ cancelAnimationFrame(animationFrameRef.current);
+ img.onload = null;
+ imageRef.current = null;
+ };
}, [dimensions]);
return (
@@ -157,6 +191,7 @@ function SnowfallRawBackground() {
ref={canvasRef}
className="fixed top-0 left-0 w-full h-full pointer-events-none"
style={{ zIndex: 0 }}
+ aria-hidden="true"
/>
);
}
diff --git a/src/components/windows.tsx b/src/components/windows.tsx
index b60c13c..e3cfcc3 100644
--- a/src/components/windows.tsx
+++ b/src/components/windows.tsx
@@ -3,7 +3,7 @@ import { FloatingLabel } from "@/components/floating-label";
import { FakeRelativeWindow, RestoreWindowsButton } from "./client-windows";
import Image from "next/image"
import Link from "next/link";
-import { ChristmasHat } from "./events/christmas";
+import { ChristmasProperty } from "./events/christmas";
export const FakeWindow = ({
windowText, children
@@ -12,7 +12,12 @@ export const FakeWindow = ({
children: React.ReactNode
}) => (
-
+
@@ -48,7 +53,7 @@ export const HomeWindows = () => (
>
-
+
(
/>