imnya.ng/src/components/Top.tsx

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>
);
}