|
103 | 103 |
|
104 | 104 | .progress-bar { |
105 | 105 | width: 100%; |
106 | | - height: 8px; |
| 106 | + height: 12px; |
107 | 107 | background: #e9ecef; |
108 | | - border-radius: 4px; |
| 108 | + border-radius: 6px; |
109 | 109 | overflow: hidden; |
110 | | - margin-bottom: 10px; |
| 110 | + margin-bottom: 15px; |
| 111 | + position: relative; |
| 112 | + box-shadow: inset 0 2px 4px rgba(0,0,0,0.1); |
111 | 113 | } |
112 | 114 |
|
113 | 115 | .progress-fill { |
114 | 116 | height: 100%; |
115 | 117 | background: linear-gradient(90deg, #667eea, #764ba2); |
116 | 118 | width: 0%; |
117 | | - transition: width 0.3s ease; |
| 119 | + transition: width 0.5s ease; |
| 120 | + position: relative; |
| 121 | + overflow: hidden; |
| 122 | + } |
| 123 | + |
| 124 | + .progress-fill::after { |
| 125 | + content: ''; |
| 126 | + position: absolute; |
| 127 | + top: 0; |
| 128 | + left: 0; |
| 129 | + bottom: 0; |
| 130 | + right: 0; |
| 131 | + background: linear-gradient( |
| 132 | + 90deg, |
| 133 | + transparent, |
| 134 | + rgba(255,255,255,0.2), |
| 135 | + transparent |
| 136 | + ); |
| 137 | + animation: shimmer 2s infinite; |
| 138 | + } |
| 139 | + |
| 140 | + @keyframes shimmer { |
| 141 | + 0% { transform: translateX(-100%); } |
| 142 | + 100% { transform: translateX(100%); } |
| 143 | + } |
| 144 | + |
| 145 | + .progress-info { |
| 146 | + display: grid; |
| 147 | + grid-template-columns: 1fr auto; |
| 148 | + gap: 10px; |
| 149 | + font-size: 13px; |
| 150 | + color: #666; |
| 151 | + margin-bottom: 10px; |
| 152 | + } |
| 153 | + |
| 154 | + .progress-details { |
| 155 | + display: grid; |
| 156 | + grid-template-columns: 1fr 1fr 1fr; |
| 157 | + gap: 15px; |
| 158 | + text-align: center; |
| 159 | + padding: 10px 0; |
| 160 | + border-top: 1px solid #eee; |
| 161 | + margin-top: 10px; |
| 162 | + } |
| 163 | + |
| 164 | + .progress-detail-item { |
| 165 | + display: flex; |
| 166 | + flex-direction: column; |
| 167 | + align-items: center; |
| 168 | + } |
| 169 | + |
| 170 | + .progress-detail-label { |
| 171 | + font-size: 11px; |
| 172 | + color: #999; |
| 173 | + text-transform: uppercase; |
| 174 | + letter-spacing: 0.5px; |
| 175 | + margin-bottom: 4px; |
| 176 | + } |
| 177 | + |
| 178 | + .progress-detail-value { |
| 179 | + font-size: 14px; |
| 180 | + font-weight: 600; |
| 181 | + color: #2c3e50; |
| 182 | + font-family: 'Courier New', monospace; |
118 | 183 | } |
119 | 184 |
|
120 | 185 | .progress-text { |
121 | 186 | font-size: 14px; |
122 | 187 | color: #666; |
123 | 188 | display: flex; |
124 | 189 | justify-content: space-between; |
| 190 | + align-items: center; |
| 191 | + } |
| 192 | + |
| 193 | + .progress-percent { |
| 194 | + font-weight: 600; |
| 195 | + color: #667eea; |
| 196 | + font-size: 16px; |
125 | 197 | } |
126 | 198 |
|
127 | 199 | .button-group { |
@@ -275,12 +347,26 @@ <h4>새로운 기능</h4> |
275 | 347 | <div class="status-message" id="statusMessage"></div> |
276 | 348 |
|
277 | 349 | <div class="progress-section" id="progressSection"> |
| 350 | + <div class="progress-info"> |
| 351 | + <span id="progressText">다운로드 준비 중...</span> |
| 352 | + <span class="progress-percent" id="progressPercent">0%</span> |
| 353 | + </div> |
278 | 354 | <div class="progress-bar"> |
279 | 355 | <div class="progress-fill" id="progressFill"></div> |
280 | 356 | </div> |
281 | | - <div class="progress-text"> |
282 | | - <span id="progressText">다운로드 중...</span> |
283 | | - <span id="progressPercent">0%</span> |
| 357 | + <div class="progress-details"> |
| 358 | + <div class="progress-detail-item"> |
| 359 | + <div class="progress-detail-label">속도</div> |
| 360 | + <div class="progress-detail-value" id="downloadSpeed">0 MB/s</div> |
| 361 | + </div> |
| 362 | + <div class="progress-detail-item"> |
| 363 | + <div class="progress-detail-label">남은 시간</div> |
| 364 | + <div class="progress-detail-value" id="timeRemaining">계산 중...</div> |
| 365 | + </div> |
| 366 | + <div class="progress-detail-item"> |
| 367 | + <div class="progress-detail-label">파일 크기</div> |
| 368 | + <div class="progress-detail-value" id="fileSize">0 MB</div> |
| 369 | + </div> |
284 | 370 | </div> |
285 | 371 | </div> |
286 | 372 |
|
@@ -385,14 +471,54 @@ <h4>새로운 기능</h4> |
385 | 471 | const progressFill = document.getElementById('progressFill'); |
386 | 472 | const progressPercent = document.getElementById('progressPercent'); |
387 | 473 | const progressText = document.getElementById('progressText'); |
| 474 | + const downloadSpeed = document.getElementById('downloadSpeed'); |
| 475 | + const timeRemaining = document.getElementById('timeRemaining'); |
| 476 | + const fileSize = document.getElementById('fileSize'); |
388 | 477 |
|
389 | 478 | const percent = Math.round(data.percent || 0); |
390 | 479 | progressFill.style.width = percent + '%'; |
391 | 480 | progressPercent.textContent = percent + '%'; |
392 | 481 |
|
| 482 | + // 다운로드 속도 표시 |
393 | 483 | if (data.bytesPerSecond) { |
394 | | - const speed = (data.bytesPerSecond / 1024 / 1024).toFixed(1); |
395 | | - progressText.textContent = `다운로드 중... ${speed} MB/s`; |
| 484 | + const speedMB = (data.bytesPerSecond / 1024 / 1024).toFixed(1); |
| 485 | + downloadSpeed.textContent = `${speedMB} MB/s`; |
| 486 | + |
| 487 | + // 남은 시간 계산 |
| 488 | + if (data.total && data.transferred && data.bytesPerSecond > 0) { |
| 489 | + const remainingBytes = data.total - data.transferred; |
| 490 | + const remainingSeconds = remainingBytes / data.bytesPerSecond; |
| 491 | + timeRemaining.textContent = this.formatTime(remainingSeconds); |
| 492 | + } |
| 493 | + } else { |
| 494 | + downloadSpeed.textContent = '0 MB/s'; |
| 495 | + } |
| 496 | + |
| 497 | + // 파일 크기 표시 |
| 498 | + if (data.total) { |
| 499 | + const totalMB = (data.total / 1024 / 1024).toFixed(1); |
| 500 | + fileSize.textContent = `${totalMB} MB`; |
| 501 | + } |
| 502 | + |
| 503 | + // 상태 텍스트 업데이트 |
| 504 | + if (percent > 0) { |
| 505 | + progressText.textContent = '다운로드 중...'; |
| 506 | + } else { |
| 507 | + progressText.textContent = '다운로드 시작 중...'; |
| 508 | + } |
| 509 | + } |
| 510 | + |
| 511 | + formatTime(seconds) { |
| 512 | + if (seconds < 60) { |
| 513 | + return `${Math.round(seconds)}초`; |
| 514 | + } else if (seconds < 3600) { |
| 515 | + const minutes = Math.floor(seconds / 60); |
| 516 | + const remainingSeconds = Math.round(seconds % 60); |
| 517 | + return `${minutes}분 ${remainingSeconds}초`; |
| 518 | + } else { |
| 519 | + const hours = Math.floor(seconds / 3600); |
| 520 | + const minutes = Math.floor((seconds % 3600) / 60); |
| 521 | + return `${hours}시간 ${minutes}분`; |
396 | 522 | } |
397 | 523 | } |
398 | 524 |
|
|
0 commit comments