⬅ ギャラリーに戻る
✏️ '
timer2.html
' を修正中... (終わったら保存を押してね)
📷 素材アップロード
アップロード
🛠️ プログラム作成
[修正モード]
ファイル名:
.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>進化版!わんわんタイマー</title> <link href="https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c:wght@400;800&display=swap" rel="stylesheet"> <style> :root { --primary-color: #FFB347; /* 優しいオレンジ */ --secondary-color: #F57C00; /* 濃いオレンジ */ --bg-color: #FFF9F0; /* クリーム色 */ --bowl-color: #8D6E63; /* お皿の色 */ --text-color: #5D4037; --bone-color: #FBE9E7; /* 骨の色 */ } body { font-family: 'M PLUS Rounded 1c', sans-serif; background-color: var(--bg-color); color: var(--text-color); display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; margin: 0; } h1 { font-size: 1.6rem; font-weight: 800; margin: 10px 0; color: var(--text-color); } /* --- メインのビジュアルエリア --- */ .stage-container { background: white; padding: 20px 30px 30px; border-radius: 30px; box-shadow: 0 10px 25px rgba(0,0,0,0.1); display: flex; flex-direction: column; align-items: center; margin-bottom: 20px; } .stage { position: relative; width: 300px; height: 280px; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; } /* キャラクターエリア */ .character-wrapper { position: relative; z-index: 10; margin-bottom: -10px; /* お皿に少し被せる */ } .character { font-size: 100px; line-height: 1; display: block; filter: drop-shadow(0 4px 4px rgba(0,0,0,0.15)); transition: transform 0.3s; } /* 激しくもぐもぐするアニメーション */ .character.eating { animation: crazyMunch 0.25s infinite alternate ease-in-out; } @keyframes crazyMunch { 0% { transform: translateY(0) rotate(0deg) scaleX(1); } 50% { transform: translateY(5px) rotate(-2deg) scaleX(1.05); } 100% { transform: translateY(2px) rotate(2deg) scaleX(0.98); } } /* 食べかすエフェクト */ .crumbs { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); width: 60px; height: 30px; pointer-events: none; opacity: 0; } .character.eating + .crumbs { opacity: 1; animation: crumbsFly 0.5s infinite; } .crumbs::before, .crumbs::after { content: '・'; position: absolute; color: var(--secondary-color); font-weight: bold; } .crumbs::before { left: 10px; animation: crumb1 0.3s infinite alternate; } .crumbs::after { right: 10px; animation: crumb2 0.4s infinite alternate-reverse; } @keyframes crumbsFly { 0% { opacity: 0.5; translateY(0); } 100% { opacity: 1; translateY(-5px); } } @keyframes crumb1 { 0% { transform: translate(0,0); } 100% { transform: translate(-5px, -10px); } } @keyframes crumb2 { 0% { transform: translate(0,0); } 100% { transform: translate(5px, -8px); } } /* 完了時の喜び */ .character.happy { animation: dogHappy 0.6s infinite alternate ease-in-out; } @keyframes dogHappy { 0% { transform: translateY(0) rotate(0); } 100% { transform: translateY(-20px) rotate(5deg); } } /* ご飯エリア(お皿と中身) */ .bowl-container { position: relative; width: 240px; display: flex; flex-direction: column; align-items: center; } /* 個別のご飯アイテムが並ぶ場所 */ .food-items-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px; width: 90%; margin-bottom: 5px; z-index: 5; min-height: 30px; /* 空でも高さを確保 */ } /* 個別の骨アイテム */ .food-item { font-size: 24px; transition: all 0.5s ease; opacity: 1; transform: scale(1); } /* 今食べている骨 */ .food-item.current { animation: itemShake 0.5s infinite; filter: brightness(1.2); } @keyframes itemShake { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(2px); } } /* 食べ終わった骨 */ .food-item.eaten { opacity: 0; transform: scale(0.5) translateY(-20px); } .bowl { width: 220px; height: 45px; background-color: var(--bowl-color); border-radius: 15px 15px 50px 50px; box-shadow: inset 0 -5px 10px rgba(0,0,0,0.2), 0 5px 10px rgba(0,0,0,0.1); z-index: 1; } /* 吹き出しメッセージ */ .message-box { position: absolute; top: 0; background: white; padding: 10px 20px; border-radius: 20px; box-shadow: 0 4px 10px rgba(0,0,0,0.1); font-weight: bold; font-size: 0.9rem; color: var(--text-color); opacity: 0; transform: translateY(10px); transition: all 0.3s; white-space: nowrap; } .message-box.show { opacity: 1; transform: translateY(0); } .message-box::after { content: ''; position: absolute; bottom: -8px; left: 50%; transform: translateX(-50%); border-width: 8px 8px 0; border-style: solid; border-color: white transparent transparent; } /* --- タイマー表示と操作パネル --- */ .timer-display { font-size: 4rem; font-weight: 800; color: var(--primary-color); margin: 10px 0; font-feature-settings: "tnum"; text-shadow: 2px 2px 0px #FFF; } /* 設定エリア */ .settings-wrapper { background: #FFF3E0; padding: 15px 25px; border-radius: 20px; margin-bottom: 20px; transition: all 0.3s; } .settings-wrapper.hidden { opacity: 0; visibility: hidden; transform: translateY(-20px); height: 0; padding: 0; margin: 0; } .settings-row { display: flex; align-items: center; gap: 10px; margin: 10px 0; } .label-txt { font-weight: bold; font-size: 0.9rem; } select, input[type="number"] { padding: 8px 12px; border: 2px solid var(--primary-color); border-radius: 10px; font-size: 1rem; font-family: inherit; outline: none; background-color: #FFF; color: var(--text-color); text-align: center; } select { cursor: pointer; text-align: left; } input[type="number"] { width: 50px; } /* コントロールボタン */ .controls { display: flex; gap: 15px; } button { padding: 12px 25px; border: none; border-radius: 50px; font-size: 1.1rem; font-weight: 800; cursor: pointer; font-family: inherit; transition: all 0.1s; box-shadow: 0 4px 0 rgba(0,0,0,0.2); } button:active { transform: translateY(2px); box-shadow: 0 2px 0 rgba(0,0,0,0.2); } .btn-start { background-color: var(--primary-color); color: white; } .btn-reset { background-color: #CFD8DC; color: #455A64; } </style> </head> <body> <h1>進化版!わんわんタイマー 🐾</h1> <div class="stage-container"> <div class="stage"> <div class="message-box" id="messageBox">準備OKだワン!</div> <div class="character-wrapper"> <div class="character" id="character">🐕</div> <div class="crumbs"></div> </div> <div class="bowl-container"> <div class="food-items-grid" id="foodGrid"></div> <div class="bowl"></div> </div> </div> <div class="timer-display" id="timerDisplay">25:00</div> </div> <div class="settings-wrapper" id="settingsWrapper"> <div class="settings-row"> <label for="breedSelect" class="label-txt">犬種:</label> <select id="breedSelect"> </select> </div> <div class="settings-row"> <label class="label-txt">時間:</label> <input type="number" id="inputMin" min="0" max="180" value="25"> <span>分</span> <input type="number" id="inputSec" min="0" max="59" value="00"> <span>秒</span> </div> </div> <div class="controls"> <button class="btn-start" id="startBtn" onclick="toggleTimer()">スタート</button> <button class="btn-reset" onclick="resetTimer()">リセット</button> </div> <script> // --- 犬のデータ --- const dogBreeds = [ { id: 'shiba', name: '柴犬', emoji: '🐕' }, { id: 'poodle', name: 'プードル', emoji: '🐩' }, { id: 'chiwawa', name: 'チワワ風', emoji: '🐶' }, { id: 'guide', name: 'レトリバー', emoji: '🦮' } ]; // おやつの種類(今回は骨で統一) const foodIcon = '🍖'; let timerInterval; let totalSeconds = 0; let remainingSeconds = 0; let isRunning = false; let totalFoodItems = 0; // おやつの総数 // 要素の取得 const character = document.getElementById('character'); const breedSelect = document.getElementById('breedSelect'); const foodGrid = document.getElementById('foodGrid'); const messageBox = document.getElementById('messageBox'); const timerDisplay = document.getElementById('timerDisplay'); const startBtn = document.getElementById('startBtn'); const settingsWrapper = document.getElementById('settingsWrapper'); const inputMin = document.getElementById('inputMin'); const inputSec = document.getElementById('inputSec'); // --- 初期化処理 --- dogBreeds.forEach(breed => { const option = document.createElement('option'); option.value = breed.id; option.textContent = `${breed.emoji} ${breed.name}`; breedSelect.appendChild(option); }); updateDisplay(parseInt(inputMin.value) * 60 + parseInt(inputSec.value)); // --- イベントリスナー --- breedSelect.addEventListener('change', () => { const selected = dogBreeds.find(b => b.id === breedSelect.value); if (selected) { character.textContent = selected.emoji; character.classList.remove('happy'); void character.offsetWidth; character.classList.add('happy'); setTimeout(() => character.classList.remove('happy'), 1000); } }); inputMin.addEventListener('change', updateInputDisplay); inputSec.addEventListener('change', updateInputDisplay); // --- 関数群 --- function updateInputDisplay() { let m = Math.max(0, parseInt(inputMin.value) || 0); let s = Math.max(0, Math.min(59, parseInt(inputSec.value) || 0)); inputMin.value = m; inputSec.value = String(s).padStart(2, '0'); updateDisplay(m * 60 + s); } function toggleTimer() { isRunning ? pauseTimer() : startTimer(); } function startTimer() { // 初回スタート時 if (remainingSeconds === 0 && !timerInterval) { let m = parseInt(inputMin.value) || 0; let s = parseInt(inputSec.value) || 0; totalSeconds = m * 60 + s; remainingSeconds = totalSeconds; if (totalSeconds <= 0) return; // おやつを準備する prepareFood(totalSeconds); settingsWrapper.classList.add('hidden'); } if (remainingSeconds <= 0) return; isRunning = true; startBtn.textContent = "一時停止"; startBtn.style.backgroundColor = "#FF8A65"; character.classList.remove('happy'); character.classList.add('eating'); // もぐもぐ開始 showMessage("ガツガツ!美味しいワン!"); timerInterval = setInterval(() => { remainingSeconds--; updateDisplay(remainingSeconds); updateFoodProgress(); // おやつの状況を更新 if (remainingSeconds <= 0) { finishTimer(); } }, 1000); updateFoodProgress(); } function pauseTimer() { isRunning = false; clearInterval(timerInterval); startBtn.textContent = "再開"; startBtn.style.backgroundColor = ""; character.classList.remove('eating'); // もぐもぐ停止 showMessage("待て!...まだかワン?"); // 現在食べているアイテムの揺れを止める const currentItem = document.querySelector('.food-item.current'); if(currentItem) currentItem.classList.remove('current'); } function resetTimer() { pauseTimer(); startBtn.textContent = "スタート"; settingsWrapper.classList.remove('hidden'); let m = parseInt(inputMin.value) || 0; let s = parseInt(inputSec.value) || 0; totalSeconds = 0; remainingSeconds = 0; timerInterval = null; updateDisplay(m * 60 + s); foodGrid.innerHTML = ''; // おやつを片付ける showMessage("準備OKだワン!"); character.classList.remove('happy'); } function finishTimer() { clearInterval(timerInterval); isRunning = false; startBtn.textContent = "完了!"; character.classList.remove('eating'); updateDisplay(0); updateFoodProgress(); // 最後の一つを確実に消す showMessage("完食!美味しかったワン!✨"); character.classList.add('happy'); } function updateDisplay(seconds) { const m = Math.floor(seconds / 60); const s = seconds % 60; timerDisplay.textContent = `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`; } // おやつを準備する関数 function prepareFood(seconds) { foodGrid.innerHTML = ''; // 時間に応じて個数を決定(例: 1分あたり0.5個、最低1個、最大15個) let count = Math.max(1, Math.floor(seconds / 120)); if (seconds > 0 && count < 3) count = 3; // 短い時間でも最低3個は見せる if (count > 15) count = 15; // 多すぎると溢れるので制限 totalFoodItems = count; for (let i = 0; i < totalFoodItems; i++) { const item = document.createElement('div'); item.classList.add('food-item'); item.textContent = foodIcon; foodGrid.appendChild(item); } } // おやつの進捗を更新する関数 function updateFoodProgress() { if (totalSeconds <= 0 || totalFoodItems === 0) return; // 現在の進捗率 (1.0 -> 0.0) const progress = remainingSeconds / totalSeconds; // まだ残っているべきおやつの数 const itemsRemaining = Math.ceil(progress * totalFoodItems); const items = foodGrid.children; for (let i = 0; i < totalFoodItems; i++) { // 食べ終わったアイテム(インデックスが残り数以上のもの) if (i >= itemsRemaining) { items[i].classList.add('eaten'); items[i].classList.remove('current'); } // 今まさに食べているアイテム(残っている中で最後のひとつ) else if (i === itemsRemaining - 1 && isRunning) { items[i].classList.add('current'); } // まだ手をつけていないアイテム else { items[i].classList.remove('eaten', 'current'); } } } function showMessage(text) { if(!text) { messageBox.classList.remove('show'); return; } messageBox.textContent = text; messageBox.classList.add('show'); } </script> </body> </html>
上書き保存する
キャンセルして新規作成に戻る
📂 APP の作品
ProVocab - 縦スクロール暗記版
eigo2.html (01/15 10:37)
✏️ 修正
🌏 公開
🗑️
ProVocab - 語源マスター版
eigo.html (01/15 10:37)
✏️ 修正
🌏 公開
🗑️
進化版!わんわんタイマー
timer3.html (01/14 13:38)
✏️ 修正
🌏 公開
🗑️
わんわん応援タイマー
timer.html (01/14 12:49)
✏️ 修正
🌏 公開
🗑️
進化版!わんわんタイマー
timer2.html (01/14 12:17)
✏️ 修正
🌏 公開
🗑️