Update stopwatch-timer.html

This commit is contained in:
FURK4NGG
2025-08-28 19:46:30 +03:00
committed by GitHub
parent eb3b6c8d28
commit a87f86df27
+724
View File
@@ -1 +1,725 @@
<!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;}
@font-face {font-family: 'Teko'; src: url('/FONTS/Teko.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;}
.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>