こんにちはハッピーサトです。
PCでもスマホでも使いやすいシンプルな焙煎記録アプリを開発しました。
タイマーには#1~#8と番号を付けているので、当日の焙煎メニューと連動させてご活用下さい☆彡
コーヒー自家焙煎タイマー
#1
00:00
#2
00:00
#3
00:00
#4
00:00
#5
00:00
#6
00:00
#7
00:00
#8
00:00
焙煎指数計算
プログラム内容
アプリのHTML、CSS、JAVAコードを公開しておきます。
HTMLコード
<div class="timer-section">
<p><!-- 各タイマーのコンテナ。以下をタイマーの数だけ繰り返します --><br /><!-- タイマー #1 --></p>
<div id="timer1" class="timer-container">
<div class="timer-header">
<div class="timer-title">#1</div>
<div class="timer-display">00:00</div>
</div>
<div class="buttons-container"><button class="start">Start</button><br /><button class="first-crack">1st<br />Crack</button><br /><button class="second-crack">2nd<br />Crack</button><br /><button class="stop-reset">Stop<br />Reset</button></div>
<div class="crack-times"> </div>
</div>
<div id="timer2" class="timer-container">
<div class="timer-header">
<div class="timer-title">#2</div>
<div class="timer-display">00:00</div>
</div>
<div class="buttons-container"><button class="start">Start</button><br /><button class="first-crack">1st<br />Crack</button><br /><button class="second-crack">2nd<br />Crack</button><br /><button class="stop-reset">Stop<br />Reset</button></div>
<div class="crack-times"> </div>
</div>
<div id="timer3" class="timer-container">
<div class="timer-header">
<div class="timer-title">#3</div>
<div class="timer-display">00:00</div>
</div>
<div class="buttons-container"><button class="start">Start</button><br /><button class="first-crack">1st<br />Crack</button><br /><button class="second-crack">2nd<br />Crack</button><br /><button class="stop-reset">Stop<br />Reset</button></div>
<div class="crack-times"> </div>
</div>
<div id="timer4" class="timer-container">
<div class="timer-header">
<div class="timer-title">#4</div>
<div class="timer-display">00:00</div>
</div>
<div class="buttons-container"><button class="start">Start</button><br /><button class="first-crack">1st<br />Crack</button><br /><button class="second-crack">2nd<br />Crack</button><br /><button class="stop-reset">Stop<br />Reset</button></div>
<div class="crack-times"> </div>
</div>
<div id="timer5" class="timer-container">
<div class="timer-header">
<div class="timer-title">#5</div>
<div class="timer-display">00:00</div>
</div>
<div class="buttons-container"><button class="start">Start</button><br /><button class="first-crack">1st<br />Crack</button><br /><button class="second-crack">2nd<br />Crack</button><br /><button class="stop-reset">Stop<br />Reset</button></div>
<div class="crack-times"> </div>
</div>
<div id="timer6" class="timer-container">
<div class="timer-header">
<div class="timer-title">#6</div>
<div class="timer-display">00:00</div>
</div>
<div class="buttons-container"><button class="start">Start</button><br /><button class="first-crack">1st<br />Crack</button><br /><button class="second-crack">2nd<br />Crack</button><br /><button class="stop-reset">Stop<br />Reset</button></div>
<div class="crack-times"> </div>
</div>
<div id="timer7" class="timer-container">
<div class="timer-header">
<div class="timer-title">#7</div>
<div class="timer-display">00:00</div>
</div>
<div class="buttons-container"><button class="start">Start</button><br /><button class="first-crack">1st<br />Crack</button><br /><button class="second-crack">2nd<br />Crack</button><br /><button class="stop-reset">Stop<br />Reset</button></div>
<div class="crack-times"> </div>
</div>
<div id="timer8" class="timer-container">
<div class="timer-header">
<div class="timer-title">#8</div>
<div class="timer-display">00:00</div>
</div>
<div class="buttons-container"><button class="start">Start</button><br /><button class="first-crack">1st<br />Crack</button><br /><button class="second-crack">2nd<br />Crack</button><br /><button class="stop-reset">Stop<br />Reset</button></div>
<div class="crack-times"> </div>
</div>
<div class="calculation-section">
<h1 class="center-title">焙煎指数計算</h1>
<form id="calculationForm">
<div class="input-group"><label for="before">焙煎前(g)<input id="before" maxlength="3" name="before" pattern="\d*\.?\d*" required="" type="text" /></label> <label for="after">焙煎後(g)<input id="after" maxlength="3" name="after" pattern="\d*\.?\d*" required="" type="text" /></label> <!-- 計算ボタンにcalculate()関数を関連付け --> <button type="button">計算</button></div>
<div id="result">
<h2>指数: <span id="roastIndex"></span></h2>
</div>
</form></div>
</div>
<p><script src="script.js"></script> <script src="timer_script.js"></script></p>
CSSコード
/* 全体のCSS設定 */
body {
font-family: Arial, sans-serif;
}
.center-title {
text-align: center;
}
.crack-times {
font-size: 14px;
text-align: left;
margin-top: 0px;
margin-bottom: 0; /* クラックタイム表示部分の下部マージンを減らす */
}
.timer-container {
background-color: #fff;
border: none;
padding: 10px;
margin-bottom: 10px; /* タイマーコンテナ間の間隔 */
text-align: center;
border-bottom: 1px solid #ddd; /* 各タイマーコンテナの下部にボーダーを追加 */
}
/* 最後のタイマーコンテナにはボーダーを追加しない */
.timer-container:last-child {
border-bottom: none;
}
.timer-header {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 0px;
}
.timer-title {
font-size: 24px;
font-weight: bold;
margin-right: 10px;
order: -1; /* タイトルを左に移動 */
}
.timer-display {
background-color: black;
color: white;
font-size: 42px;
padding: 0px 45px;
margin: 3px 0;
}
.buttons-container {
display: flex;
justify-content: center;
flex-wrap: nowrap;
}
button {
background-color: #8B4513; /* 薄茶色より少し濃い茶色 */
color: white;
border: none;
padding: 10px 10px;
margin: 5px;
font-size: 16px;
cursor: pointer;
min-width: 80px;
}
#calculationForm h1 {
font-family: Arial, sans-serif; /* 焙煎指数計算のフォント指定 */
font-size: 24px;
font-weight: bold;
}
JAVAコード
document.addEventListener('DOMContentLoaded', function() {
const timers = document.querySelectorAll('.timer-container');
timers.forEach((timerContainer, index) => {
let startTime = 0;
let timerInterval = null;
let firstCrackTimes = [];
let secondCrackTimes = [];
let stopTime = 0;
let resetCount = 0;
const timerDisplay = timerContainer.querySelector('.timer-display');
const crackTimesDisplay = timerContainer.querySelector('.crack-times');
function formatTime(time) {
const minutes = Math.floor(time / 60000).toString().padStart(2, '0');
const seconds = ((time % 60000) / 1000).toFixed(0).padStart(2, '0');
return `${minutes}:${seconds}`;
}
function updateTimerDisplay(time) {
timerDisplay.textContent = formatTime(time);
}
function updateCrackTimesDisplay() {
const firstCracks = firstCrackTimes.map(t => formatTime(t)).join(', ');
const secondCracks = secondCrackTimes.map(t => formatTime(t)).join(', ');
const stopDisplay = stopTime ? `Stop: ${formatTime(stopTime)}` : '';
crackTimesDisplay.innerHTML = [
firstCracks ? `1st Crack: ${firstCracks}` : '',
secondCracks ? `2nd Crack: ${secondCracks}` : '',
stopDisplay
].filter(Boolean).join('<br>');
}
function startTimer() {
if (!timerInterval) {
startTime = Date.now() - (stopTime ? stopTime : 0);
timerInterval = setInterval(() => {
updateTimerDisplay(Date.now() - startTime);
}, 1000);
}
}
function recordCrack(crackArray) {
if (timerInterval && crackArray.length < 2) {
crackArray.push(Date.now() - startTime);
updateCrackTimesDisplay();
}
}
function stopResetTimer() {
if (timerInterval) {
clearInterval(timerInterval);
stopTime = Date.now() - startTime;
timerInterval = null;
updateCrackTimesDisplay();
resetCount = 0;
} else {
resetCount++;
if (resetCount === 3) {
timerDisplay.textContent = '00:00';
firstCrackTimes = [];
secondCrackTimes = [];
stopTime = 0;
updateCrackTimesDisplay();
resetCount = 0;
}
}
}
timerContainer.querySelector('.start').addEventListener('click', startTimer);
timerContainer.querySelector('.first-crack').addEventListener('click', () => recordCrack(firstCrackTimes));
timerContainer.querySelector('.second-crack').addEventListener('click', () => recordCrack(secondCrackTimes));
timerContainer.querySelector('.stop-reset').addEventListener('click', stopResetTimer);
});
});
function calculate() {
const before = parseFloat(document.getElementById('before').value);
const after = parseFloat(document.getElementById('after').value);
if (isNaN(before) || isNaN(after) || after === 0) {
alert("有効な数値を入力してください");
return;
}
const roastIndex = (before / after).toFixed(3);
document.getElementById('roastIndex').textContent = roastIndex;
}