制作不易请您珍惜。html!DOCTYPE htmlhtml langzh-CNheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableyes, viewport-fitcovertitle 毛玻璃 · 番茄钟/titlestyle* {margin: 0;padding: 0;box-sizing: border-box;-webkit-tap-highlight-color: transparent;font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, sans-serif;}body {min-height: 100vh;display: flex;align-items: center;justify-content: center;background: radial-gradient(circle at 20% 30%, #f8b4b4, #b5d3e7);padding: 16px;/* 为毛玻璃提供透底背景 */}/* 毛玻璃卡片 */.glass-card {width: 100%;max-width: 400px;padding: 32px 24px 40px;background: rgba(255, 255, 255, 0.25);backdrop-filter: blur(16px) saturate(180%);-webkit-backdrop-filter: blur(16px) saturate(180%);border-radius: 56px;box-shadow: 0 30px 50px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.3) inset;transition: box-shadow 0.2s;text-align: center;border: 1px solid rgba(255, 255, 255, 0.4);}/* 标题 */.title {font-size: 1.8rem;font-weight: 600;letter-spacing: 2px;color: rgba(30, 30, 40, 0.8);text-shadow: 0 4px 12px rgba(255, 255, 255, 0.3);margin-bottom: 12px;display: flex;align-items: center;justify-content: center;gap: 8px;}.title span {background: rgba(255, 255, 255, 0.2);padding: 0 12px;border-radius: 40px;font-size: 1rem;font-weight: 400;}/* 圆形进度条容器 */.circle-wrapper {position: relative;width: 220px;height: 220px;margin: 12px auto 18px;}/* 进度环 SVG */.progress-ring {transform: rotate(-90deg);width: 220px;height: 220px;display: block;}.progress-ring .bg-circle {fill: none;stroke: rgba(255, 255, 255, 0.3);stroke-width: 10;}.progress-ring .progress-circle {fill: none;stroke: #ffffff;stroke-width: 10;stroke-linecap: round;stroke-dasharray: 565.48; /* 2 * PI * 90 ≈ 565.48 */stroke-dashoffset: 0;transition: stroke-dashoffset 0.25s ease, stroke 0.3s;filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.4));}/* 中央计时数字 */.timer-display {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);font-size: 4rem;font-weight: 300;color: rgba(255, 255, 255, 0.95);text-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);letter-spacing: 2px;background: rgba(255, 255, 255, 0.1);padding: 0 12px;border-radius: 60px;backdrop-filter: blur(4px);-webkit-backdrop-filter: blur(4px);border: 1px solid rgba(255, 255, 255, 0.2);line-height: 1.4;min-width: 140px;}/* 状态标签 */.status-badge {display: inline-block;font-size: 1rem;font-weight: 500;padding: 4px 20px;border-radius: 40px;background: rgba(255, 255, 255, 0.2);backdrop-filter: blur(4px);-webkit-backdrop-filter: blur(4px);color: rgba(30, 30, 40, 0.8);border: 1px solid rgba(255, 255, 255, 0.25);margin-bottom: 20px;letter-spacing: 0.5px;}/* 按钮组 */.btn-group {display: flex;justify-content: center;gap: 16px;flex-wrap: wrap;margin: 8px 0 20px;}.btn {flex: 1 1 0;min-width: 80px;padding: 14px 10px;border: none;border-radius: 60px;font-size: 1.1rem;font-weight: 500;background: rgba(255, 255, 255, 0.3);backdrop-filter: blur(8px);-webkit-backdrop-filter: blur(8px);color: #1e1e2a;border: 1px solid rgba(255, 255, 255, 0.35);box-shadow: 0 8px 20px rgba(0, 0, 0, 0.05);transition: all 0.2s;cursor: pointer;touch-action: manipulation;letter-spacing: 0.3px;backdrop-filter: blur(8px);}.btn:active {transform: scale(0.94);background: rgba(255, 255, 255, 0.5);}.btn-primary {background: rgba(255, 255, 255, 0.45);color: #1a1a2c;font-weight: 600;border-color: rgba(255, 255, 255, 0.6);box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);}.btn-primary:active {background: rgba(255, 255, 255, 0.7);}.btn-secondary {background: rgba(255, 255, 255, 0.15);border-color: rgba(255, 255, 255, 0.2);}.btn-secondary:active {background: rgba(255, 255, 255, 0.3);}.btn:disabled {opacity: 0.4;transform: none;pointer-events: none;}/* 完成计数 */.counter-box {margin-top: 18px;padding: 12px 20px;background: rgba(255, 255, 255, 0.15);backdrop-filter: blur(4px);-webkit-backdrop-filter: blur(4px);border-radius: 60px;display: inline-flex;align-items: center;gap: 12px;border: 1px solid rgba(255, 255, 255, 0.2);color: rgba(30, 30, 40, 0.8);font-size: 1rem;}.counter-box strong {font-size: 1.4rem;font-weight: 600;color: #ffffff;text-shadow: 0 2px 8px rgba(0,0,0,0.1);min-width: 32px;display: inline-block;}.reset-counter {background: rgba(255,255,255,0.1);border: 1px solid rgba(255,255,255,0.15);border-radius: 40px;padding: 4px 14px;font-size: 0.8rem;color: rgba(30,30,40,0.7);cursor: pointer;backdrop-filter: blur(4px);transition: 0.15s;}.reset-counter:active {background: rgba(255,255,255,0.3);transform: scale(0.94);}/* 移动端适配 */media (max-width: 480px) {.glass-card {padding: 28px 16px 32px;border-radius: 40px;}.circle-wrapper {width: 180px;height: 180px;}.progress-ring {width: 180px;height: 180px;}.progress-ring .bg-circle,.progress-ring .progress-circle {stroke-width: 8;}.timer-display {font-size: 3.2rem;min-width: 110px;padding: 0 10px;}.btn {padding: 12px 8px;font-size: 1rem;min-width: 70px;}.title {font-size: 1.5rem;}.counter-box {padding: 10px 16px;font-size: 0.9rem;}.counter-box strong {font-size: 1.2rem;min-width: 28px;}}media (max-width: 380px) {.circle-wrapper {width: 150px;height: 150px;}.progress-ring {width: 150px;height: 150px;}.timer-display {font-size: 2.6rem;min-width: 90px;}}/* 无痕过渡 */.fade-transition {transition: all 0.2s;}/style/headbodydiv classglass-card rolemaindiv classtitle 番茄钟span25:00/span/divdiv classstatus-badge idstatusBadge● 就绪/div!-- 圆形进度环 --div classcircle-wrappersvg classprogress-ring viewBox0 0 200 200 aria-label番茄钟进度!-- 背景环 --circle classbg-circle cx100 cy100 r90/circle!-- 进度环 (周长 565.48) --circle classprogress-circle cx100 cy100 r90 idprogressCircle/circle/svgdiv classtimer-display idtimerDisplay25:00/div/div!-- 按钮组 --div classbtn-groupbutton classbtn btn-primary idstartBtn aria-label开始番茄钟▶ 开始/buttonbutton classbtn btn-secondary idpauseBtn aria-label暂停番茄钟⏸️ 暂停/buttonbutton classbtn btn-secondary idresetBtn aria-label重置番茄钟↺ 重置/button/div!-- 完成计数 --div classcounter-boxspan✅ 已完成/spanstrong idcountDisplay0/strongspan次/spanspan classreset-counter idresetCounterBtn rolebutton tabindex0清零/span/div/divscript(function() {use strict;// ---------- DOM 引用 ----------const timerDisplay document.getElementById(timerDisplay);const progressCircle document.getElementById(progressCircle);const statusBadge document.getElementById(statusBadge);const countDisplay document.getElementById(countDisplay);const startBtn document.getElementById(startBtn);const pauseBtn document.getElementById(pauseBtn);const resetBtn document.getElementById(resetBtn);const resetCounterBtn document.getElementById(resetCounterBtn);// ---------- 常量 ----------const WORK_MINUTES 25;const TOTAL_SECONDS WORK_MINUTES * 60; // 1500秒const CIRCUMFERENCE 565.48; // 2 * PI * 90// ---------- 状态 ----------let remainingSeconds TOTAL_SECONDS; // 剩余秒数let timerId null; // setInterval idlet isRunning false;let isPaused false;let completedCount 0;// 初始进度偏移 0 (满环)let currentOffset 0;// ---------- localStorage 读取 ----------function loadCompletedCount() {try {const stored localStorage.getItem(tomatoCompletedCount);if (stored ! null) {const val parseInt(stored, 10);if (!isNaN(val) val 0) {completedCount val;return;}}completedCount 0;} catch (_) {completedCount 0;}}function saveCompletedCount() {try {localStorage.setItem(tomatoCompletedCount, String(completedCount));} catch (_) { /* ignore */ }}// 初始化计数loadCompletedCount();countDisplay.textContent completedCount;// ---------- 更新 UI (时间 进度) ----------function updateDisplayAndProgress() {// 格式化 mm:ssconst mins Math.floor(remainingSeconds / 60);const secs remainingSeconds % 60;timerDisplay.textContent ${String(mins).padStart(2, 0)}:${String(secs).padStart(2, 0)};// 计算偏移: (剩余 / 总秒数) * 周长const fraction remainingSeconds / TOTAL_SECONDS; // 0 ~ 1const offset CIRCUMFERENCE * (1 - fraction); // 从0开始, 到周长 (逐渐增加)currentOffset offset;progressCircle.style.strokeDashoffset offset;// 根据剩余时间微调颜色 (柔和渐变)const ratio remainingSeconds / TOTAL_SECONDS;const r 255 - Math.round(60 * (1 - ratio));const g 220 Math.round(35 * (1 - ratio));const b 245;progressCircle.style.stroke rgb(${Math.min(255, r)}, ${Math.min(255, g)}, ${Math.min(255, b)});}// ---------- 状态标签 ----------function updateStatus(text, isActive false) {statusBadge.textContent text;if (isActive) {statusBadge.style.background rgba(255, 255, 255, 0.35);} else {statusBadge.style.background rgba(255, 255, 255, 0.2);}}// ---------- 核心逻辑 ----------function tick() {if (remainingSeconds 0) {// 计时结束 - 完成一次番茄clearInterval(timerId);timerId null;isRunning false;isPaused false;// 增加完成次数completedCount 1;countDisplay.textContent completedCount;saveCompletedCount();// 重置时间到25分钟 (但不重置进度环动画, 重置时显示满环)remainingSeconds TOTAL_SECONDS;updateDisplayAndProgress();updateStatus(✅ 完成, false);// 启用按钮 (开始可再次启动)startBtn.disabled false;pauseBtn.disabled true;// 额外闪烁反馈progressCircle.style.stroke #a8e6cf;setTimeout(() {progressCircle.style.stroke #ffffff;}, 400);return;}// 正常倒计时remainingSeconds - 1;updateDisplayAndProgress();// 状态更新if (remainingSeconds 5) {updateStatus(⏳ 即将完成, true);} else {updateStatus(⏳ 专注中, true);}}// ---------- 开始 ----------function startTimer() {if (isRunning) return;// 如果已暂停, 继续if (isPaused) {isPaused false;isRunning true;timerId setInterval(tick, 1000);startBtn.disabled true;pauseBtn.disabled false;updateStatus(▶ 运行中, true);return;}// 全新开始: 如果剩余秒数为0或25:00, 重置到满if (remainingSeconds 0 || remainingSeconds TOTAL_SECONDS) {remainingSeconds TOTAL_SECONDS;updateDisplayAndProgress();}// 如果已经完成 (剩余0 但未重置) - 重置if (remainingSeconds 0) {remainingSeconds TOTAL_SECONDS;updateDisplayAndProgress();}isRunning true;isPaused false;timerId setInterval(tick, 1000);startBtn.disabled true;pauseBtn.disabled false;updateStatus(▶ 运行中, true);}// ---------- 暂停 ----------function pauseTimer() {if (!isRunning) return;if (isPaused) return;clearInterval(timerId);timerId null;isRunning false;isPaused true;startBtn.disabled false;pauseBtn.disabled true;updateStatus(⏸️ 已暂停, false);}// ---------- 重置 ----------function resetTimer() {// 停止计时器if (timerId) {clearInterval(timerId);timerId null;}isRunning false;isPaused false;// 重置时间为25分钟remainingSeconds TOTAL_SECONDS;updateDisplayAndProgress();startBtn.disabled false;pauseBtn.disabled true;// 重置进度环样式progressCircle.style.stroke #ffffff;updateStatus(↺ 已重置, false);}// ---------- 重置计数 ----------function resetCounter() {completedCount 0;countDisplay.textContent 0;saveCompletedCount();// 轻微触感反馈resetCounterBtn.style.opacity 0.6;setTimeout(() { resetCounterBtn.style.opacity 1; }, 200);}// ---------- 初始化 UI ----------function initUI() {remainingSeconds TOTAL_SECONDS;updateDisplayAndProgress();startBtn.disabled false;pauseBtn.disabled true;updateStatus(● 就绪, false);progressCircle.style.stroke #ffffff;}// ---------- 事件绑定 (移动端兼容) ----------startBtn.addEventListener(click, startTimer);pauseBtn.addEventListener(click, pauseTimer);resetBtn.addEventListener(click, resetTimer);resetCounterBtn.addEventListener(click, resetCounter);// 额外处理如果页面隐藏时暂停 (避免后台消耗)document.addEventListener(visibilitychange, function() {if (document.hidden isRunning !isPaused) {// 不自动暂停, 但保持前台运行; 但浏览器会节流, 我们保留。// 更好的做法记录时间差, 但为了简单, 不增加复杂度。// 用户可手动暂停。}});// ---------- 启动 ----------initUI();// 如果用户刷新页面, 保留计数, 但是计时状态不保留 (重置)// 确保重置状态window.addEventListener(load, function() {// 确保显示正确的计数countDisplay.textContent completedCount;resetTimer(); // 重置计时状态});})();/script/body/html
Python实现番茄闹钟制作(毛玻璃)
制作不易请您珍惜。html!DOCTYPE htmlhtml langzh-CNheadmeta charsetUTF-8meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableyes, viewport-fitcovertitle 毛玻璃 · 番茄钟/titlestyle* {margin: 0;padding: 0;box-sizing: border-box;-webkit-tap-highlight-color: transparent;font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, sans-serif;}body {min-height: 100vh;display: flex;align-items: center;justify-content: center;background: radial-gradient(circle at 20% 30%, #f8b4b4, #b5d3e7);padding: 16px;/* 为毛玻璃提供透底背景 */}/* 毛玻璃卡片 */.glass-card {width: 100%;max-width: 400px;padding: 32px 24px 40px;background: rgba(255, 255, 255, 0.25);backdrop-filter: blur(16px) saturate(180%);-webkit-backdrop-filter: blur(16px) saturate(180%);border-radius: 56px;box-shadow: 0 30px 50px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.3) inset;transition: box-shadow 0.2s;text-align: center;border: 1px solid rgba(255, 255, 255, 0.4);}/* 标题 */.title {font-size: 1.8rem;font-weight: 600;letter-spacing: 2px;color: rgba(30, 30, 40, 0.8);text-shadow: 0 4px 12px rgba(255, 255, 255, 0.3);margin-bottom: 12px;display: flex;align-items: center;justify-content: center;gap: 8px;}.title span {background: rgba(255, 255, 255, 0.2);padding: 0 12px;border-radius: 40px;font-size: 1rem;font-weight: 400;}/* 圆形进度条容器 */.circle-wrapper {position: relative;width: 220px;height: 220px;margin: 12px auto 18px;}/* 进度环 SVG */.progress-ring {transform: rotate(-90deg);width: 220px;height: 220px;display: block;}.progress-ring .bg-circle {fill: none;stroke: rgba(255, 255, 255, 0.3);stroke-width: 10;}.progress-ring .progress-circle {fill: none;stroke: #ffffff;stroke-width: 10;stroke-linecap: round;stroke-dasharray: 565.48; /* 2 * PI * 90 ≈ 565.48 */stroke-dashoffset: 0;transition: stroke-dashoffset 0.25s ease, stroke 0.3s;filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.4));}/* 中央计时数字 */.timer-display {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);font-size: 4rem;font-weight: 300;color: rgba(255, 255, 255, 0.95);text-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);letter-spacing: 2px;background: rgba(255, 255, 255, 0.1);padding: 0 12px;border-radius: 60px;backdrop-filter: blur(4px);-webkit-backdrop-filter: blur(4px);border: 1px solid rgba(255, 255, 255, 0.2);line-height: 1.4;min-width: 140px;}/* 状态标签 */.status-badge {display: inline-block;font-size: 1rem;font-weight: 500;padding: 4px 20px;border-radius: 40px;background: rgba(255, 255, 255, 0.2);backdrop-filter: blur(4px);-webkit-backdrop-filter: blur(4px);color: rgba(30, 30, 40, 0.8);border: 1px solid rgba(255, 255, 255, 0.25);margin-bottom: 20px;letter-spacing: 0.5px;}/* 按钮组 */.btn-group {display: flex;justify-content: center;gap: 16px;flex-wrap: wrap;margin: 8px 0 20px;}.btn {flex: 1 1 0;min-width: 80px;padding: 14px 10px;border: none;border-radius: 60px;font-size: 1.1rem;font-weight: 500;background: rgba(255, 255, 255, 0.3);backdrop-filter: blur(8px);-webkit-backdrop-filter: blur(8px);color: #1e1e2a;border: 1px solid rgba(255, 255, 255, 0.35);box-shadow: 0 8px 20px rgba(0, 0, 0, 0.05);transition: all 0.2s;cursor: pointer;touch-action: manipulation;letter-spacing: 0.3px;backdrop-filter: blur(8px);}.btn:active {transform: scale(0.94);background: rgba(255, 255, 255, 0.5);}.btn-primary {background: rgba(255, 255, 255, 0.45);color: #1a1a2c;font-weight: 600;border-color: rgba(255, 255, 255, 0.6);box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);}.btn-primary:active {background: rgba(255, 255, 255, 0.7);}.btn-secondary {background: rgba(255, 255, 255, 0.15);border-color: rgba(255, 255, 255, 0.2);}.btn-secondary:active {background: rgba(255, 255, 255, 0.3);}.btn:disabled {opacity: 0.4;transform: none;pointer-events: none;}/* 完成计数 */.counter-box {margin-top: 18px;padding: 12px 20px;background: rgba(255, 255, 255, 0.15);backdrop-filter: blur(4px);-webkit-backdrop-filter: blur(4px);border-radius: 60px;display: inline-flex;align-items: center;gap: 12px;border: 1px solid rgba(255, 255, 255, 0.2);color: rgba(30, 30, 40, 0.8);font-size: 1rem;}.counter-box strong {font-size: 1.4rem;font-weight: 600;color: #ffffff;text-shadow: 0 2px 8px rgba(0,0,0,0.1);min-width: 32px;display: inline-block;}.reset-counter {background: rgba(255,255,255,0.1);border: 1px solid rgba(255,255,255,0.15);border-radius: 40px;padding: 4px 14px;font-size: 0.8rem;color: rgba(30,30,40,0.7);cursor: pointer;backdrop-filter: blur(4px);transition: 0.15s;}.reset-counter:active {background: rgba(255,255,255,0.3);transform: scale(0.94);}/* 移动端适配 */media (max-width: 480px) {.glass-card {padding: 28px 16px 32px;border-radius: 40px;}.circle-wrapper {width: 180px;height: 180px;}.progress-ring {width: 180px;height: 180px;}.progress-ring .bg-circle,.progress-ring .progress-circle {stroke-width: 8;}.timer-display {font-size: 3.2rem;min-width: 110px;padding: 0 10px;}.btn {padding: 12px 8px;font-size: 1rem;min-width: 70px;}.title {font-size: 1.5rem;}.counter-box {padding: 10px 16px;font-size: 0.9rem;}.counter-box strong {font-size: 1.2rem;min-width: 28px;}}media (max-width: 380px) {.circle-wrapper {width: 150px;height: 150px;}.progress-ring {width: 150px;height: 150px;}.timer-display {font-size: 2.6rem;min-width: 90px;}}/* 无痕过渡 */.fade-transition {transition: all 0.2s;}/style/headbodydiv classglass-card rolemaindiv classtitle 番茄钟span25:00/span/divdiv classstatus-badge idstatusBadge● 就绪/div!-- 圆形进度环 --div classcircle-wrappersvg classprogress-ring viewBox0 0 200 200 aria-label番茄钟进度!-- 背景环 --circle classbg-circle cx100 cy100 r90/circle!-- 进度环 (周长 565.48) --circle classprogress-circle cx100 cy100 r90 idprogressCircle/circle/svgdiv classtimer-display idtimerDisplay25:00/div/div!-- 按钮组 --div classbtn-groupbutton classbtn btn-primary idstartBtn aria-label开始番茄钟▶ 开始/buttonbutton classbtn btn-secondary idpauseBtn aria-label暂停番茄钟⏸️ 暂停/buttonbutton classbtn btn-secondary idresetBtn aria-label重置番茄钟↺ 重置/button/div!-- 完成计数 --div classcounter-boxspan✅ 已完成/spanstrong idcountDisplay0/strongspan次/spanspan classreset-counter idresetCounterBtn rolebutton tabindex0清零/span/div/divscript(function() {use strict;// ---------- DOM 引用 ----------const timerDisplay document.getElementById(timerDisplay);const progressCircle document.getElementById(progressCircle);const statusBadge document.getElementById(statusBadge);const countDisplay document.getElementById(countDisplay);const startBtn document.getElementById(startBtn);const pauseBtn document.getElementById(pauseBtn);const resetBtn document.getElementById(resetBtn);const resetCounterBtn document.getElementById(resetCounterBtn);// ---------- 常量 ----------const WORK_MINUTES 25;const TOTAL_SECONDS WORK_MINUTES * 60; // 1500秒const CIRCUMFERENCE 565.48; // 2 * PI * 90// ---------- 状态 ----------let remainingSeconds TOTAL_SECONDS; // 剩余秒数let timerId null; // setInterval idlet isRunning false;let isPaused false;let completedCount 0;// 初始进度偏移 0 (满环)let currentOffset 0;// ---------- localStorage 读取 ----------function loadCompletedCount() {try {const stored localStorage.getItem(tomatoCompletedCount);if (stored ! null) {const val parseInt(stored, 10);if (!isNaN(val) val 0) {completedCount val;return;}}completedCount 0;} catch (_) {completedCount 0;}}function saveCompletedCount() {try {localStorage.setItem(tomatoCompletedCount, String(completedCount));} catch (_) { /* ignore */ }}// 初始化计数loadCompletedCount();countDisplay.textContent completedCount;// ---------- 更新 UI (时间 进度) ----------function updateDisplayAndProgress() {// 格式化 mm:ssconst mins Math.floor(remainingSeconds / 60);const secs remainingSeconds % 60;timerDisplay.textContent ${String(mins).padStart(2, 0)}:${String(secs).padStart(2, 0)};// 计算偏移: (剩余 / 总秒数) * 周长const fraction remainingSeconds / TOTAL_SECONDS; // 0 ~ 1const offset CIRCUMFERENCE * (1 - fraction); // 从0开始, 到周长 (逐渐增加)currentOffset offset;progressCircle.style.strokeDashoffset offset;// 根据剩余时间微调颜色 (柔和渐变)const ratio remainingSeconds / TOTAL_SECONDS;const r 255 - Math.round(60 * (1 - ratio));const g 220 Math.round(35 * (1 - ratio));const b 245;progressCircle.style.stroke rgb(${Math.min(255, r)}, ${Math.min(255, g)}, ${Math.min(255, b)});}// ---------- 状态标签 ----------function updateStatus(text, isActive false) {statusBadge.textContent text;if (isActive) {statusBadge.style.background rgba(255, 255, 255, 0.35);} else {statusBadge.style.background rgba(255, 255, 255, 0.2);}}// ---------- 核心逻辑 ----------function tick() {if (remainingSeconds 0) {// 计时结束 - 完成一次番茄clearInterval(timerId);timerId null;isRunning false;isPaused false;// 增加完成次数completedCount 1;countDisplay.textContent completedCount;saveCompletedCount();// 重置时间到25分钟 (但不重置进度环动画, 重置时显示满环)remainingSeconds TOTAL_SECONDS;updateDisplayAndProgress();updateStatus(✅ 完成, false);// 启用按钮 (开始可再次启动)startBtn.disabled false;pauseBtn.disabled true;// 额外闪烁反馈progressCircle.style.stroke #a8e6cf;setTimeout(() {progressCircle.style.stroke #ffffff;}, 400);return;}// 正常倒计时remainingSeconds - 1;updateDisplayAndProgress();// 状态更新if (remainingSeconds 5) {updateStatus(⏳ 即将完成, true);} else {updateStatus(⏳ 专注中, true);}}// ---------- 开始 ----------function startTimer() {if (isRunning) return;// 如果已暂停, 继续if (isPaused) {isPaused false;isRunning true;timerId setInterval(tick, 1000);startBtn.disabled true;pauseBtn.disabled false;updateStatus(▶ 运行中, true);return;}// 全新开始: 如果剩余秒数为0或25:00, 重置到满if (remainingSeconds 0 || remainingSeconds TOTAL_SECONDS) {remainingSeconds TOTAL_SECONDS;updateDisplayAndProgress();}// 如果已经完成 (剩余0 但未重置) - 重置if (remainingSeconds 0) {remainingSeconds TOTAL_SECONDS;updateDisplayAndProgress();}isRunning true;isPaused false;timerId setInterval(tick, 1000);startBtn.disabled true;pauseBtn.disabled false;updateStatus(▶ 运行中, true);}// ---------- 暂停 ----------function pauseTimer() {if (!isRunning) return;if (isPaused) return;clearInterval(timerId);timerId null;isRunning false;isPaused true;startBtn.disabled false;pauseBtn.disabled true;updateStatus(⏸️ 已暂停, false);}// ---------- 重置 ----------function resetTimer() {// 停止计时器if (timerId) {clearInterval(timerId);timerId null;}isRunning false;isPaused false;// 重置时间为25分钟remainingSeconds TOTAL_SECONDS;updateDisplayAndProgress();startBtn.disabled false;pauseBtn.disabled true;// 重置进度环样式progressCircle.style.stroke #ffffff;updateStatus(↺ 已重置, false);}// ---------- 重置计数 ----------function resetCounter() {completedCount 0;countDisplay.textContent 0;saveCompletedCount();// 轻微触感反馈resetCounterBtn.style.opacity 0.6;setTimeout(() { resetCounterBtn.style.opacity 1; }, 200);}// ---------- 初始化 UI ----------function initUI() {remainingSeconds TOTAL_SECONDS;updateDisplayAndProgress();startBtn.disabled false;pauseBtn.disabled true;updateStatus(● 就绪, false);progressCircle.style.stroke #ffffff;}// ---------- 事件绑定 (移动端兼容) ----------startBtn.addEventListener(click, startTimer);pauseBtn.addEventListener(click, pauseTimer);resetBtn.addEventListener(click, resetTimer);resetCounterBtn.addEventListener(click, resetCounter);// 额外处理如果页面隐藏时暂停 (避免后台消耗)document.addEventListener(visibilitychange, function() {if (document.hidden isRunning !isPaused) {// 不自动暂停, 但保持前台运行; 但浏览器会节流, 我们保留。// 更好的做法记录时间差, 但为了简单, 不增加复杂度。// 用户可手动暂停。}});// ---------- 启动 ----------initUI();// 如果用户刷新页面, 保留计数, 但是计时状态不保留 (重置)// 确保重置状态window.addEventListener(load, function() {// 确保显示正确的计数countDisplay.textContent completedCount;resetTimer(); // 重置计时状态});})();/script/body/html