ship-gis/src/tracking/components/TrackQueryTimeline.scss
HeungTak Lee e74688a969 feat: 항적조회 기능 구현
- tracking 패키지 TS→JS 변환 (stores, services, components, hooks, utils)
- 모달 항적조회 + 우클릭 항적조회
- 라이브 연결선 (PathStyleExtension dash + 1초 인터벌)
- TrackQueryModal, TrackQueryViewer, GlobalTrackQueryViewer
- 항적 레이어 (trackLayer.js)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 06:36:57 +09:00

409 lines
7.6 KiB
SCSS

/**
* 항적조회 타임라인 스타일
* TrackQueryViewer와 통합되는 재생 컨트롤
*/
.track-query-timeline {
background: rgba(30, 35, 55, 0.95);
border-radius: 6px;
padding: 8px 12px;
margin-top: 8px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
&.compact {
padding: 6px 8px;
.control-btn {
width: 28px;
height: 28px;
font-size: 10px;
}
.speed-btn {
font-size: 10px;
padding: 4px 8px;
}
.current-time-display {
font-size: 10px;
}
}
&.playing {
.play-btn {
animation: pulse-glow 1.5s infinite;
}
}
}
.timeline-controls {
display: flex;
align-items: center;
gap: 8px;
}
// 배속 선택기
.speed-selector {
position: relative;
z-index: 100;
.speed-btn {
background: rgba(79, 195, 247, 0.2);
border: 1px solid rgba(79, 195, 247, 0.4);
border-radius: 4px;
color: #4fc3f7;
font-size: 11px;
font-weight: 600;
padding: 5px 10px;
cursor: pointer;
transition: all 0.2s ease;
min-width: 50px;
&:hover:not(:disabled) {
background: rgba(79, 195, 247, 0.3);
border-color: #4fc3f7;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
.speed-menu {
position: absolute;
bottom: 100%;
left: 0;
margin-bottom: 4px;
background: rgba(40, 45, 70, 0.98);
border: 1px solid rgba(79, 195, 247, 0.4);
border-radius: 6px;
padding: 6px;
display: flex;
flex-wrap: wrap;
gap: 4px;
min-width: 180px;
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.3);
z-index: 101;
.speed-option {
flex: 0 0 calc(33.333% - 4px);
background: rgba(255, 255, 255, 0.1);
border: 1px solid transparent;
border-radius: 4px;
color: #fff;
font-size: 11px;
font-weight: 500;
padding: 6px 8px;
cursor: pointer;
transition: all 0.2s ease;
text-align: center;
&:hover {
background: rgba(79, 195, 247, 0.3);
border-color: rgba(79, 195, 247, 0.5);
}
&.active {
background: rgba(79, 195, 247, 0.5);
border-color: #4fc3f7;
color: #fff;
}
}
}
}
// 컨트롤 버튼
.control-btn {
width: 32px;
height: 32px;
border: none;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
transition: all 0.2s ease;
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
&.play-btn {
background: linear-gradient(135deg, #4caf50, #45a049);
color: #fff;
&:hover:not(:disabled) {
transform: scale(1.1);
box-shadow: 0 0 12px rgba(76, 175, 80, 0.5);
}
&.playing {
background: linear-gradient(135deg, #ffc107, #ffb300);
}
}
&.stop-btn {
background: rgba(244, 67, 54, 0.8);
color: #fff;
&:hover:not(:disabled) {
background: rgba(244, 67, 54, 1);
transform: scale(1.1);
}
}
}
// 슬라이더 컨테이너
.timeline-slider-container {
flex: 1;
position: relative;
height: 20px;
display: flex;
align-items: center;
min-width: 100px;
.timeline-slider {
width: 100%;
height: 6px;
-webkit-appearance: none;
appearance: none;
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
cursor: pointer;
position: relative;
z-index: 2;
&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
background: #fff;
border: 2px solid #4fc3f7;
border-radius: 50%;
cursor: grab;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
transition: transform 0.1s ease;
&:hover {
transform: scale(1.2);
}
&:active {
cursor: grabbing;
}
}
&::-moz-range-thumb {
width: 14px;
height: 14px;
background: #fff;
border: 2px solid #4fc3f7;
border-radius: 50%;
cursor: grab;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
&::-webkit-slider-thumb {
cursor: not-allowed;
}
}
}
.slider-progress {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
height: 6px;
background: linear-gradient(90deg, #4fc3f7, #29b6f6);
border-radius: 3px;
pointer-events: none;
z-index: 1;
transition: width 0.05s ease-out;
}
// 구간반복 범위 하이라이트
.loop-section-highlight {
position: absolute;
top: 50%;
transform: translateY(-50%);
height: 10px;
background: rgba(255, 193, 7, 0.25);
border-radius: 5px;
pointer-events: none;
z-index: 0;
border: 1px solid rgba(255, 193, 7, 0.5);
}
// 구간반복 마커 (A, B)
.loop-marker {
position: absolute;
top: 50%;
width: 16px;
height: 22px;
transform: translate(-50%, -50%);
cursor: ew-resize;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.1s ease;
.marker-label {
display: flex;
align-items: center;
justify-content: center;
width: 14px;
height: 18px;
background: #ffc107;
color: #000;
font-size: 9px;
font-weight: 700;
border-radius: 2px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
user-select: none;
}
&:hover:not(.disabled) {
transform: translate(-50%, -50%) scale(1.15);
.marker-label {
background: #ffca28;
}
}
&.dragging {
transform: translate(-50%, -50%) scale(1.2);
z-index: 11;
.marker-label {
background: #ffb300;
box-shadow: 0 0 8px rgba(255, 193, 7, 0.6);
}
}
&.disabled {
cursor: not-allowed;
opacity: 0.6;
}
// 마커 꼬리 (삼각형)
&::after {
content: '';
position: absolute;
bottom: -4px;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 5px solid #ffc107;
}
&.loop-marker-start {
.marker-label {
background: #4caf50;
color: #fff;
}
&::after {
border-top-color: #4caf50;
}
&:hover:not(.disabled) .marker-label {
background: #66bb6a;
}
&.dragging .marker-label {
background: #43a047;
}
}
&.loop-marker-end {
.marker-label {
background: #f44336;
color: #fff;
}
&::after {
border-top-color: #f44336;
}
&:hover:not(.disabled) .marker-label {
background: #ef5350;
}
&.dragging .marker-label {
background: #e53935;
}
}
}
}
// 현재 시간 표시
.current-time-display {
font-family: 'Consolas', 'Monaco', monospace;
font-size: 11px;
font-weight: 500;
color: #4fc3f7;
min-width: 110px;
text-align: center;
white-space: nowrap;
}
// 반복 토글
.loop-toggle,
.trail-toggle {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
font-size: 11px;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
input[type='checkbox'] {
width: 14px;
height: 14px;
cursor: pointer;
accent-color: #4fc3f7;
&:disabled {
cursor: not-allowed;
}
}
&:hover {
color: #fff;
}
}
// 항적표시 토글 (액센트 색상)
.trail-toggle {
input[type='checkbox'] {
accent-color: #ff9800;
}
}
// 재생 중 글로우 애니메이션
@keyframes pulse-glow {
0% {
box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.4);
}
70% {
box-shadow: 0 0 0 8px rgba(255, 193, 7, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(255, 193, 7, 0);
}
}