Add christmas events
This commit is contained in:
parent
4760f51095
commit
29d251459a
16
package-lock.json
generated
16
package-lock.json
generated
@ -13,7 +13,7 @@
|
||||
"@next/mdx": "^15.3.4",
|
||||
"@types/mdx": "^2.0.13",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"date-fns": "^4.1.0",
|
||||
"next": "^14.2.35",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
@ -2112,11 +2112,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.19",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
|
||||
"integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
|
||||
"license": "MIT"
|
||||
"node_modules/date-fns": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
"@next/mdx": "^15.3.4",
|
||||
"@types/mdx": "^2.0.13",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"date-fns": "^4.1.0",
|
||||
"next": "^14.2.35",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
|
||||
BIN
public/images/event_santahat01.png
Normal file
BIN
public/images/event_santahat01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
@ -6,10 +6,12 @@ import HomeText from "@/components/texts/home.mdx"
|
||||
import Link from "@/components/link";
|
||||
import { FakeWindow, HomeWindows } from "@/components/windows";
|
||||
import { WindowManagerProvider } from "@/hooks/window-manager";
|
||||
import { SnowfallBackground } from "@/components/events/christmas";
|
||||
|
||||
export default function Home() {
|
||||
return (<>
|
||||
<main className="flex items-center pt-16 md:pt-24 pb-12 px-8 md:px-0 overflow-x-hidden">
|
||||
<SnowfallBackground />
|
||||
<WindowManagerProvider>
|
||||
<FakeWindow windowText="Homepage">
|
||||
<HomeWindows />
|
||||
|
||||
168
src/components/events/christmas.tsx
Normal file
168
src/components/events/christmas.tsx
Normal file
@ -0,0 +1,168 @@
|
||||
'use client'
|
||||
|
||||
import { getEvent } from '@/lib/utils';
|
||||
import clsx from 'clsx';
|
||||
import Image from 'next/image';
|
||||
import { ReactNode, useEffect, useRef, useState } from 'react';
|
||||
|
||||
interface Snowflake {
|
||||
x: number;
|
||||
y: number;
|
||||
radius: number;
|
||||
speed: number;
|
||||
sway: number;
|
||||
swayOffset: number;
|
||||
opacity: number;
|
||||
}
|
||||
|
||||
interface ChristmasProps {
|
||||
left: number;
|
||||
top: number;
|
||||
size: number;
|
||||
className?: string;
|
||||
flip?: boolean;
|
||||
absolute?: boolean;
|
||||
}
|
||||
|
||||
export const ChristmasExclusive = ({ children }: { children: ReactNode }) => {
|
||||
const isItChristmas = getEvent()?.name == 'christmas';
|
||||
|
||||
if (isItChristmas) return children;
|
||||
return null;
|
||||
}
|
||||
|
||||
export const ChristmasHat: React.FC<ChristmasProps> = ({
|
||||
left,
|
||||
top,
|
||||
size,
|
||||
className,
|
||||
flip
|
||||
}) => {
|
||||
return (
|
||||
<ChristmasExclusive>
|
||||
<div
|
||||
className={clsx('absolute md:block hidden', flip && '-scale-x-100', className)}
|
||||
style={{
|
||||
left, top
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
className='pointer-events-none'
|
||||
src={'/images/event_santahat01.png'}
|
||||
alt='Santa Hat'
|
||||
height={size}
|
||||
width={size}
|
||||
/>
|
||||
</div>
|
||||
</ChristmasExclusive>
|
||||
)
|
||||
}
|
||||
|
||||
function SnowfallRawBackground() {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
||||
const snowflakesRef = useRef<Snowflake[]>([]);
|
||||
const animationFrameRef = useRef<number>(0);
|
||||
|
||||
// Update dimensions on resize
|
||||
useEffect(() => {
|
||||
const updateDimensions = () => {
|
||||
setDimensions({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
};
|
||||
|
||||
updateDimensions();
|
||||
window.addEventListener('resize', updateDimensions);
|
||||
|
||||
return () => window.removeEventListener('resize', updateDimensions);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvasRef.current) return;
|
||||
|
||||
const canvas = canvasRef.current;
|
||||
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;
|
||||
|
||||
if (init) {
|
||||
const initHeight = (dimensions.height / 1.1);
|
||||
posy = Math.random() * initHeight + posy;
|
||||
radius = (posy / (initHeight + posy) - 1) * -radius;
|
||||
}
|
||||
|
||||
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.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;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2);
|
||||
ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity})`;
|
||||
ctx.fill();
|
||||
});
|
||||
|
||||
animationFrameRef.current = requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
animationFrameRef.current = requestAnimationFrame(animate);
|
||||
|
||||
return () => cancelAnimationFrame(animationFrameRef.current);
|
||||
}, [dimensions]);
|
||||
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="fixed top-0 left-0 w-full h-full pointer-events-none"
|
||||
style={{ zIndex: 0 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export const SnowfallBackground = () => (
|
||||
<ChristmasExclusive>
|
||||
<SnowfallRawBackground />
|
||||
</ChristmasExclusive>
|
||||
)
|
||||
1
src/components/events/eventprops.tsx
Normal file
1
src/components/events/eventprops.tsx
Normal file
@ -0,0 +1 @@
|
||||
'use client'
|
||||
@ -3,6 +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";
|
||||
|
||||
export const FakeWindow = ({
|
||||
windowText, children
|
||||
@ -10,7 +11,8 @@ export const FakeWindow = ({
|
||||
windowText: string
|
||||
children: React.ReactNode
|
||||
}) => (
|
||||
<div className="mx-auto w-[480px] md:w-[520px] md:border border-primary">
|
||||
<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} />
|
||||
<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">
|
||||
@ -45,6 +47,8 @@ export const HomeWindows = () => (
|
||||
draggable
|
||||
>
|
||||
<FloatingLabel placeholder="This is Nola, my OC :3">
|
||||
<div className="relative">
|
||||
<ChristmasHat size={150} top={-40} left={70} flip />
|
||||
<Image
|
||||
className=""
|
||||
alt="Nola"
|
||||
@ -52,6 +56,7 @@ export const HomeWindows = () => (
|
||||
width={200}
|
||||
height={200}
|
||||
/>
|
||||
</div>
|
||||
</FloatingLabel>
|
||||
</FakeRelativeWindow>
|
||||
<FakeRelativeWindow
|
||||
|
||||
5
src/lib/types.ts
Normal file
5
src/lib/types.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export type EventsDate = {
|
||||
name: string,
|
||||
start: number[],
|
||||
end: number[]
|
||||
}
|
||||
@ -1,8 +1,29 @@
|
||||
'use client'
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import { format, isWithinInterval } from "date-fns";
|
||||
import type { EventsDate } from "./types";
|
||||
|
||||
const events: EventsDate[] = [
|
||||
{
|
||||
name: 'christmas',
|
||||
start: [12, 20],
|
||||
end: [12, 30]
|
||||
},
|
||||
]
|
||||
|
||||
export function getExpirationDate () {
|
||||
const expirationDate = new Date("2026-02-15");
|
||||
return dayjs(expirationDate).format("dddd, D MMMM YYYY");
|
||||
return format(expirationDate, "dddd, D MMMM YYYY");
|
||||
}
|
||||
|
||||
export function getEvent (): EventsDate | undefined {
|
||||
return events.find((e) => {
|
||||
const today = new Date(Date.now());
|
||||
const year = today.getFullYear();
|
||||
|
||||
const start = new Date(year, e.start[0] - 1, e.start[1]);
|
||||
const end = new Date(year, e.end[0] - 1, e.end[1]);
|
||||
|
||||
return isWithinInterval(today, { start, end })
|
||||
})
|
||||
}
|
||||
14
yarn.lock
14
yarn.lock
@ -320,10 +320,10 @@
|
||||
dependencies:
|
||||
source-map "^0.7.0"
|
||||
|
||||
"@next/swc-linux-x64-gnu@14.2.33":
|
||||
"@next/swc-win32-x64-msvc@14.2.33":
|
||||
version "14.2.33"
|
||||
resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz"
|
||||
integrity sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==
|
||||
resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz"
|
||||
integrity sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
@ -1003,10 +1003,10 @@ data-view-byte-offset@^1.0.1:
|
||||
es-errors "^1.3.0"
|
||||
is-data-view "^1.0.1"
|
||||
|
||||
dayjs@^1.11.13:
|
||||
version "1.11.19"
|
||||
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz"
|
||||
integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==
|
||||
date-fns@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz"
|
||||
integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==
|
||||
|
||||
debug@^3.2.7:
|
||||
version "3.2.7"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user