imnya.ng/src/components/DraggableWindow.tsx
2026-03-06 22:30:35 +09:00

144 lines
4.8 KiB
TypeScript

'use client';
import { useState, useRef, useEffect } from 'react';
import Image from 'next/image';
import { useIsMobile } from '@/hooks/use-mobile';
export default function DraggableWindow() {
const isMobile = useIsMobile();
const [isVisible, setIsVisible] = useState(true);
const [position, setPosition] = useState({ x: 100, y: 100 });
const [isDragging, setIsDragging] = useState(false);
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
const [isNadaeVisible, setIsNadaeVisible] = useState(false);
const windowRef = useRef<HTMLDivElement>(null);
const audioRef = useRef<HTMLAudioElement | null>(null);
const nadaeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const handleMouseDown = (e: React.MouseEvent) => {
setIsDragging(true);
if (windowRef.current) {
const rect = windowRef.current.getBoundingClientRect();
setDragOffset({
x: e.clientX - rect.left,
y: e.clientY - rect.top,
});
}
};
const handleActivate = () => {
if (!audioRef.current) {
audioRef.current = new Audio('/audio.opus');
}
audioRef.current.currentTime = 0;
void audioRef.current.play();
setIsNadaeVisible(true);
if (nadaeTimeoutRef.current) {
clearTimeout(nadaeTimeoutRef.current);
}
nadaeTimeoutRef.current = setTimeout(() => {
setIsNadaeVisible(false);
nadaeTimeoutRef.current = null;
}, 1000);
};
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
if (!isDragging) return;
setPosition({
x: e.clientX - dragOffset.x,
y: e.clientY - dragOffset.y,
});
};
const handleMouseUp = () => {
setIsDragging(false);
};
if (isDragging) {
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
}
return () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
}, [isDragging, dragOffset]);
useEffect(() => {
return () => {
audioRef.current?.pause();
audioRef.current = null;
if (nadaeTimeoutRef.current) {
clearTimeout(nadaeTimeoutRef.current);
}
};
}, []);
return (
isMobile ? (
<figure className="mb-8 w-full h-auto">
<button
type="button"
className="block w-full rounded-xl text-left"
>
<picture className="block bg-gray-100 rounded-xl aspect-3-2 overflow-hidden image-scale object-shadowed">
<Image
src={'/full.webp'}
alt="Banner"
width={1200}
height={400}
priority
className="object-cover object-center transition-transform duration-300 hover:scale-105"
/>
</picture>
</button>
</figure >
) : (
isVisible && (
<div
ref={windowRef}
className="fixed cursor-pointer select-none"
style={{
left: `${position.x}px`,
top: `${position.y}px`,
}}
>
<div className="relative w-fit h-fit">
<Image
src={isNadaeVisible ? '/image.png' : '/window.webp'}
alt="Draggable Window"
width={500}
height={400}
priority
draggable={false}
/>
<button
type="button"
onMouseDown={handleMouseDown}
onClick={handleActivate}
className="absolute inset-0 cursor-pointer"
aria-label="Play audio"
/>
<button
type="button"
onClick={(e) => {
e.stopPropagation();
setIsVisible(false);
}}
className="absolute top-1 right-2 z-10 w-5 h-5 cursor-pointer"
aria-label="Close window"
/>
</div>
</div>
)
)
);
}