"use client" import type React from "react" import { useState, useRef, useCallback, useEffect } from "react" import { defaultStars, type EsportsStar } from "../_lib/config-types" import { getStreamerAvatarUrl, getAvatarPlaceholderUrl } from "@/lib/streamer-avatar" // 游戏类型颜色 const gameColors: Record = { 王者荣耀: "#f59e0b", 英雄联盟: "#3b82f6", 魔兽世界: "#10b981", } const chinaProvinces = [ { id: "xinjiang", name: "新疆", d: "M85,95 L110,85 L145,80 L180,85 L210,95 L235,115 L250,145 L255,180 L250,215 L235,250 L210,275 L175,290 L140,295 L105,285 L75,265 L55,235 L45,200 L45,160 L55,125 L70,105 Z", }, { id: "xizang", name: "西藏", d: "M55,265 L105,285 L145,295 L180,300 L210,315 L225,345 L220,380 L200,410 L165,430 L125,435 L85,420 L55,390 L35,350 L35,305 Z", }, { id: "qinghai", name: "青海", d: "M210,215 L250,205 L285,210 L315,225 L330,255 L325,290 L305,320 L270,335 L235,330 L205,310 L190,280 L195,245 Z", }, { id: "gansu", name: "甘肃", d: "M285,165 L330,150 L375,145 L410,155 L435,175 L445,205 L440,240 L420,270 L385,290 L345,295 L310,280 L290,250 L285,215 Z", }, { id: "neimenggu", name: "内蒙古", d: "M330,55 L400,45 L470,40 L540,45 L600,60 L645,85 L665,120 L660,155 L635,185 L590,200 L535,205 L475,200 L415,190 L365,175 L335,150 L320,115 L320,80 Z", }, { id: "heilongjiang", name: "黑龙江", d: "M665,35 L720,25 L765,35 L795,60 L805,100 L795,140 L770,170 L730,185 L690,180 L660,155 L655,115 L660,75 Z", }, { id: "jilin", name: "吉林", d: "M665,165 L715,155 L755,165 L780,190 L780,225 L760,255 L720,270 L680,265 L655,240 L650,200 Z", }, { id: "liaoning", name: "辽宁", d: "M620,215 L680,200 L720,210 L750,235 L755,270 L740,300 L700,320 L655,320 L620,300 L605,265 L605,235 Z", }, { id: "hebei", name: "河北", d: "M545,150 L590,140 L625,150 L645,180 L645,215 L630,250 L595,275 L555,280 L525,265 L515,230 L520,190 Z", }, { id: "beijing", name: "北京", d: "M575,175 L595,170 L605,185 L595,200 L575,200 L568,188 Z", isCity: true }, { id: "tianjin", name: "天津", d: "M605,200 L620,195 L628,212 L618,228 L602,225 L598,212 Z", isCity: true }, { id: "shanxi", name: "山西", d: "M500,190 L545,175 L565,210 L560,255 L540,290 L505,300 L480,280 L475,240 L480,205 Z", }, { id: "shandong", name: "山东", d: "M580,245 L630,230 L680,235 L720,255 L735,285 L720,320 L680,340 L630,345 L590,330 L570,295 Z", }, { id: "henan", name: "河南", d: "M500,280 L555,265 L590,280 L600,320 L580,360 L540,380 L500,375 L475,345 L475,310 Z", }, { id: "jiangsu", name: "江苏", d: "M620,310 L680,295 L720,305 L740,340 L725,380 L685,400 L645,395 L615,370 L610,335 Z", }, { id: "anhui", name: "安徽", d: "M575,340 L620,325 L650,345 L660,385 L640,425 L600,440 L560,430 L545,390 L555,360 Z", }, { id: "shanghai", name: "上海", d: "M705,355 L725,350 L732,372 L718,385 L700,378 L698,362 Z", isCity: true }, { id: "zhejiang", name: "浙江", d: "M660,385 L710,370 L740,390 L745,435 L720,470 L680,480 L645,465 L640,425 Z" }, { id: "fujian", name: "福建", d: "M645,465 L695,450 L735,465 L750,510 L730,555 L685,570 L645,555 L630,510 Z" }, { id: "taiwan", name: "台湾", d: "M755,480 L780,470 L795,505 L785,550 L760,565 L745,540 L745,505 Z" }, { id: "jiangxi", name: "江西", d: "M575,420 L640,405 L670,435 L675,485 L650,530 L600,545 L560,525 L550,475 Z" }, { id: "hubei", name: "湖北", d: "M475,335 L540,320 L580,340 L590,385 L565,425 L520,440 L475,425 L460,385 Z" }, { id: "hunan", name: "湖南", d: "M475,430 L535,415 L575,435 L585,485 L560,535 L515,555 L470,540 L455,490 Z" }, { id: "guangdong", name: "广东", d: "M505,545 L575,530 L635,540 L680,560 L695,605 L665,645 L605,660 L545,650 L500,620 L485,580 Z", }, { id: "guangxi", name: "广西", d: "M395,545 L470,530 L520,545 L535,595 L510,640 L455,655 L400,640 L375,600 Z" }, { id: "hainan", name: "海南", d: "M485,680 L530,670 L545,710 L520,745 L480,750 L460,720 Z" }, { id: "sichuan", name: "四川", d: "M295,320 L365,300 L420,315 L455,355 L455,410 L430,455 L375,475 L320,460 L280,420 L275,365 Z", }, { id: "chongqing", name: "重庆", d: "M420,370 L470,355 L490,390 L480,435 L445,455 L410,445 L400,405 Z" }, { id: "guizhou", name: "贵州", d: "M375,455 L445,440 L485,460 L495,510 L470,555 L420,570 L375,555 L360,505 Z" }, { id: "yunnan", name: "云南", d: "M275,455 L345,440 L395,465 L410,525 L390,585 L340,620 L280,625 L235,590 L225,530 L240,480 Z", }, { id: "shaanxi", name: "陕西", d: "M420,210 L480,195 L510,230 L515,285 L495,335 L455,355 L415,345 L395,300 L400,250 Z", }, { id: "ningxia", name: "宁夏", d: "M400,195 L435,185 L450,215 L445,250 L425,275 L395,270 L385,235 Z" }, ] export default function ChinaMap() { const containerRef = useRef(null) const [scale, setScale] = useState(1) const [position, setPosition] = useState({ x: 0, y: 0 }) const [isDragging, setIsDragging] = useState(false) const [dragStart, setDragStart] = useState({ x: 0, y: 0 }) const [hoveredProvince, setHoveredProvince] = useState(null) const [hoveredStar, setHoveredStar] = useState(null) const [tooltipPos, setTooltipPos] = useState({ x: 0, y: 0 }) const [stars, setStars] = useState(defaultStars) /** 头像加载失败的主播 id,改用首字占位避免裂图 */ const [failedAvatarIds, setFailedAvatarIds] = useState>(new Set()) useEffect(() => { fetch("/api/screen/streamers") .then((res) => res.json()) .then((body: { success?: boolean; data?: EsportsStar[] }) => { if (body.success && Array.isArray(body.data) && body.data.length > 0) { setStars(body.data) } }) .catch(() => {}) }, []) // 将经纬度映射到SVG坐标系 const getStarPosition = useCallback((lng: number, lat: number) => { const x = ((lng - 73) / (135 - 73)) * 750 + 30 const y = ((54 - lat) / (54 - 18)) * 680 + 25 return { x, y } }, []) // 鼠标滚轮缩放 const handleWheel = useCallback((e: React.WheelEvent) => { e.preventDefault() const delta = e.deltaY > 0 ? 0.9 : 1.1 setScale((prev) => Math.min(Math.max(prev * delta, 0.5), 4)) }, []) // 拖拽事件 const handleMouseDown = useCallback( (e: React.MouseEvent) => { if (e.button === 0) { setIsDragging(true) setDragStart({ x: e.clientX - position.x, y: e.clientY - position.y }) } }, [position], ) const handleMouseMove = useCallback( (e: React.MouseEvent) => { if (isDragging) { setPosition({ x: e.clientX - dragStart.x, y: e.clientY - dragStart.y, }) } setTooltipPos({ x: e.clientX, y: e.clientY }) }, [isDragging, dragStart], ) const handleMouseUp = useCallback(() => { setIsDragging(false) }, []) // 触摸事件 const handleTouchStart = useCallback( (e: React.TouchEvent) => { if (e.touches.length === 1) { setIsDragging(true) setDragStart({ x: e.touches[0].clientX - position.x, y: e.touches[0].clientY - position.y, }) } }, [position], ) const handleTouchMove = useCallback( (e: React.TouchEvent) => { if (isDragging && e.touches.length === 1) { setPosition({ x: e.touches[0].clientX - dragStart.x, y: e.touches[0].clientY - dragStart.y, }) } }, [isDragging, dragStart], ) const handleTouchEnd = useCallback(() => { setIsDragging(false) }, []) const resetView = useCallback(() => { setScale(1) setPosition({ x: 0, y: 0 }) }, []) return (
{/* 地图标题和图例 */}

