Files
Stopwatch-Timer-App_Kronome…/stopwatch-timer.html
T
2025-08-28 20:12:37 +03:00

740 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<link id="favicon" href="img/icon.ico" rel="shortcut icon" type="image/x-icon">
<title>Stopwatch Timer App</title>
<style>
@font-face{font-family:'JetBrains Mono';src:url('/FONTS/JetBrains.woff2')format('woff2');font-style:normal;}
:root{
--light: #F8F6E3;
--dark: #0C0C0C;
margin:0;
padding:0;
box-sizing:border-box}
body{
overflow-y: hidden;
height:100vh;
display:flex;
flex-direction: column;
align-items:center;
justify-content:center;
background:var(--dark)}
.wrapper .wrapper2{
user-select: none !important;
overflow: hidden;
position: relative;
flex: 0 1 auto;
height: auto;
background: var(--dark);
width: calc(100% - 4.5rem);
max-width: 27rem;}
.wrapper2{
flex-direction: column;
align-items: center;
margin-top: 1rem;}
.wrapper .time{
-webkit-user-select: none !important;
font-family: "JetBrains Mono";
height: 6.25rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
overflow: hidden;
position: relative;
font-size: clamp(1rem, 9vw, 3rem) !important;}
.wrapper .time span{
width: auto;
text-align: center;
font-weight: 500;
color: var(--light);}
.wrapper .buttons{
-webkit-user-select: none !important;
text-align:center;
margin-top:1.25rem}
.buttons button{
font-family: "JetBrains Mono";
padding: .375rem 1rem;
outline: none;
border: none;
color: var(--dark);
background: var(--light);
font-size: 17px;
font-weight: 500;
border-radius: 6px;
cursor: pointer;
box-shadow: 10px 10px 20px rgb(0 0 0 / .09);
width: 5rem;
margin: .2rem .2rem;}
.buttons button.active,.buttons button.stopActive{
pointer-events:none;
opacity:.7}
.laps-container{
margin-top:1.25rem;
max-height:9.375rem;
background:transparent;
padding: 0 2.8rem;
scrollbar-width: thin;
scrollbar-color: var(--light) transparent;}
.laps-container::-webkit-scrollbar {
width: 6px;}
.laps-container::-webkit-scrollbar-track {
background: transparent;}
.laps-container::-webkit-scrollbar-thumb {
background-color: var(--light);
border-radius: 6px;}
.laps-container::-webkit-scrollbar-thumb:hover {
background-color: var(--light);}
.laps {
height: 9.375rem; /* örnek sınır */
list-style: none;
padding: 0;
overflow-y: auto;}
.laps li{
font-size:16px;
color: var(--light);
text-align: center;
border-bottom:1px solid #ddd;
font-family: "JetBrains Mono";
padding:.25rem 0;}
.circle {
position: relative;}
.display-remain-time {
position: absolute;
top: 44%;
left: 50%;
transform: translate(-50%, -50%);}
#set{
display: flex;
flex-direction: column;
align-items: center;
margin: 0 .3rem;}
button[data-setter], .controls button {
outline: none;
background: transparent;
border: none;
font-family: "JetBrains Mono";
font-weight: 300;
font-size: 18px;
width: 2.1875rem;
height: 2.17rem;
color: var(--light);
cursor: pointer;}
button[data-setter]:hover, .controls button:hover {
opacity: 0.7;}
.setters {
display: flex;
justify-content: center;
font-family: "JetBrains Mono";
color: var(--light);
margin-top: 1.25rem;}
.setters .value {
font-size: 20px;
margin: 0 5px;
display: inline-block;}
.display-remain-time {
font-family: "JetBrains Mono";
font-weight: 100;
font-size: 50px;
color: var(--light);
margin-top: 1.25rem;
width: fit-content;}
.e-c-base {
fill: none;
stroke: var(--light);
stroke-width: 5px;}
.e-c-progress {
fill: none;
stroke: var(--dark);
stroke-width: 3px;
transition: stroke-dashoffset 0.5s linear;}
.e-c-pointer {
fill: #FFF;
stroke: var(--light);
stroke-width: 2px;}
#e-pointer {
transition: transform 0.5s linear;}
.controls {
display: flex;
align-items: center;
}
.controls button {
margin: 10px 5px;
font-size: 16px;
color: var(--light);}
#toggleBtn {
outline: none;
background: transparent;
border: none;
width: 50px;
height: 50px;
position: relative;
cursor: pointer;}
/* Play ikon (üçgen) */
.play::before {
content: "";
position: absolute;
top: 8px;
left: 16px;
border-top: 15px solid transparent;
border-bottom: 15px solid transparent;
border-left: 22px solid var(--light);}
/* Pause ikon (iki dikdörtgen) */
.pause::after {
content: "";
position: absolute;
top: 8px;
left: 12px;
width: 15px;
height: 30px;
background-color: transparent;
border-radius: 1px;
border: 5px solid var(--light);
border-top: none;
border-bottom: none;}
#resetBttn {
width: 75px;}
input[type="color"] {
width: 2.5rem;
height: 2.5rem;
position: fixed;
bottom: 32px;
left: unset;
right: 15px;
border: none;
cursor: pointer;}
.theme-toggle {
width: 2.5rem;
height: 2.5rem;
position: fixed;
bottom: 32px;
left: 15px;
right:unset;
background: var(--light);
color: var(--dark);
border: none;
border-radius: 50%;
cursor: pointer;
justify-content: center;
align-items: center;
transition: background 0.3s ease, color 0.3s ease;
z-index: 10;}
.nav {
position: relative;
display: flex;}
.nav__item {
position: relative;
display: flex;
width: 7rem;
height: 2rem;
text-decoration: none;
align-items: center;
justify-content: center;}
.nav__indicator {
position: absolute;
bottom: 0;
left: 0;
height: 1rem;
background: var(--light);
transition: all 0.3s ease;
border-radius: 9px;}
.custom-alert {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
padding: 6.25rem;
border-radius: 8px;
text-align: center;
font-size: 20px;
font-weight: bold;
color: var(--dark);
background: var(--light);
animation: blinkBg 1s infinite;
z-index: 9999;}
/* blink efekti */
@keyframes blinkBg {
0%, 50%, 100% { background: var(--light);color: var(--dark); }
25%, 75% { background: var(--dark); color: var(--light); border: 2px solid var(--light); }
}
/* buton blinkten etkilenmesin */
.custom-alert button {
margin-top: 1rem;
padding: .5rem 1rem;
background: var(--dark);
color: var(--light);
font-size: 16px;
font-weight: bold;
border: none;
border-radius: 5px;
cursor: pointer;
animation: none !important; /* blinki devre dışı bırak */}
.elapsed-time {
margin-top: .625rem;
font-size: 18px;
font-weight: normal;
color: var(--dark);}
</style>
</head>
<body>
<nav class="nav">
<div class="nav__indicator"></div>
<a href="#" id="item_1" class="nav__item">Stopwatch</a>
<a href="#" id="item_2" class="nav__item">Timers</a>
</nav><div class="wrapper"><div class="time"><span class="hour">00</span><span class="colon">:</span><span class="minute">00</span><span class="colon">:</span><span class="second">00</span><span class="colon ms-colon">:</span><span class="millisecond">00</span></div><div class="buttons"><button class="start">Start</button><button class="stop">Stop</button><button class="reset">Reset</button><button class="lap">Lap</button></div><div class="laps-container"><ul class="laps"></ul></div></div>
<div class="wrapper2">
<!-- Dairesel Gösterge -->
<div class="circle">
<svg width="300" viewBox="0 0 220 220" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(110,110)">
<circle r="100" class="e-c-base"/>
<g transform="rotate(-90)">
<circle r="100" class="e-c-progress"/>
<g id="e-pointer">
<circle cx="100" cy="0" r="8" class="e-c-pointer"/>
</g>
</g>
</g>
</svg>
<div class="display-remain-time">00:00:00</div>
</div>
<div class="controls">
<button id="toggleBtn" class="play"></button>
<button id="resetBttn" class="reset">Reset</button>
</div>
<div class="setters">
<div id="set" class="hours-set">
<button data-setter="hours-plus">+</button>
<div class="value hours">00</div>
<button data-setter="hours-minus">-</button>
</div>
<div id="set" class="minutes-set">
<button data-setter="minutes-plus">+</button>
<div class="value minutes">00</div>
<button data-setter="minutes-minus">-</button>
</div>
<div id="set" class="seconds-set">
<button data-setter="seconds-plus">+</button>
<div class="value seconds">00</div>
<button data-setter="seconds-minus">-</button>
</div>
</div>
</div>
<button class="theme-toggle" id="themeToggle"></button>
<input type="color" id="colorPicker" value="#ffffff">
<script>
document.addEventListener("dblclick", function(e) {
e.preventDefault();
});
let startTimer,
hr = (min = sec = ms = "00");
const startBtn = document.querySelector(".start"),
stopBtn = document.querySelector(".stop"),
resetBtn = document.querySelector(".reset"),
lapBtn = document.querySelector(".lap"),
lapsList = document.querySelector(".laps");
let lapCount = 0; // global değişken
let lastLapTime = ""; // son eklenen lap zamanı
const body = document.body;
const themeToggle = document.getElementById('themeToggle');
let clicked = false;
let lightMode = false;
function start() {
startBtn.classList.add("active");
stopBtn.classList.remove("stopActive");
startTimer = setInterval(() => {
ms++;
ms = ms < 10 ? "0" + ms : ms;
if (ms == 100) {
sec++;
sec = sec < 10 ? "0" + sec : sec;
ms = "00";
}
if (sec == 60) {
min++;
min = min < 10 ? "0" + min : min;
sec = "00";
}
if (min == 60) {
hr++;
hr = hr < 10 ? "0" + hr : hr;
min = "00";
}
putValue();
}, 10);
}
function stop() {
startBtn.classList.remove("active");
stopBtn.classList.add("stopActive");
clearInterval(startTimer);
}
function reset() {
startBtn.classList.remove("active");
stopBtn.classList.remove("stopActive");
clearInterval(startTimer);
hr = min = sec = ms = "00";
lastLapTime = "";
lapsList.innerHTML = "";
lapCount = 0;
putValue();
}
function lap() {
if (hr === "00" && min === "00" && sec === "00" && ms === "00") return;
const currentTime = `${hr}:${min}:${sec}:${ms}`;
// Son lap ile aynıysa ekleme
if (currentTime === lastLapTime) return;
lapCount++; // her yeni lap için sayacı artır
const li = document.createElement("li");
li.textContent = `Lap ${lapCount}: ${currentTime}`;
lapsList.appendChild(li);
lastLapTime = currentTime;
// DOM güncellendikten sonra en alta kaydır
setTimeout(() => {
lapsList.scrollTop = lapsList.scrollHeight;
}, 0);
}
function putValue() {
document.querySelector(".millisecond").innerText = ms;
document.querySelector(".second").innerText = sec;
document.querySelector(".minute").innerText = min;
document.querySelector(".hour").innerText = hr;
}
startBtn.addEventListener("click", start);
stopBtn.addEventListener("click", stop);
resetBtn.addEventListener("click", reset);
lapBtn.addEventListener("click", lap);
// === CIRCLE SETUP ===
const progressBar = document.querySelector('.e-c-progress');
const pointer = document.getElementById('e-pointer');
const displayOutput = document.querySelector('.display-remain-time');
const toggleBtn = document.getElementById('toggleBtn'); // play/pause tek buton
const resetBttn = document.getElementById('resetBttn');
const setterBtns = document.querySelectorAll('button[data-setter]');
const hoursDiv = document.querySelector('.hours');
const minutesDiv = document.querySelector('.minutes');
const secondsDiv = document.querySelector('.seconds');
let totalSeconds = 0;
let remainingSeconds = 0;
let timerInterval = null;
let isRunning = false;
// circle setup
const circleLength = 2 * Math.PI * 100;
progressBar.style.strokeDasharray = circleLength;
progressBar.style.strokeDashoffset = circleLength;
// update circle and pointer
function updateCircle() {
if (totalSeconds === 0) {
progressBar.style.strokeDashoffset = circleLength; // TAM çember → 100%
pointer.style.transform = 'rotate(0deg)';
return};
const percent = remainingSeconds / totalSeconds;
progressBar.style.strokeDashoffset = circleLength * (1 - percent);
pointer.style.transform = `rotate(${360 * percent}deg)`; // saat yönünde
}
// update display
function updateDisplay() {
let h = Math.floor(remainingSeconds / 3600);
let m = Math.floor((remainingSeconds % 3600) / 60);
let s = remainingSeconds % 60;
displayOutput.textContent =
`${h.toString().padStart(2,'0')}:${m.toString().padStart(2,'0')}:${s.toString().padStart(2,'0')}`;
hoursDiv.textContent = h.toString().padStart(2,'0');
minutesDiv.textContent = m.toString().padStart(2,'0');
secondsDiv.textContent = s.toString().padStart(2,'0');
updateCircle();
}
// setter buttons
setterBtns.forEach(btn => {
btn.addEventListener('click', () => {
const type = btn.dataset.setter;
if (type.includes('hours')) {
let val = parseInt(hoursDiv.textContent);
if (type.endsWith('plus')) val = (val + 1) % 24;
else val = (val - 1 + 24) % 24;
hoursDiv.textContent = val.toString().padStart(2,'0');
} else if (type.includes('minutes')) {
let val = parseInt(minutesDiv.textContent);
if (type.endsWith('plus')) val = (val + 1) % 60;
else val = (val - 1 + 60) % 60;
minutesDiv.textContent = val.toString().padStart(2,'0');
} else if (type.includes('seconds')) {
let val = parseInt(secondsDiv.textContent);
if (type.endsWith('plus')) val = (val + 1) % 60;
else val = (val - 1 + 60) % 60;
secondsDiv.textContent = val.toString().padStart(2,'0');
}
totalSeconds =
parseInt(hoursDiv.textContent) * 3600 +
parseInt(minutesDiv.textContent) * 60 +
parseInt(secondsDiv.textContent);
remainingSeconds = totalSeconds;
updateDisplay();
});
});
const control_Setters = document.querySelector(".setters");
// toggle play/pause
toggleBtn.addEventListener('click', () => {
if (!isRunning) {
if (totalSeconds === 0) return; // süre yoksa başlatma
toggleBtn.classList.remove("play");
toggleBtn.classList.add("pause");
control_Setters.style.display = "none";
if (!timerInterval) {
timerInterval = setInterval(() => {
remainingSeconds--;
updateDisplay();
if (remainingSeconds <= 0) {
clearInterval(timerInterval);
timerInterval = null;
showTimerAlert();
toggleBtn.classList.remove("pause");
toggleBtn.classList.add("play");
control_Setters.style.display = "flex";
isRunning = false;
}
}, 1000);
}
isRunning = true;
} else {
toggleBtn.classList.remove("pause");
toggleBtn.classList.add("play");
control_Setters.style.display = "flex";
clearInterval(timerInterval);
timerInterval = null;
isRunning = false;
}
});
// reset
resetBttn.addEventListener('click', () => {
clearInterval(timerInterval);
control_Setters.style.display = "flex";
timerInterval = null;
isRunning = false;
remainingSeconds = 0;
totalSeconds = 0;
// Saat, dakika ve saniyeyi sıfırla
hoursDiv.textContent = '00';
minutesDiv.textContent = '00';
secondsDiv.textContent = '00';
displayOutput.textContent = '00:00:00';
// progress bar ve pointer sıfırla
progressBar.style.strokeDashoffset = circleLength; // TAM çember → 100%
pointer.style.transform = 'rotate(0deg)';
// buton reset → play
toggleBtn.classList.remove("pause");
toggleBtn.classList.add("play");
});
let faviconBlinkInterval = null;
let elapsedInterval = null;
let elapsedSeconds = 0;
function showTimerAlert() {
const alertBox = document.createElement("div");
alertBox.className = "custom-alert";
alertBox.innerHTML = `
<p>Time is up!</p>
<div class="elapsed-time" id="elapsedTime">+00:00</div>
<button id="muteBtn">Ignore</button>
`;
document.body.appendChild(alertBox);
// favicon blink başlat
startFaviconBlink();
// kronometre başlat
startElapsedTime();
// mute/durdur
document.getElementById("muteBtn").addEventListener("click", () => {
stopAlert(alertBox);
});
}
function startFaviconBlink() {
const favicon = document.getElementById("favicon");
let isRed = false;
faviconBlinkInterval = setInterval(() => {
favicon.href = isRed ? "img/icon.ico" : "img/black.ico";
isRed = !isRed;
}, 800);
}
function startElapsedTime() {
const display = document.getElementById("elapsedTime");
elapsedSeconds = 0;
elapsedInterval = setInterval(() => {
elapsedSeconds++;
let minutes = Math.floor(elapsedSeconds / 60);
let seconds = elapsedSeconds % 60;
display.textContent = `+${minutes.toString().padStart(2,"0")}:${seconds.toString().padStart(2,"0")}`;
}, 1000);
}
function stopAlert(alertBox) {
// alert kutusunu kapat
alertBox.remove();
// favicon blink durdur
clearInterval(faviconBlinkInterval);
faviconBlinkInterval = null;
// favicon resetle
document.getElementById("favicon").href = "img/icon.ico";
// kronometre durdur
clearInterval(elapsedInterval);
elapsedInterval = null;
elapsedSeconds = 0;
}
// Tema değiştirme
themeToggle.addEventListener('click', () => {
document.body.classList.toggle('light-mode');
/*lightMode = !lightMode;*/
const darkColor = getComputedStyle(document.documentElement)
.getPropertyValue('--dark').trim();
const lightColor = getComputedStyle(document.documentElement)
.getPropertyValue('--light').trim();
// Toggle işlemi
document.documentElement.style.setProperty('--dark', lightColor);
document.documentElement.style.setProperty('--light', darkColor);
});
const colorInput = document.getElementById("colorPicker");
colorInput.addEventListener("input", function() {
if (body.className === "") {
// Light mode aktif ediliyor
document.documentElement.style.setProperty("--dark", this.value);
} else {
// Light mode kapatılıyor (dark mode'a dön)
document.documentElement.style.setProperty("--dark", this.value);
}
});
const indicator = document.querySelector('.nav__indicator');
const items = document.querySelectorAll('.nav__item');
const gameSection = document.querySelector('.wrapper');
const webSection = document.querySelector('.wrapper2');
const item1 = document.getElementById('item_1');
const item2 = document.getElementById('item_2');
let activeItem = items[0]; // açılışta ilk item seçili
moveIndicator(activeItem);
updateVisibility(0); // ilk item açık
items.forEach((item, index) => {
item.addEventListener('click', e => {
e.preventDefault();
activeItem = item;
moveIndicator(activeItem);
updateVisibility(index);
});
});
function moveIndicator(target) {
const { offsetLeft: left, offsetTop: top, offsetWidth: width, offsetHeight: height } = target;
indicator.style.left = `${left - 4}px`; // biraz dışarı taşsın
indicator.style.top = `${top - 4}px`;
indicator.style.width = `7.5rem`;
indicator.style.height = `2.5rem`;
}
function updateVisibility(index) {
if (index === 0) {
gameSection.style.display = 'block';
webSection.style.display = 'none';
item1.style.color = "var(--dark)";
item2.style.color = "var(--light)";
} else if (index === 1) {
gameSection.style.display = 'none';
webSection.style.display = 'flex';
item1.style.color = "var(--light)";
item2.style.color = "var(--dark)";
}
}
</script></body></html>