在前幾天的開發中,陸續完成了核心計時功能、IndexedDB 本地儲存以及 JSON 資料備份與還原。系統的核心功能已經非常完整,但除此之外,希望能增進良好的使用者體驗,因此決定加入三個小功能。
當使用者開啟計時器後,可能會因為暫時離開座位或專注於任務之外的事情,而忘記按下暫停。這會導致計時數據失真,失去時間追蹤的意義,因此需要針對這樣的狀況設計閒置提醒。
透過監聽使用者的操作事件(如 mousemove、keydown),只要使用者在指定時間內(例如 45 分鐘)沒有任何動作,且計時器處於「執行中(isRunning)」狀態,就自動跳出確認視窗並暫停計時。
let idleTimer = null;
let idleMinutes = 45; // 預設閒置時間
function resetIdleTimer() {
// 如果沒有在計時,不需要計算閒置
if (!timerState.isRunning) return;
clearTimeout(idleTimer);
idleTimer = setTimeout(() => {
// 觸發閒置提醒前先自動暫停計時器,避免時間繼續走
pauseTimer();
const confirmSave = confirm(
`您已經閒置超過 ${idleMinutes} 分鐘囉!要幫您結束並儲存當前這筆任務計時嗎?`
);
if (confirmSave) {
openSaveModal(); // 選擇確定:開啟儲存 Modal
} else {
resumeTimer(); // 選擇取消:恢復計時並重新倒數閒置
}
}, idleMinutes * 60 * 1000);
}
// 監聽使用者互動事件,重置閒置倒數
['mousemove', 'keydown', 'click', 'scroll'].forEach(eventType => {
window.addEventListener(eventType, resetIdleTimer);
});
開發小技巧:如何測試長時閒置?
在測試時,不需要真的坐在電腦前面等45分鐘。最快驗證的方法是在 Console 手動將時間縮短,例如改為 6 秒(0.1 分鐘):
idleMinutes = 0.1; // 6秒
resetIdleTimer();
由於我們採用的是 Local-First(在地優先) 架構,資料優先寫入本地的 IndexedDB,即使沒有網路也能正常運作。但我們仍需在網路斷線時適時提醒使用者,給予充足的安全感。
運用瀏覽器原生的 navigator.onLine 以及 online / offline 事件:
function initNetworkListener() {
window.addEventListener('online', () => {
showToast('網路已恢復連線', 'success');
});
window.addEventListener('offline', () => {
showToast('目前處於離線狀態,您的計時資料仍會安全儲存在本地!', 'warning');
});
}
不知道大家會不會覺得視線內有移動的物體會很分散注意力,我自己是覺得多少會受到影響,所以這邊想要做一個小功能是讓使用者可以自行選定要讓畫面維持在純粹的 HH:MM 或完整顯示 HH:MM:SS。
let showSeconds = true;
function formatDisplayTime(seconds) {
const hrs = Math.floor(seconds / 3600);
const mins = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
const pad = num => String(num).padStart(2, '0');
if (showSeconds) {
return `${pad(hrs)}:${pad(mins)}:${pad(secs)}`;
} else {
return `${pad(hrs)}:${pad(mins)}`;
}
}

雖然這些細節並非產品的「核心功能」,但正是這些微小的體驗優化,形塑了產品的質感與順暢度。期待未來在規劃產品時,能帶著這些積累的實戰經驗,設計出更多方便好用的產品。