change snowflakes

This commit is contained in:
Nomi Nonsense (Nonszy) 2025-12-26 17:55:35 +07:00
parent 32e609ad71
commit 1215f2f987
7 changed files with 106 additions and 67 deletions

View File

@ -6,8 +6,7 @@ const nextConfig = {
remotePatterns: [
{
protocol: 'https',
hostname: 'media.tenor.com',
pathname: '/1BCeG1aTiBAAAAAd/temptation-stairway-ena.gif'
hostname: 'media.tenor.com'
}
]
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -191,7 +191,7 @@ export const FakeRelativeWindow = ({
{/* Main window */}
<div
className={clsx("md:border bg-background border-primary", withAnim && animation.window)}
className={clsx("md:border bg-background bg-opacity-50 border-primary", withAnim && animation.window)}
style={{
transform: `translate(${currentWindow.offset.x}px, ${currentWindow.offset.y}px)`
}}

View File

@ -2,7 +2,7 @@
import { getEvent } from '@/lib/utils';
import clsx from 'clsx';
import Image from 'next/image';
import NextImage from 'next/image';
import { ReactNode, useEffect, useRef, useState } from 'react';
interface Snowflake {
@ -13,12 +13,15 @@ interface Snowflake {
sway: number;
swayOffset: number;
opacity: number;
rotation: number;
rotationSpeed: number;
}
interface ChristmasProps {
left: number;
top: number;
size: number;
img: string;
className?: string;
flip?: boolean;
absolute?: boolean;
@ -31,9 +34,10 @@ export const ChristmasExclusive = ({ children }: { children: ReactNode }) => {
return null;
}
export const ChristmasHat: React.FC<ChristmasProps> = ({
export const ChristmasProperty: React.FC<ChristmasProps> = ({
left,
top,
img,
size,
className,
flip
@ -46,12 +50,13 @@ export const ChristmasHat: React.FC<ChristmasProps> = ({
left, top
}}
>
<Image
<NextImage
className='pointer-events-none'
src={'/images/event_santahat01.png'}
alt='Santa Hat'
src={img}
alt='Christmas prop'
height={size}
width={size}
unoptimized={img.slice(img.length - 3, img.length) == 'gif'}
/>
</div>
</ChristmasExclusive>
@ -63,8 +68,8 @@ function SnowfallRawBackground() {
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const snowflakesRef = useRef<Snowflake[]>([]);
const animationFrameRef = useRef<number>(0);
const imageRef = useRef<HTMLImageElement | null>(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"
/>
);
}

View File

@ -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
}) => (
<div className="relative md:bg-background mx-auto w-[480px] md:w-[520px] md:border border-primary z-10">
<ChristmasHat size={180} left={-60} top={-80} />
<ChristmasProperty
img="/images/event_santahat1.png"
size={180}
left={-60}
top={-80}
/>
<div className="p-1 pb-0">
<div className="hidden md:flex bg-primary p-2 justify-between text-background">
<div className="ms-1 pointer-events-none">
@ -48,7 +53,7 @@ export const HomeWindows = () => (
>
<FloatingLabel placeholder="This is Nola, my OC :3">
<div className="relative">
<ChristmasHat size={150} top={-40} left={70} flip />
<ChristmasProperty img="/images/event_santahat1.png" size={150} top={-40} left={70} flip />
<Image
className=""
alt="Nola"
@ -93,13 +98,13 @@ export const HomeWindows = () => (
/>
</FakeRelativeWindow>
<FakeRelativeWindow
windowText="coral_cupcake.mkv"
windowText="ena_spin.obj"
className="-right-[85%] top-[440px] z-10"
draggable
>
<Image
alt="Coral Cupcake"
src="https://media1.tenor.com/m/N5K-4AWj8QcAAAAC/coral-glasses-cupcake.gif"
src="https://media.tenor.com/Uv-PLe5GIe0AAAAi/gyaruface.gif"
width={240}
height={200}
quality={10}