47 lines
1.3 KiB
TypeScript
47 lines
1.3 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useState } from "react";
|
||
import { Heart } from "lucide-react";
|
||
import { cn } from "@/lib/cn";
|
||
|
||
interface FloatingVoteButtonProps {
|
||
onClick: () => void;
|
||
/** 显示前的滚动阈值(px) */
|
||
threshold?: number;
|
||
className?: string;
|
||
}
|
||
|
||
export default function FloatingVoteButton({
|
||
onClick,
|
||
threshold = 300,
|
||
className,
|
||
}: FloatingVoteButtonProps) {
|
||
const [visible, setVisible] = useState(false);
|
||
|
||
useEffect(() => {
|
||
const handler = () => setVisible(window.scrollY > threshold);
|
||
handler();
|
||
window.addEventListener("scroll", handler, { passive: true });
|
||
return () => window.removeEventListener("scroll", handler);
|
||
}, [threshold]);
|
||
|
||
return (
|
||
<button
|
||
type="button"
|
||
onClick={onClick}
|
||
aria-label="立即投票"
|
||
className={cn(
|
||
"fixed bottom-6 right-6 sm:bottom-8 sm:right-8 z-40 w-14 h-14 rounded-full bg-grad-purple text-white flex flex-col items-center justify-center font-display text-[9px] tracking-widest shadow-purple-glow animate-pulse-glow transition-all",
|
||
visible
|
||
? "opacity-100 translate-y-0 pointer-events-auto"
|
||
: "opacity-0 translate-y-3 pointer-events-none",
|
||
"hover:scale-105",
|
||
className,
|
||
)}
|
||
>
|
||
<Heart size={16} fill="white" className="mb-0.5" />
|
||
<span>VOTE</span>
|
||
</button>
|
||
);
|
||
}
|