fix: 修复移动端报错

This commit is contained in:
Daniel
2026-03-03 11:14:34 +08:00
parent 4dd1f7e7dc
commit 7284a1a60d
15 changed files with 244 additions and 20 deletions

View File

@@ -9,6 +9,9 @@ import {
Rocket,
Asterisk,
Amphora,
Layers,
Sailboat,
Warehouse,
} from 'lucide-react'
import type { CombatLosses } from '@/data/mockData'
@@ -25,6 +28,9 @@ export function CombatLossesOtherPanel({ usLosses, iranLosses, className = '' }:
{ label: '战舰', icon: Ship, iconColor: 'text-blue-500', us: usLosses.warships, ir: iranLosses.warships },
{ label: '装甲', icon: Shield, iconColor: 'text-emerald-500', us: usLosses.armor, ir: iranLosses.armor },
{ label: '车辆', icon: Car, iconColor: 'text-slate-400', us: usLosses.vehicles, ir: iranLosses.vehicles },
{ label: '坦克', icon: Layers, iconColor: 'text-amber-600', us: usLosses.tanks ?? 0, ir: iranLosses.tanks ?? 0 },
{ label: '民船', icon: Sailboat, iconColor: 'text-cyan-400', us: usLosses.civilianShips ?? 0, ir: iranLosses.civilianShips ?? 0 },
{ label: '机/港', icon: Warehouse, iconColor: 'text-orange-400', us: usLosses.airportPort ?? 0, ir: iranLosses.airportPort ?? 0 },
{ label: '无人机', icon: Drone, iconColor: 'text-violet-400', us: usLosses.drones ?? 0, ir: iranLosses.drones ?? 0 },
{ label: '导弹', icon: Rocket, iconColor: 'text-orange-500', us: usLosses.missiles ?? 0, ir: iranLosses.missiles ?? 0 },
{ label: '直升机', icon: Asterisk, iconColor: 'text-teal-400', us: usLosses.helicopters ?? 0, ir: iranLosses.helicopters ?? 0 },

View File

@@ -127,11 +127,25 @@ function toFeature(loc: KeyLoc, side: 'us' | 'iran', status?: BaseStatus) {
const FLIGHT_DURATION_MS = 2500 // 光点飞行单程时间
/** 移动端/小屏降低动画更新频率以减轻卡顿;返回最小间隔 ms */
function getAnimIntervalMs(): number {
try {
if (typeof window === 'undefined') return 33
const reducedMotion =
window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (reducedMotion) return 100 // 约 10fps兼顾可访问性
return window.innerWidth <= 768 ? 50 : 33 // 移动端约 20fps桌面约 30fps
} catch {
return 33
}
}
export function WarMap() {
const mapRef = useRef<MapRef>(null)
const containerRef = useRef<HTMLDivElement>(null)
const animRef = useRef<number>(0)
const startRef = useRef<number>(0)
const lastAnimUpdateRef = useRef<number>(0)
const attackPathsRef = useRef<[number, number][][]>([])
const lincolnPathsRef = useRef<[number, number][][]>([])
const fordPathsRef = useRef<[number, number][][]>([])
@@ -293,10 +307,15 @@ export function WarMap() {
const tick = (t: number) => {
const elapsed = t - startRef.current
const zoom = map.getZoom()
const zoomScale = Math.max(0.5, zoom / 4.2) // 随镜头缩放放大变大、缩小变小4.2 为默认 zoom
try {
// 光点从起点飞向目标的循环动画
const intervalMs = getAnimIntervalMs()
const shouldUpdate = t - lastAnimUpdateRef.current >= intervalMs
if (shouldUpdate) lastAnimUpdateRef.current = t
if (shouldUpdate) {
const zoom = map.getZoom()
const zoomScale = Math.max(0.5, zoom / 4.2) // 随镜头缩放放大变大、缩小变小4.2 为默认 zoom
try {
// 光点从起点飞向目标的循环动画
const src = map.getSource('attack-dots') as { setData: (d: GeoJSON.FeatureCollection) => void } | undefined
const paths = attackPathsRef.current
if (src && paths.length > 0) {
@@ -408,7 +427,8 @@ export function WarMap() {
map.setPaintProperty('gdelt-events-red-pulse', 'circle-radius', r)
map.setPaintProperty('gdelt-events-red-pulse', 'circle-opacity', opacity)
}
} catch (_) {}
} catch (_) {}
}
animRef.current = requestAnimationFrame(tick)
}

View File

@@ -42,6 +42,12 @@ export interface CombatLosses {
missiles?: number
helicopters?: number
submarines?: number
/** 坦克 */
tanks?: number
/** 民船 */
civilianShips?: number
/** 机/港(机场/港口) */
airportPort?: number
}
export interface SituationUpdate {
@@ -161,6 +167,9 @@ export const INITIAL_MOCK_DATA: MilitarySituation = {
missiles: 12,
helicopters: 1,
submarines: 0,
tanks: 0,
civilianShips: 0,
airportPort: 0,
},
wallStreetInvestmentTrend: [
{ time: '2025-03-01T00:00:00', value: 82 },
@@ -215,6 +224,9 @@ export const INITIAL_MOCK_DATA: MilitarySituation = {
missiles: 156,
helicopters: 8,
submarines: 2,
tanks: 0,
civilianShips: 0,
airportPort: 0,
},
retaliationSentiment: 78,
retaliationSentimentHistory: [

View File

@@ -80,6 +80,9 @@ export function useReplaySituation(): MilitarySituation {
missiles: lerp(0, usLoss.missiles ?? 0),
helicopters: lerp(0, usLoss.helicopters ?? 0),
submarines: lerp(0, usLoss.submarines ?? 0),
tanks: lerp(0, usLoss.tanks ?? 0),
civilianShips: lerp(0, usLoss.civilianShips ?? 0),
airportPort: lerp(0, usLoss.airportPort ?? 0),
}
const irLossesAt = {
bases: {
@@ -99,6 +102,9 @@ export function useReplaySituation(): MilitarySituation {
missiles: lerp(0, irLoss.missiles ?? 0),
helicopters: lerp(0, irLoss.helicopters ?? 0),
submarines: lerp(0, irLoss.submarines ?? 0),
tanks: lerp(0, irLoss.tanks ?? 0),
civilianShips: lerp(0, irLoss.civilianShips ?? 0),
airportPort: lerp(0, irLoss.airportPort ?? 0),
}
// 被袭基地:按 damage_level 排序,高损毁先出现;根据 progress 决定显示哪些为 attacked