全国电竞明星分布

王者荣耀
英雄联盟
魔兽世界
{/* 缩放控制按钮 */}
{Math.round(scale * 100)}%
{/* SVG地图容器 */}
{/* 发光滤镜 */} {/* 省份高亮渐变 */} {/* 涟漪渐变 */} {/* 中国地图省份 */} {chinaProvinces.map((province) => ( setHoveredProvince(province.id)} onMouseLeave={() => setHoveredProvince(null)} /> ))} {/* 南海诸岛示意框 */} 南海诸岛 {/* 电竞明星标记(带头像)- 与后台主播管理数据互通 */} {stars.map((star, index) => { const pos = getStarPosition(star.location.lng, star.location.lat) const color = gameColors[star.game] || "#06b6d4" const rippleId = star.game === "王者荣耀" ? "ripple-yellow" : star.game === "英雄联盟" ? "ripple-blue" : "ripple-green" const avatarUrl = failedAvatarIds.has(star.id) ? getAvatarPlaceholderUrl(star.name, 32) : getStreamerAvatarUrl(star.name, star.avatar, 32) return ( setHoveredStar(star)} onMouseLeave={() => setHoveredStar(null)} > {/* 涟漪动画 */} {/* 外圈发光边框 */} {/* 头像背景 */} {/* 头像图片 */} setFailedAvatarIds((prev) => new Set(prev).add(star.id))} /> {/* 游戏类型小标识 */} ) })}
{/* 悬停省份名称 */} {hoveredProvince && (
{chinaProvinces.find((p) => p.id === hoveredProvince)?.name}
)} {/* 悬停明星信息卡片 */} {hoveredStar && (
{hoveredStar.name} { setFailedAvatarIds((prev) => new Set(prev).add(hoveredStar.id)) const t = e.target as HTMLImageElement if (t) t.src = getAvatarPlaceholderUrl(hoveredStar.name, 48) }} />
{hoveredStar.name}
{hoveredStar.location.province} · {hoveredStar.location.city}
{hoveredStar.game}
日收入: ¥{hoveredStar.dailyRevenue.toLocaleString()}
粉丝: {(hoveredStar.fans / 1000).toFixed(1)}K
转化率: {hoveredStar.conversionRate}%
直播平台: 抖音
)} {/* 操作提示 */}
滚轮缩放 · 拖拽移动
) }