728x90
안녕하세요 오늘은 지난 시간에 이어서 다중 Progrees Bar 에 일시정지 버튼, 그리고 삭제 버튼을 추가해보도록 하겠습니다.
지난 포스팅은 아래 링크에서 확인하실 수 있습니다
[JavaScript] 사용자 입력 기반 다중 Progress Bar와 전체 진행률 구현하기
안녕하세요 오늘은 자바스크립트와 html 을 이용해 Progress Bar를 만들어보도록 하겠습니다. 프로그레스바는 사용자가 진행 상황을 시각적으로 쉽게 파악할 수 있도록 도와주는 UI 요소로, 다
stickode.tistory.com
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>진행률 표시줄</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.progress-container {
margin-bottom: 20px;
border: 1px solid #ddd;
padding: 10px;
border-radius: 5px;
position: relative;
}
.progress-bar {
width: 100%;
background-color: #f3f3f3;
border-radius: 5px;
overflow: hidden;
height: 20px;
margin: 10px 0;
}
.progress-bar div {
height: 100%;
width: 0;
background-color: #4caf50;
transition: width 0.2s;
}
.label {
margin-bottom: 5px;
font-weight: bold;
}
.time-input {
margin-top: 5px;
width: 100%;
padding: 5px;
box-sizing: border-box;
}
.start-button {
margin: 20px 5px 0 0;
padding: 10px 20px;
font-size: 16px;
background-color: #4caf50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.start-button:hover {
background-color: #45a049;
}
.control-buttons {
margin-top: 10px;
}
.control-buttons button {
margin-right: 5px;
padding: 5px 10px;
font-size: 14px;
border: none;
border-radius: 3px;
cursor: pointer;
}
.pause-button {
background-color: #ffc107;
color: white;
}
.cancel-button {
background-color: #9c27b0;
color: white;
}
.delete-button {
background-color: #e91e63;
color: white;
position: absolute;
top: 10px;
right: 10px;
}
.pause-button:hover {
background-color: #ffb300;
}
.cancel-button:hover {
background-color: #7b1fa2;
}
.delete-button:hover {
background-color: #c2185b;
}
.progress-container.paused .progress-bar div {
background-color: #ff9800;
}
</style>
</head>
<body>
<h1>진행률 표시줄</h1>
<div id="progress-bars"></div>
<button class="start-button" id="start-button">시작</button>
<div class="progress-container">
<div class="label">전체 진행률:</div>
<div class="progress-bar" id="overall-bar">
<div></div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
const numItems = 5;
const progressBarsContainer = document.getElementById("progress-bars");
const overallBar = document.getElementById("overall-bar").children[0];
const startButton = document.getElementById("start-button");
let completedItems = 0;
const progressStates = [];
function createProgressBar(index) {
const container = document.createElement("div");
container.className = "progress-container";
const label = document.createElement("div");
label.className = "label";
label.innerText = `항목 ${index + 1}`;
const progressBar = document.createElement("div");
progressBar.className = "progress-bar";
const progress = document.createElement("div");
progressBar.appendChild(progress);
const input = document.createElement("input");
input.type = "number";
input.className = "time-input";
input.placeholder = "시간(초)을 입력하세요";
input.min = 1;
const controlButtons = document.createElement("div");
controlButtons.className = "control-buttons";
const pauseButton = document.createElement("button");
pauseButton.className = "pause-button";
pauseButton.innerText = "일시중지";
const cancelButton = document.createElement("button");
cancelButton.className = "cancel-button";
cancelButton.innerText = "취소";
controlButtons.appendChild(pauseButton);
controlButtons.appendChild(cancelButton);
// 삭제 버튼 추가
const deleteButton = document.createElement("button");
deleteButton.className = "delete-button";
deleteButton.innerText = "삭제";
container.appendChild(deleteButton);
container.appendChild(label);
container.appendChild(progressBar);
container.appendChild(input);
container.appendChild(controlButtons);
progressBarsContainer.appendChild(container);
// 상태 객체 초기화
const state = {
progress: progress,
input: input,
interval: null,
startTime: null,
endTime: null,
duration: 0,
paused: false,
remainingTime: 0
};
progressStates.push(state);
// 일시중지/재개 버튼 이벤트
pauseButton.addEventListener("click", () => {
if (!state.interval) return; // 진행 중이 아닌 경우
if (!state.paused) {
// 일시중지
clearInterval(state.interval);
state.interval = null;
state.paused = true;
const now = Date.now();
state.remainingTime = (state.endTime - now) / 1000;
pauseButton.innerText = "재개";
container.classList.add("paused");
updateOverallProgress();
} else {
// 재개
state.paused = false;
state.startTime = Date.now();
state.endTime = state.startTime + state.remainingTime * 1000;
state.interval = setInterval(() => {
const now = Date.now();
const percentage = Math.min(((now - state.startTime) / (state.endTime - state.startTime)) * 100, 100);
state.progress.style.width = percentage + "%";
if (percentage >= 100) {
clearInterval(state.interval);
state.interval = null;
completedItems++;
updateOverallProgress();
} else {
updateOverallProgress();
}
}, 100);
pauseButton.innerText = "일시중지";
container.classList.remove("paused");
updateOverallProgress();
}
});
// 취소 버튼 이벤트
cancelButton.addEventListener("click", () => {
if (state.interval) {
clearInterval(state.interval);
state.interval = null;
}
const wasCompleted = parseFloat(state.progress.style.width) === 100;
state.progress.style.width = "0%";
state.input.value = "";
if (wasCompleted) {
completedItems--;
}
state.paused = false;
pauseButton.innerText = "일시중지";
container.classList.remove("paused");
updateOverallProgress();
});
// 삭제 버튼 이벤트
deleteButton.addEventListener("click", () => {
if (state.interval) {
clearInterval(state.interval);
}
const wasCompleted = parseFloat(state.progress.style.width) === 100;
if (wasCompleted) {
completedItems--;
}
progressBarsContainer.removeChild(container);
const progressIndex = progressStates.indexOf(state);
if (progressIndex > -1) {
progressStates.splice(progressIndex, 1);
updateOverallProgress();
}
});
return state;
}
function updateOverallProgress() {
const activeStates = progressStates.filter(state => state.duration > 0 && parseFloat(state.progress.style.width) > 0);
const total = activeStates.length;
if (total === 0) {
overallBar.style.width = "0%";
return;
}
const totalPercentage = activeStates.reduce((acc, state) => {
const width = parseFloat(state.progress.style.width);
return acc + (isNaN(width) ? 0 : width);
}, 0);
const overallPercentage = totalPercentage / total;
overallBar.style.width = `${overallPercentage}%`;
}
function startProgress() {
progressStates.forEach((state, index) => {
const seconds = parseInt(state.input.value, 10);
if (isNaN(seconds) || seconds < 1) {
return;
}
if (state.interval || parseFloat(state.progress.style.width) === 100) return;
state.duration = seconds;
state.startTime = Date.now();
state.endTime = state.startTime + state.duration * 1000;
state.progress.style.width = "0%";
state.interval = setInterval(() => {
const now = Date.now();
const percentage = Math.min(((now - state.startTime) / (state.endTime - state.startTime)) * 100, 100);
state.progress.style.width = percentage + "%";
if (percentage >= 100) {
clearInterval(state.interval);
state.interval = null;
completedItems++;
updateOverallProgress();
} else {
updateOverallProgress();
}
}, 100);
});
updateOverallProgress();
}
startButton.addEventListener("click", startProgress);
// 초기 진행률 표시줄 생성
for (let i = 0; i < numItems; i++) {
createProgressBar(i);
}
});
</script>
</body>
</html>
위 코드의 흐름은 다음과 같습니다.
- 시간 입력 및 시작
- 각 항목의 입력란에 원하는 시간을 초 단위로 입력합니다.
- "시작" 버튼을 클릭하면, 입력된 시간에 해당하는 항목의 진행률이 시작됩니다 - 일시정지
- 진행 중인 항목의 "일시중지" 버튼을 클릭하면 해당 진행이 일시정지됩니다. - 삭제
- 각 진행률 표시줄의 오른쪽 상단에 있는 "삭제" 버튼을 클릭하면 해당 항목이 전체 목록에서 제거됩니다.
'JavaScript' 카테고리의 다른 글
[JavaScript] 싱글톤 패턴 사용하기 (0) | 2025.02.17 |
---|---|
[JavaScript] 로컬 이미지 업로드 및 이미지 확대하여 모달창에 띄우기 (0) | 2025.02.12 |
[JavaScript]TensorFlow.js로 고양이 이미지 판별 앱 만들기 (0) | 2025.02.04 |
[JavaScript] 사용자 입력 기반 다중 Progress Bar와 전체 진행률 구현하기 (0) | 2025.01.22 |
[JavaScript] Jest로 유닛 테스트 작성하기 (2) | 2025.01.19 |