feat(aerial): 드론 아이콘 쿼드콥터 + 함정 MarineTraffic 삼각형 스타일

- 함정: MarineTraffic 스타일 삼각형 (선수 방향 위, 상태색 채움)
- 드론: 쿼드콥터 아이콘 (X자 팔 + 프로펠러 회전 애니메이션 + 카메라 렌즈)
- 함정↔드론 점선 연결선 유지
- 송출중 REC LED 깜빡임, 드론 모델명 라벨

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nan Kyung Lee 2026-03-16 09:53:30 +09:00
부모 615f7f9277
커밋 fbef59341e

파일 보기

@ -278,39 +278,50 @@ export function RealtimeDrone() {
onClick={e => { e.originalEvent.stopPropagation(); setMapPopup(stream) }} onClick={e => { e.originalEvent.stopPropagation(); setMapPopup(stream) }}
> >
<div className="cursor-pointer group" title={stream.shipName}> <div className="cursor-pointer group" title={stream.shipName}>
<svg width="120" height="80" viewBox="0 0 120 80" fill="none" className="drop-shadow-lg transition-transform group-hover:scale-105" style={{ overflow: 'visible' }}> <svg width="130" height="85" viewBox="0 0 130 85" fill="none" className="drop-shadow-lg transition-transform group-hover:scale-105" style={{ overflow: 'visible' }}>
{/* 연결선 (점선) */} {/* 연결선 (점선) */}
<line x1="30" y1="55" x2="85" y2="28" stroke={statusColor} strokeWidth="1.5" strokeDasharray="4 3" opacity="0.6" /> <line x1="28" y1="52" x2="88" y2="30" stroke={statusColor} strokeWidth="1.2" strokeDasharray="4 3" opacity="0.5" />
{/* 함정 삼각형 (좌하단) */} {/* ── 함정: MarineTraffic 스타일 삼각형 (선수 방향 위) ── */}
<polygon points="30,45 20,65 40,65" fill="rgba(0,0,0,.5)" stroke={statusColor} strokeWidth="1.5" /> <polygon points="28,38 18,58 38,58" fill={statusColor} opacity="0.85" />
<text x="30" y="59" textAnchor="middle" fill="#fff" fontSize="7" fontWeight="bold"></text> <polygon points="28,38 18,58 38,58" fill="none" stroke="#fff" strokeWidth="0.8" opacity="0.5" />
{/* 함정명 */} {/* 함정명 라벨 */}
<rect x="5" y="67" width="50" height="13" rx="3" fill="rgba(0,0,0,.7)" /> <rect x="3" y="61" width="50" height="13" rx="3" fill="rgba(0,0,0,.75)" />
<text x="30" y="76" textAnchor="middle" fill="#fff" fontSize="7" fontFamily="sans-serif" fontWeight="bold">{stream.shipName.replace(/서 /, ' ')}</text> <text x="28" y="70.5" textAnchor="middle" fill="#fff" fontSize="7" fontFamily="sans-serif" fontWeight="bold">{stream.shipName.replace(/서 /, ' ')}</text>
{/* 드론 원형 아이콘 (우상단) */} {/* ── 드론: 쿼드콥터 아이콘 ── */}
<circle cx="85" cy="28" r="16" fill="rgba(0,0,0,.6)" stroke={statusColor} strokeWidth="2" /> {/* 외곽 원 */}
{/* 드론 내부 — 십자 프로펠러 */} <circle cx="88" cy="30" r="18" fill="rgba(10,14,24,.7)" stroke={statusColor} strokeWidth="1.5" />
<line x1="73" y1="28" x2="97" y2="28" stroke={statusColor} strokeWidth="1" opacity="0.4" /> {/* X자 팔 */}
<line x1="85" y1="16" x2="85" y2="40" stroke={statusColor} strokeWidth="1" opacity="0.4" /> <line x1="76" y1="18" x2="100" y2="42" stroke={statusColor} strokeWidth="1.2" opacity="0.5" />
{/* 프로펠러 4개 */} <line x1="100" y1="18" x2="76" y2="42" stroke={statusColor} strokeWidth="1.2" opacity="0.5" />
<circle cx="73" cy="28" r="4" fill={statusColor} opacity="0.25" /> {/* 프로펠러 4개 (회전 애니메이션) */}
<circle cx="97" cy="28" r="4" fill={statusColor} opacity="0.25" /> <ellipse cx="76" cy="18" rx="5" ry="2.5" fill={statusColor} opacity="0.35">
<circle cx="85" cy="16" r="4" fill={statusColor} opacity="0.25" /> <animateTransform attributeName="transform" type="rotate" from="0 76 18" to="360 76 18" dur="1.5s" repeatCount="indefinite" />
<circle cx="85" cy="40" r="4" fill={statusColor} opacity="0.25" /> </ellipse>
<ellipse cx="100" cy="18" rx="5" ry="2.5" fill={statusColor} opacity="0.35">
<animateTransform attributeName="transform" type="rotate" from="0 100 18" to="-360 100 18" dur="1.5s" repeatCount="indefinite" />
</ellipse>
<ellipse cx="76" cy="42" rx="5" ry="2.5" fill={statusColor} opacity="0.35">
<animateTransform attributeName="transform" type="rotate" from="0 76 42" to="-360 76 42" dur="1.5s" repeatCount="indefinite" />
</ellipse>
<ellipse cx="100" cy="42" rx="5" ry="2.5" fill={statusColor} opacity="0.35">
<animateTransform attributeName="transform" type="rotate" from="0 100 42" to="360 100 42" dur="1.5s" repeatCount="indefinite" />
</ellipse>
{/* 본체 */} {/* 본체 */}
<circle cx="85" cy="28" r="5" fill={statusColor} opacity="0.8" /> <circle cx="88" cy="30" r="6" fill={statusColor} opacity="0.8" />
<circle cx="85" cy="28" r="2.5" fill="#fff" opacity="0.9" /> {/* 카메라 렌즈 */}
{/* 송출중 LED */} <circle cx="88" cy="30" r="3" fill="#fff" opacity="0.9" />
<circle cx="88" cy="30" r="1.5" fill={statusColor} />
{/* 송출중 REC LED */}
{stream.status === 'streaming' && ( {stream.status === 'streaming' && (
<circle cx="95" cy="18" r="3" fill="#ef4444"> <circle cx="100" cy="16" r="3" fill="#ef4444">
<animate attributeName="opacity" values="1;0.2;1" dur="1.2s" repeatCount="indefinite" /> <animate attributeName="opacity" values="1;0.2;1" dur="1s" repeatCount="indefinite" />
</circle> </circle>
)} )}
{/* 드론 이름 */} {/* 드론 모델명 */}
<rect x="62" y="46" width="46" height="12" rx="3" fill="rgba(0,0,0,.7)" /> <rect x="65" y="51" width="46" height="12" rx="3" fill="rgba(0,0,0,.75)" />
<text x="85" y="55" textAnchor="middle" fill={statusColor} fontSize="7" fontFamily="sans-serif" fontWeight="bold">{stream.droneModel.split(' ').slice(-1)[0]}</text> <text x="88" y="60" textAnchor="middle" fill={statusColor} fontSize="7" fontFamily="sans-serif" fontWeight="bold">{stream.droneModel.split(' ').slice(-1)[0]}</text>
</svg> </svg>
</div> </div>
</Marker> </Marker>