概要
これは初心者のブラウザ拡張機能 Advent Calendar 2024の17日目の記事です。
ポモドーロタイマーを作ってみます。
ポモドーロタイマー
具体的なコード
manifest.json
{
"browser_specific_settings": {
"gecko": {
"id": "the-town-pomodoro@example.com"
}
},
"manifest_version": 2,
"name": "init-extention",
"version": "0.1",
"description": "ポモドーロタイマー",
"icons": {
"48": "icons/icon-48.png"
},
"sidebar_action": {
"default_title": "pomodoro",
"default_panel": "timer.html",
"default_icon": "icons/icon-48.png"
},
"permissions": [
"tabs"
]
}
timer.js
const WORK_TIME = 25 * 60; // 25分
const BREAK_TIME = 5 * 60; // 5分
let timeRemaining = WORK_TIME;
let totalTime = WORK_TIME;
let timerInterval;
let isRunning = false;
let isWorkSession = true;
const timerDisplay = document.getElementById('timeDisplay');
const progressCircle = document.getElementById('progressCircle');
const startBtn = document.getElementById('startBtn');
const resetBtn = document.getElementById('resetBtn');
// 円周の長さを正確に計算(2πr)
const R = 90
const circumference = 2 * Math.PI * R;
progressCircle.setAttribute('stroke-dasharray', circumference);
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
function updateTimer() {
timeRemaining--;
if (timeRemaining < 0) {
clearInterval(timerInterval);
if (isWorkSession) {
// 作業セッション終了、休憩開始
timeRemaining = BREAK_TIME;
totalTime = BREAK_TIME;
isWorkSession = false;
} else {
// 休憩終了、作業セッション再開
timeRemaining = WORK_TIME;
totalTime = WORK_TIME;
isWorkSession = true;
}
isRunning = false;
startTimer();
return;
}
timerDisplay.textContent = formatTime(timeRemaining);
// プログレスバーの進捗を計算
const progress = circumference * (timeRemaining / totalTime);
progressCircle.style.strokeDashoffset = progress;
}
function startTimer() {
if (!isRunning) {
timerInterval = setInterval(updateTimer, 1000);
startBtn.textContent = '一時停止';
isRunning = true;
} else {
clearInterval(timerInterval);
startBtn.textContent = '再開';
isRunning = false;
}
}
function resetTimer() {
clearInterval(timerInterval);
timeRemaining = WORK_TIME;
totalTime = WORK_TIME;
isWorkSession = true;
isRunning = false;
timerDisplay.textContent = formatTime(timeRemaining);
progressCircle.style.strokeDashoffset = circumference;
startBtn.textContent = '開始';
}
startBtn.addEventListener('click', startTimer);
resetBtn.addEventListener('click', resetTimer);
timer.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="color-scheme" content="dark light">
<title>ポモドーロタイマー</title>
<link href="timer.css" rel="stylesheet" />
</head>
<body>
<h1>ポモドーロタイマー</h1>
<div id="timer">
<svg viewBox="0 0 200 200">
<circle
id="backgroundCircle"
cx="100"
cy="100"
r="90"
/>
<circle
id="progressCircle"
cx="100"
cy="100"
r="90"
stroke-dasharray="565.48"
stroke-dashoffset="565.48"
/>
</svg>
<div id="timeDisplay">25:00</div>
</div>
<div class="controls">
<button id="startBtn">開始</button>
<button id="resetBtn">リセット</button>
</div>
<script src="timer.js"></script>
</body>
</html>
timer.css
body {
width: 300px;
text-align: center;
font-family: Arial, sans-serif;
background-color: #f0f0f0;
padding: 20px;
}
#timer {
margin: 20px auto;
width: 200px;
height: 200px;
position: relative;
}
#backgroundCircle {
fill: none;
stroke: #e0e0e0;
stroke-width: 10;
}
#progressCircle {
fill: none;
stroke: #4CAF50;
stroke-width: 10;
transform: rotate(-90deg);
transform-origin: center;
transition: stroke-dashoffset 1s linear;
}
#timeDisplay {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: black;
font-size: 36px;
font-weight: bold;
}
.controls {
display: flex;
justify-content: center;
gap: 10px;
}
button {
padding: 10px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}