146 lines
4.7 KiB
TypeScript
146 lines
4.7 KiB
TypeScript
"use client";
|
|
import Image from "next/image";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import Sidebar from "@/components/sidebar";
|
|
import { useIsMobile } from "@/hooks/use-mobile";
|
|
|
|
export default function Top() {
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const [randomBg, setRandomBg] = useState(1);
|
|
const isMobile = useIsMobile();
|
|
const [clickCount, setClickCount] = useState(0);
|
|
const clickTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
const [isMagicalGirlEnabled, setIsMagicalGirlEnabled] = useState(false);
|
|
const REQUIRED_CLICKS = 6;
|
|
|
|
useEffect(() => {
|
|
setRandomBg(Math.floor(Math.random() * 14) + 1);
|
|
setIsMagicalGirlEnabled(document.cookie.includes('MagicalGirl=true'));
|
|
}, []);
|
|
|
|
const requestPermission = async () => {
|
|
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
|
|
if (!isIOS)
|
|
window.addEventListener("deviceorientation", handleDeviceOrientation);
|
|
|
|
try {
|
|
const permission = await (DeviceOrientationEvent as unknown as { requestPermission: () => Promise<string> }).requestPermission();
|
|
if (permission === "granted") {
|
|
window.addEventListener("devicemotion", handleDeviceMotion);
|
|
window.addEventListener("deviceorientation", handleDeviceOrientation);
|
|
}
|
|
} catch {
|
|
window.addEventListener("deviceorientation", handleDeviceOrientation);
|
|
}
|
|
};
|
|
|
|
const handleDeviceOrientation = (event: DeviceOrientationEvent) => {
|
|
if (!containerRef.current) return;
|
|
const gamma = event.gamma || 0;
|
|
const beta = event.beta || 0;
|
|
|
|
const x = (gamma / 90) * -50;
|
|
const y = (beta / 180) * -50;
|
|
|
|
const bg = containerRef.current;
|
|
bg.style.backgroundPosition = `${50 + x}% ${50 + y}%`;
|
|
|
|
const img = bg.querySelector("img");
|
|
if (img) img.style.transform = `translate(${x * 2}px, ${y * 2}px)`;
|
|
};
|
|
|
|
const handleDeviceMotion = (event: DeviceMotionEvent) => {
|
|
if (!containerRef.current || !event.accelerationIncludingGravity) return;
|
|
const { x, y } = event.accelerationIncludingGravity;
|
|
if (x === null || y === null) return;
|
|
const bg = containerRef.current;
|
|
bg.style.backgroundPosition = `${50 - x * 3}% ${50 - y * 3}%`;
|
|
};
|
|
|
|
const handleBackgroundTouchClick = () => {
|
|
if (!isMobile) return;
|
|
|
|
setClickCount((prev) => {
|
|
const newCount = prev + 1;
|
|
|
|
// Clear existing timeout
|
|
if (clickTimeoutRef.current) {
|
|
clearTimeout(clickTimeoutRef.current);
|
|
}
|
|
|
|
// Check if required clicks reached
|
|
if (newCount >= REQUIRED_CLICKS) {
|
|
const newValue = !isMagicalGirlEnabled;
|
|
setIsMagicalGirlEnabled(newValue);
|
|
|
|
document.cookie = `MagicalGirl=${newValue}; path=/; max-age=${60 * 60 * 24 * 365}`;
|
|
window.location.reload();
|
|
return 0;
|
|
}
|
|
|
|
// Reset click count after 1 second if not enough clicks
|
|
clickTimeoutRef.current = setTimeout(() => {
|
|
setClickCount(0);
|
|
}, 1000);
|
|
|
|
return newCount;
|
|
});
|
|
};
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
window.removeEventListener("deviceorientation", handleDeviceOrientation);
|
|
window.removeEventListener("devicemotion", handleDeviceMotion);
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<section
|
|
id="top"
|
|
className="snap-start h-screen w-full relative"
|
|
// onWheel={(e) => {
|
|
// e.preventDefault();
|
|
// window.scrollBy(0, window.innerHeight * (e.deltaY > 0 ? 1 : -1));
|
|
// }}
|
|
>
|
|
<Sidebar />
|
|
<div
|
|
ref={containerRef}
|
|
className="absolute top-0 right-0 w-[calc(100%-100px)] h-screen lg:h-[calc(100vh-56px)] flex flex-col items-center justify-end lg:rounded-bl-[160px] bg-cover bg-center overflow-hidden"
|
|
style={{
|
|
backgroundImage: `url('/background/${randomBg}.avif')`,
|
|
}}
|
|
role="button"
|
|
tabIndex={0}
|
|
aria-label="Toggle MagicalGirl mode on mobile"
|
|
onClick={handleBackgroundTouchClick}
|
|
onMouseMove={(e) => {
|
|
const { clientX, clientY } = e;
|
|
const { innerWidth, innerHeight } = window;
|
|
const x = (clientX / innerWidth - 0.5) * -50;
|
|
const y = (clientY / innerHeight - 0.5) * -50;
|
|
|
|
const bg = e.currentTarget;
|
|
if (bg) {
|
|
bg.style.backgroundPosition = `${50 + x}% ${50 + y}%`;
|
|
}
|
|
const img = e.currentTarget.querySelector("img");
|
|
if (img) {
|
|
img.style.transform = `translate(${x}px, ${y}px)`;
|
|
}
|
|
}}
|
|
>
|
|
<Image
|
|
src={"/char.avif"}
|
|
alt="character"
|
|
width={4740}
|
|
height={7584}
|
|
className="w-[50vh] lg:w-[30vw] translate-y-[10%] transition-transform duration-100 ease-out"
|
|
unoptimized
|
|
onClick={requestPermission}
|
|
/>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|