92 lines
3.2 KiB
TypeScript
92 lines
3.2 KiB
TypeScript
"use client";
|
||
|
||
import { useRef, useState } from "react";
|
||
import Link from "next/link";
|
||
import type { Destination } from "@/lib/types";
|
||
|
||
interface Props {
|
||
destinations: Destination[];
|
||
}
|
||
|
||
export default function SpinGlobe({ destinations }: Props) {
|
||
const [spinning, setSpinning] = useState(false);
|
||
const [result, setResult] = useState<Destination | null>(null);
|
||
const [display, setDisplay] = useState<Destination | null>(null);
|
||
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||
|
||
const spin = () => {
|
||
if (spinning || destinations.length === 0) return;
|
||
setSpinning(true);
|
||
setResult(null);
|
||
|
||
let ticks = 0;
|
||
const total = 18 + Math.floor(Math.random() * 8);
|
||
const winner = destinations[Math.floor(Math.random() * destinations.length)];
|
||
|
||
timerRef.current = setInterval(() => {
|
||
ticks++;
|
||
setDisplay(destinations[Math.floor(Math.random() * destinations.length)]);
|
||
if (ticks >= total) {
|
||
if (timerRef.current) clearInterval(timerRef.current);
|
||
setDisplay(winner);
|
||
setResult(winner);
|
||
setSpinning(false);
|
||
}
|
||
}, 90);
|
||
};
|
||
|
||
const shown = display || destinations[0];
|
||
|
||
return (
|
||
<section className="section spin-section" id="spin">
|
||
<div className="container">
|
||
<div className="section-header reveal">
|
||
<span className="section-tag">🎲 SPIN</span>
|
||
<h2>命运转盘 · 下一站去哪?</h2>
|
||
<p>不知道去哪?让命运帮你选一座游民城市</p>
|
||
</div>
|
||
|
||
<div className="spin-card reveal">
|
||
<div className={`spin-orb${spinning ? " spinning" : ""}`}>
|
||
<span className="spin-emoji">{shown?.emoji || "🌍"}</span>
|
||
<div className="spin-orb-ring" />
|
||
<div className="spin-orb-ring spin-orb-ring-2" />
|
||
</div>
|
||
|
||
<h3 className="spin-city-name">
|
||
{shown ? `${shown.name}, ${shown.country}` : "准备就绪"}
|
||
</h3>
|
||
{shown && !spinning && !result && (
|
||
<p className="spin-hint">点击下方按钮,开启随机旅居冒险</p>
|
||
)}
|
||
{spinning && <p className="spin-hint">正在穿越时区… ✈️</p>}
|
||
|
||
{result && !spinning && (
|
||
<div className="spin-result">
|
||
<p className="spin-result-tag">🎯 命运指引你去</p>
|
||
<div className="spin-result-meta">
|
||
<span>💰 ¥{result.cost.toLocaleString()}/月</span>
|
||
<span>📶 {result.speed}Mbps</span>
|
||
<span>⭐ {result.rating}</span>
|
||
</div>
|
||
<p className="spin-result-desc">{result.description}</p>
|
||
<div className="spin-result-actions">
|
||
<Link href={`/destinations/${result.slug}`} className="btn btn-primary">
|
||
查看详情 →
|
||
</Link>
|
||
<button className="btn btn-ghost" onClick={spin}>再转一次 🎲</button>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{!result && (
|
||
<button className="btn btn-primary spin-btn" onClick={spin} disabled={spinning}>
|
||
{spinning ? "旋转中…" : "🎲 转动命运转盘"}
|
||
</button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|