change snowflakes
This commit is contained in:
parent
32e609ad71
commit
1215f2f987
@ -6,8 +6,7 @@ const nextConfig = {
|
|||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
hostname: 'media.tenor.com',
|
hostname: 'media.tenor.com'
|
||||||
pathname: '/1BCeG1aTiBAAAAAd/temptation-stairway-ena.gif'
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 55 KiB |
BIN
public/images/event_santahat1.png
Normal file
BIN
public/images/event_santahat1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
BIN
public/images/event_snowflake.png
Normal file
BIN
public/images/event_snowflake.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
@ -191,7 +191,7 @@ export const FakeRelativeWindow = ({
|
|||||||
|
|
||||||
{/* Main window */}
|
{/* Main window */}
|
||||||
<div
|
<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={{
|
style={{
|
||||||
transform: `translate(${currentWindow.offset.x}px, ${currentWindow.offset.y}px)`
|
transform: `translate(${currentWindow.offset.x}px, ${currentWindow.offset.y}px)`
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { getEvent } from '@/lib/utils';
|
import { getEvent } from '@/lib/utils';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import Image from 'next/image';
|
import NextImage from 'next/image';
|
||||||
import { ReactNode, useEffect, useRef, useState } from 'react';
|
import { ReactNode, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
interface Snowflake {
|
interface Snowflake {
|
||||||
@ -13,12 +13,15 @@ interface Snowflake {
|
|||||||
sway: number;
|
sway: number;
|
||||||
swayOffset: number;
|
swayOffset: number;
|
||||||
opacity: number;
|
opacity: number;
|
||||||
|
rotation: number;
|
||||||
|
rotationSpeed: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChristmasProps {
|
interface ChristmasProps {
|
||||||
left: number;
|
left: number;
|
||||||
top: number;
|
top: number;
|
||||||
size: number;
|
size: number;
|
||||||
|
img: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
flip?: boolean;
|
flip?: boolean;
|
||||||
absolute?: boolean;
|
absolute?: boolean;
|
||||||
@ -31,9 +34,10 @@ export const ChristmasExclusive = ({ children }: { children: ReactNode }) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChristmasHat: React.FC<ChristmasProps> = ({
|
export const ChristmasProperty: React.FC<ChristmasProps> = ({
|
||||||
left,
|
left,
|
||||||
top,
|
top,
|
||||||
|
img,
|
||||||
size,
|
size,
|
||||||
className,
|
className,
|
||||||
flip
|
flip
|
||||||
@ -46,12 +50,13 @@ export const ChristmasHat: React.FC<ChristmasProps> = ({
|
|||||||
left, top
|
left, top
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<NextImage
|
||||||
className='pointer-events-none'
|
className='pointer-events-none'
|
||||||
src={'/images/event_santahat01.png'}
|
src={img}
|
||||||
alt='Santa Hat'
|
alt='Christmas prop'
|
||||||
height={size}
|
height={size}
|
||||||
width={size}
|
width={size}
|
||||||
|
unoptimized={img.slice(img.length - 3, img.length) == 'gif'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ChristmasExclusive>
|
</ChristmasExclusive>
|
||||||
@ -63,8 +68,8 @@ function SnowfallRawBackground() {
|
|||||||
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
||||||
const snowflakesRef = useRef<Snowflake[]>([]);
|
const snowflakesRef = useRef<Snowflake[]>([]);
|
||||||
const animationFrameRef = useRef<number>(0);
|
const animationFrameRef = useRef<number>(0);
|
||||||
|
const imageRef = useRef<HTMLImageElement | null>(null);
|
||||||
|
|
||||||
// Update dimensions on resize
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const updateDimensions = () => {
|
const updateDimensions = () => {
|
||||||
setDimensions({
|
setDimensions({
|
||||||
@ -80,18 +85,26 @@ function SnowfallRawBackground() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!canvasRef.current) return;
|
|
||||||
|
|
||||||
const canvas = canvasRef.current;
|
const canvas = canvasRef.current;
|
||||||
|
if (!canvas) return;
|
||||||
const ctx = canvas.getContext('2d');
|
const ctx = canvas.getContext('2d');
|
||||||
if (!ctx) return;
|
if (!ctx) return;
|
||||||
|
|
||||||
canvas.width = dimensions.width;
|
canvas.width = dimensions.width;
|
||||||
canvas.height = dimensions.height;
|
canvas.height = dimensions.height;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
const start = () => {
|
||||||
|
if (!mounted) return;
|
||||||
|
imageRef.current = img;
|
||||||
|
|
||||||
const createSnowflake = (init?: boolean): Snowflake => {
|
const createSnowflake = (init?: boolean): Snowflake => {
|
||||||
let posy = -10;
|
let posy = -10;
|
||||||
let radius = Math.random() * 4 + 2;
|
let radius = Math.random() * 18 + 10;
|
||||||
let speed = Math.random() * 1 + 0.5;
|
let speed = Math.random() * 1 + 0.5;
|
||||||
let sway = Math.random() * 0.5 + 0.2;
|
let sway = Math.random() * 0.5 + 0.2;
|
||||||
|
|
||||||
@ -112,13 +125,14 @@ function SnowfallRawBackground() {
|
|||||||
speed,
|
speed,
|
||||||
sway,
|
sway,
|
||||||
swayOffset: Math.random() * Math.PI * 2,
|
swayOffset: Math.random() * Math.PI * 2,
|
||||||
opacity: Math.random() * 0.5 + 0.5,
|
opacity: Math.random() * 0.5 + 0.2,
|
||||||
|
rotation: Math.random() * Math.PI * 2,
|
||||||
|
rotationSpeed: (Math.random() - 0.5) * 0.02,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const snowflakes: Snowflake[] = [];
|
const snowflakes: Snowflake[] = [];
|
||||||
for (let i = 0; i < 200; i++) {
|
for (let i = 0; i < 80; i++) {
|
||||||
snowflakes.push(createSnowflake(true));
|
snowflakes.push(createSnowflake(true));
|
||||||
}
|
}
|
||||||
snowflakesRef.current = snowflakes;
|
snowflakesRef.current = snowflakes;
|
||||||
@ -129,27 +143,47 @@ function SnowfallRawBackground() {
|
|||||||
snowflakesRef.current.forEach((flake, index) => {
|
snowflakesRef.current.forEach((flake, index) => {
|
||||||
flake.y += flake.speed;
|
flake.y += flake.speed;
|
||||||
flake.x += Math.sin(flake.swayOffset + flake.y * 0.02) * flake.sway;
|
flake.x += Math.sin(flake.swayOffset + flake.y * 0.02) * flake.sway;
|
||||||
|
flake.rotation += flake.rotationSpeed;
|
||||||
|
|
||||||
const shrink = dimensions.height < 768 ? 0.001 : 0.005
|
const shrink = dimensions.height < 768 ? 0.005 : 0.01;
|
||||||
flake.radius = Math.max(0, flake.radius - shrink);
|
flake.radius = Math.max(2, flake.radius - shrink);
|
||||||
|
|
||||||
if (flake.y > dimensions.height || flake.radius <= 0.5) {
|
if (flake.y > dimensions.height || flake.radius <= 0.5) {
|
||||||
snowflakesRef.current[index] = createSnowflake();
|
snowflakesRef.current[index] = createSnowflake();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.save();
|
||||||
ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2);
|
ctx.globalAlpha = flake.opacity;
|
||||||
ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity})`;
|
ctx.translate(flake.x, flake.y);
|
||||||
ctx.fill();
|
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);
|
animationFrameRef.current = requestAnimationFrame(animate);
|
||||||
|
};
|
||||||
|
|
||||||
return () => cancelAnimationFrame(animationFrameRef.current);
|
if (img.complete) start();
|
||||||
|
else img.onload = start;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
mounted = false;
|
||||||
|
cancelAnimationFrame(animationFrameRef.current);
|
||||||
|
img.onload = null;
|
||||||
|
imageRef.current = null;
|
||||||
|
};
|
||||||
}, [dimensions]);
|
}, [dimensions]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -157,6 +191,7 @@ function SnowfallRawBackground() {
|
|||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
className="fixed top-0 left-0 w-full h-full pointer-events-none"
|
className="fixed top-0 left-0 w-full h-full pointer-events-none"
|
||||||
style={{ zIndex: 0 }}
|
style={{ zIndex: 0 }}
|
||||||
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { FloatingLabel } from "@/components/floating-label";
|
|||||||
import { FakeRelativeWindow, RestoreWindowsButton } from "./client-windows";
|
import { FakeRelativeWindow, RestoreWindowsButton } from "./client-windows";
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { ChristmasHat } from "./events/christmas";
|
import { ChristmasProperty } from "./events/christmas";
|
||||||
|
|
||||||
export const FakeWindow = ({
|
export const FakeWindow = ({
|
||||||
windowText, children
|
windowText, children
|
||||||
@ -12,7 +12,12 @@ export const FakeWindow = ({
|
|||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}) => (
|
}) => (
|
||||||
<div className="relative md:bg-background mx-auto w-[480px] md:w-[520px] md:border border-primary z-10">
|
<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="p-1 pb-0">
|
||||||
<div className="hidden md:flex bg-primary p-2 justify-between text-background">
|
<div className="hidden md:flex bg-primary p-2 justify-between text-background">
|
||||||
<div className="ms-1 pointer-events-none">
|
<div className="ms-1 pointer-events-none">
|
||||||
@ -48,7 +53,7 @@ export const HomeWindows = () => (
|
|||||||
>
|
>
|
||||||
<FloatingLabel placeholder="This is Nola, my OC :3">
|
<FloatingLabel placeholder="This is Nola, my OC :3">
|
||||||
<div className="relative">
|
<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
|
<Image
|
||||||
className=""
|
className=""
|
||||||
alt="Nola"
|
alt="Nola"
|
||||||
@ -93,13 +98,13 @@ export const HomeWindows = () => (
|
|||||||
/>
|
/>
|
||||||
</FakeRelativeWindow>
|
</FakeRelativeWindow>
|
||||||
<FakeRelativeWindow
|
<FakeRelativeWindow
|
||||||
windowText="coral_cupcake.mkv"
|
windowText="ena_spin.obj"
|
||||||
className="-right-[85%] top-[440px] z-10"
|
className="-right-[85%] top-[440px] z-10"
|
||||||
draggable
|
draggable
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
alt="Coral Cupcake"
|
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}
|
width={240}
|
||||||
height={200}
|
height={200}
|
||||||
quality={10}
|
quality={10}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user