junkerstock
 todo-test10 

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Mobile Ready TODO List v2 + Neko</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script>
<style>
/* --- 既存のスタイル (省略せずにそのまま使います) --- */
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: #f0f2f5;
display: flex;
justify-content: center;
padding: 20px 10px;
margin: 0;
-webkit-tap-highlight-color: transparent;
}

.container {
width: 100%;
max-width: 500px;
background: #fff;
padding: 20px;
border-radius: 12px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
position: relative;
z-index: 1; /* 猫より後ろにするため明示 */
}

.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}

.header-left {
display: flex;
align-items: center;
gap: 10px;
}

h2 { margin: 0; color: #333; font-size: 1.5rem; }

.header-btn {
border: none;
padding: 6px 12px;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
color: #333;
transition: opacity 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.header-btn:active { opacity: 0.8; }

#edit-toggle-btn { background-color: #6c757d; color: white; font-size: 14px; }
#edit-toggle-btn.active { background-color: #28a745; }
#random-btn { background-color: #ffc107; }
#history-btn { background-color: #6f42c1; color: white; }

.input-group {
display: flex;
margin-bottom: 20px;
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
border-radius: 8px;
overflow: hidden;
}

#task-input {
flex-grow: 1;
padding: 12px 15px;
border: 1px solid #ddd;
border-right: none;
outline: none;
font-size: 16px;
border-radius: 8px 0 0 8px;
}

#add-btn {
padding: 0 20px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
font-weight: bold;
font-size: 16px;
border-radius: 0 8px 8px 0;
min-width: 60px;
}

.todo-list { list-style: none; padding: 0; margin: 0; min-height: 50px; }

.todo-item {
background-color: #fff;
border-bottom: 1px solid #eee;
padding: 8px 10px;
display: flex;
justify-content: space-between;
align-items: center;
user-select: none;
touch-action: pan-y;
min-height: 30px;
}

.todo-text {
flex-grow: 1;
font-size: 16px;
padding-right: 10px;
word-break: break-all;
line-height: 1.3;
}

.edit-input {
flex-grow: 1;
font-size: 16px;
padding: 4px 8px;
margin-right: 10px;
border: 2px solid #17a2b8;
border-radius: 4px;
outline: none;
width: 100px;
}

.handle, .delete-btn, .modify-btn { display: none; }

.edit-mode .handle {
display: inline-block;
color: #ccc;
margin-right: 10px;
cursor: grab;
font-size: 20px;
padding: 0 5px;
}

.btn-group { display: flex; gap: 5px; align-items: center; }

.edit-mode .modify-btn,
.edit-mode .delete-btn {
display: block;
color: white;
border: none;
padding: 4px 8px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
flex-shrink: 0;
}

.edit-mode .modify-btn { background-color: #17a2b8; }
.edit-mode .modify-btn.saving { background-color: #007bff; }
.edit-mode .delete-btn { background-color: #ff4d4d; }

.sortable-ghost { opacity: 0.4; background-color: #e3f2fd; }
.sortable-drag { background: #fff; box-shadow: 0 5px 15px rgba(0,0,0,0.15); opacity: 1; }

/* モーダル共通 */
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
justify-content: center;
align-items: center;
backdrop-filter: blur(2px);
}
.modal-content {
background-color: #fff;
padding: 20px;
border-radius: 12px;
width: 90%;
max-width: 450px;
max-height: 90vh;
display: flex;
flex-direction: column;
box-shadow: 0 5px 20px rgba(0,0,0,0.25);
animation: popIn 0.2s ease-out;
}
@keyframes popIn {
from { transform: scale(0.9); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}

.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.modal-header h3 { margin: 0; font-size: 1.2rem; color: #333; }
.close-btn { background: none; border: none; font-size: 24px; cursor: pointer; color: #666; padding: 0 5px; }

#history-list {
list-style: none;
padding: 0;
margin: 0;
overflow-y: auto;
flex-grow: 1;
}
.history-item {
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
font-size: 14px;
}
.history-header {
display: flex;
justify-content: space-between;
margin-bottom: 2px;
}
.history-date { font-size: 11px; color: #888; }
.history-symbol { font-weight: bold; color: #6f42c1; margin-left: 10px; }

.history-text {
color: #333;
font-weight: 500;
word-break: break-all;
margin-bottom: 2px;
}
.history-memo {
font-size: 12px;
color: #666;
background: #f8f9fa;
padding: 2px 6px;
border-radius: 4px;
display: inline-block;
margin-top: 2px;
}
.empty-history { text-align: center; color: #999; padding: 20px 0; }

.history-footer {
margin-top: 15px;
border-top: 1px solid #eee;
padding-top: 10px;
}

#history-menu-toggle {
width: 100%;
background-color: #6c757d;
color: white;
border: none;
padding: 10px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
}

#history-actions-container {
display: none;
flex-direction: column;
gap: 8px;
margin-top: 10px;
animation: fadeIn 0.3s;
}
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.action-btn {
padding: 10px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
}

#btn-copy-history { background-color: #17a2b8; color: white; }
#btn-download-history { background-color: #28a745; color: white; }
#btn-clear-history { background-color: #dc3545; color: white; }

.confirm-task-preview {
font-weight: bold;
color: #007bff;
display: block;
margin-bottom: 15px;
background: #f8f9fa;
padding: 10px;
border-radius: 6px;
word-break: break-all;
text-align: center;
}

.assessment-group {
display: flex;
justify-content: center;
gap: 15px;
margin-bottom: 15px;
}
.assess-btn {
width: 50px;
height: 50px;
border-radius: 50%;
border: 2px solid #ddd;
background: #fff;
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
color: #ccc;
}
.assess-btn.selected {
border-color: #6f42c1;
color: #6f42c1;
background-color: #f3eefc;
font-weight: bold;
transform: scale(1.1);
}

#confirm-memo-input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 14px;
margin-bottom: 20px;
box-sizing: border-box;
}

.confirm-main-buttons { display: flex; gap: 10px; margin-bottom: 10px; }

.confirm-btn {
padding: 12px;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: bold;
cursor: pointer;
flex: 1;
}

#btn-record-delete { background-color: #6f42c1; color: white; }
#btn-just-delete { background-color: #ff4d4d; color: white; }
#btn-cancel-delete { background-color: #e9ecef; color: #333; width: 100%; }

</style>
</head>
<body>

<div class="container" id="main-container">
<div class="header">
<div class="header-left">
<h2>My Tasks</h2>
<button id="random-btn" class="header-btn" title="ランダム">🎲</button>
<button id="history-btn" class="header-btn" title="履歴">📖</button>
</div>
<button id="edit-toggle-btn" class="header-btn">編集</button>
</div>

<div class="input-group">
<input type="text" id="task-input" placeholder="タスクを入力..." autocomplete="off">
<button id="add-btn">追加</button>
</div>
<ul id="todo-list" class="todo-list"></ul>
</div>

<div id="history-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>完了・削除履歴</h3>
<button class="close-btn" id="close-history">×</button>
</div>
<ul id="history-list"></ul>

<div class="history-footer">
<button id="history-menu-toggle">履歴メニュー</button>
<div id="history-actions-container">
<button id="btn-copy-history" class="action-btn">📋 クリップボードにコピー</button>
<button id="btn-download-history" class="action-btn">💾 CSV形式で保存</button>
<button id="btn-clear-history" class="action-btn">🗑️ 履歴を完全に消去</button>
</div>
</div>
</div>
</div>

<div id="confirm-modal" class="modal">
<div class="modal-content" style="max-width: 380px;">
<div class="modal-header" style="border:none; padding-bottom:0; margin-bottom:10px;">
<h3 style="font-size:1rem; color:#666;">タスクを削除・記録</h3>
</div>

<span id="confirm-task-name" class="confirm-task-preview"></span>

<div class="assessment-group">
<button class="assess-btn" data-val="○">○</button>
<button class="assess-btn" data-val="△">△</button>
<button class="assess-btn" data-val="✕">✕</button>
</div>

<input type="text" id="confirm-memo-input" placeholder="ひとことメモ(任意)" autocomplete="off">

<div class="confirm-actions">
<div class="confirm-main-buttons">
<button id="btn-record-delete" class="confirm-btn">📖 記録して削除</button>
<button id="btn-just-delete" class="confirm-btn">🗑️ 記録せず削除</button>
</div>
<button id="btn-cancel-delete" class="confirm-btn">キャンセル</button>
</div>
</div>
</div>

<script>
// 既存のJavaScriptコード (省略せずにそのまま)
const input = document.getElementById('task-input');
const addBtn = document.getElementById('add-btn');
const list = document.getElementById('todo-list');
const editToggleBtn = document.getElementById('edit-toggle-btn');
const randomBtn = document.getElementById('random-btn');
const historyBtn = document.getElementById('history-btn');
const container = document.getElementById('main-container');

// 履歴モーダル
const historyModal = document.getElementById('history-modal');
const closeHistoryBtn = document.getElementById('close-history');
const historyList = document.getElementById('history-list');

// ▼ 新規追加した履歴ボタン類
const historyMenuToggle = document.getElementById('history-menu-toggle');
const historyActionsContainer = document.getElementById('history-actions-container');
const btnCopyHistory = document.getElementById('btn-copy-history');
const btnDownloadHistory = document.getElementById('btn-download-history');
const btnClearHistory = document.getElementById('btn-clear-history');

// 削除確認モーダル要素
const confirmModal = document.getElementById('confirm-modal');
const confirmTaskName = document.getElementById('confirm-task-name');
const confirmMemoInput = document.getElementById('confirm-memo-input');
const assessBtns = document.querySelectorAll('.assess-btn');

const btnRecordDelete = document.getElementById('btn-record-delete');
const btnJustDelete = document.getElementById('btn-just-delete');
const btnCancelDelete = document.getElementById('btn-cancel-delete');

let pendingDeleteTarget = null;
let selectedSymbol = '';

function getCurrentTime() {
const now = new Date();
const y = now.getFullYear();
const m = (now.getMonth() + 1).toString().padStart(2, '0');
const d = now.getDate().toString().padStart(2, '0');
const h = now.getHours().toString().padStart(2, '0');
const min = now.getMinutes().toString().padStart(2, '0');
return `${y}/${m}/${d} ${h}:${min}`;
}

function saveToHistory(text, symbol, memo) {
const historyData = JSON.parse(localStorage.getItem('myTodoHistory') || '[]');
const newItem = {
text: text,
date: getCurrentTime(),
symbol: symbol || '',
memo: memo || ''
};
historyData.unshift(newItem);
localStorage.setItem('myTodoHistory', JSON.stringify(historyData));
}

function renderHistory() {
const historyData = JSON.parse(localStorage.getItem('myTodoHistory') || '[]');
historyList.innerHTML = '';
if (historyData.length === 0) {
historyList.innerHTML = '<li class="empty-history">履歴はありません</li>';
return;
}
historyData.forEach(item => {
const li = document.createElement('li');
li.className = 'history-item';

const symbolHtml = item.symbol ? `<span class="history-symbol">${item.symbol}</span>` : '';
const memoHtml = item.memo ? `<div class="history-memo">${item.memo}</div>` : '';

li.innerHTML = `
<div class="history-header">
<span class="history-date">${item.date}</span>
${symbolHtml}
</div>
<div class="history-text">${item.text}</div>
${memoHtml}
`;
historyList.appendChild(li);
});
}

// --- 履歴モーダル制御 ---
historyBtn.addEventListener('click', () => {
renderHistory();
historyModal.style.display = 'flex';
// メニューは閉じておく
historyActionsContainer.style.display = 'none';
historyMenuToggle.textContent = '履歴メニューを表示';
});
closeHistoryBtn.addEventListener('click', () => {
historyModal.style.display = 'none';
});

// ▼▼▼ 履歴アクションの実装 ▼▼▼

// 1. メニュー表示トグル
historyMenuToggle.addEventListener('click', () => {
if (historyActionsContainer.style.display === 'none' || historyActionsContainer.style.display === '') {
historyActionsContainer.style.display = 'flex';
historyMenuToggle.textContent = '履歴メニューを隠す';
} else {
historyActionsContainer.style.display = 'none';
historyMenuToggle.textContent = '履歴メニューを表示';
}
});

// 2. コピー機能
btnCopyHistory.addEventListener('click', () => {
const historyData = JSON.parse(localStorage.getItem('myTodoHistory') || '[]');
if(historyData.length === 0) {
alert('コピーする履歴がありません');
return;
}
// テキスト形式に整形
const text = historyData.map(item => {
const sym = item.symbol ? `[${item.symbol}]` : '';
const mem = item.memo ? `(${item.memo})` : '';
return `${item.date} ${sym} ${item.text} ${mem}`;
}).join('\n');

navigator.clipboard.writeText(text).then(() => {
alert('履歴をクリップボードにコピーしました!');
}).catch(err => {
alert('コピーに失敗しました');
});
});

// 3. CSV保存機能
btnDownloadHistory.addEventListener('click', () => {
const historyData = JSON.parse(localStorage.getItem('myTodoHistory') || '[]');
if(historyData.length === 0) {
alert('保存する履歴がありません');
return;
}

// CSVヘッダーとデータ作成
let csvContent = "日時,評価,タスク名,メモ\n";
historyData.forEach(item => {
// カンマや改行を含む場合はダブルクォートで囲む簡易処理
const safeText = `"${item.text.replace(/"/g, '""')}"`;
const safeMemo = `"${item.memo.replace(/"/g, '""')}"`;
csvContent += `${item.date},${item.symbol},${safeText},${safeMemo}\n`;
});

// BOMを付加してExcel等での文字化け防止
const bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
const blob = new Blob([bom, csvContent], { type: 'text/csv;charset=utf-8;' });

const link = document.createElement("a");
const url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", `todo_history_${new Date().getTime()}.csv`);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});

// 4. 完全消去機能
btnClearHistory.addEventListener('click', () => {
if(confirm('【警告】\n履歴を全て完全に消去しますか?\nこの操作は取り消せません。')) {
localStorage.removeItem('myTodoHistory');
renderHistory();
alert('履歴を消去しました。');
}
});
// ▲▲▲ 実装終わり ▲▲▲


// --- 削除確認モーダル制御 ---
assessBtns.forEach(btn => {
btn.addEventListener('click', () => {
assessBtns.forEach(b => b.classList.remove('selected'));
btn.classList.add('selected');
selectedSymbol = btn.dataset.val;
});
});

function executeDelete(record) {
if (!pendingDeleteTarget) return;

const li = pendingDeleteTarget;

let taskText = "";
const inputEl = li.querySelector('.edit-input');
if (inputEl) taskText = inputEl.value;
else taskText = li.querySelector('.todo-text').textContent;

if (record && taskText) {
const memoText = confirmMemoInput.value.trim();
saveToHistory(taskText, selectedSymbol, memoText);
}

li.style.transform = 'translateX(100%)';
li.style.transition = '0.3s';
setTimeout(() => {
li.remove();
saveTodos();
}, 300);

confirmModal.style.display = 'none';
pendingDeleteTarget = null;
}

btnRecordDelete.addEventListener('click', () => executeDelete(true));
btnJustDelete.addEventListener('click', () => executeDelete(false));
btnCancelDelete.addEventListener('click', () => {
confirmModal.style.display = 'none';
pendingDeleteTarget = null;
});

window.addEventListener('click', (e) => {
if (e.target === historyModal) historyModal.style.display = 'none';
if (e.target === confirmModal) {
confirmModal.style.display = 'none';
pendingDeleteTarget = null;
}
});

function saveTodos() {
const todos = [];
document.querySelectorAll('.todo-item').forEach(li => {
const inputEl = li.querySelector('.edit-input');
const textEl = li.querySelector('.todo-text');
if (inputEl) todos.push(inputEl.value);
else if (textEl) todos.push(textEl.textContent);
});
localStorage.setItem('myTodoList', JSON.stringify(todos));
}

function loadTodos() {
const saved = localStorage.getItem('myTodoList');
if (saved) {
const todos = JSON.parse(saved);
todos.forEach(text => addTodo(text, false));
} else {
addTodo('タスク1', false);
addTodo('タスク2', false);
addTodo('タスク3', false);
saveTodos();
}
}

let sortable = new Sortable(list, {
animation: 150,
ghostClass: 'sortable-ghost',
dragClass: 'sortable-drag',
handle: '.handle',
delay: 0,
disabled: true,
onEnd: function() { saveTodos(); }
});

randomBtn.addEventListener('click', () => {
const items = Array.from(list.children);
if (items.length < 2) return;
for (let i = items.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[items[i], items[j]] = [items[j], items[i]];
}
items.forEach(item => list.appendChild(item));
saveTodos();
});

editToggleBtn.addEventListener('click', () => {
const isEditMode = container.classList.toggle('edit-mode');
if (isEditMode) {
editToggleBtn.textContent = '完了';
editToggleBtn.classList.add('active');
sortable.option('disabled', false);
} else {
document.querySelectorAll('.modify-btn.saving').forEach(btn => btn.click());
editToggleBtn.textContent = '編集';
editToggleBtn.classList.remove('active');
sortable.option('disabled', true);
}
});

addBtn.addEventListener('click', () => {
const text = input.value.trim();
if (text) {
addTodo(text);
input.value = '';
input.focus();
}
});

input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') addBtn.click();
});

function addTodo(text, save = true) {
const li = document.createElement('li');
li.classList.add('todo-item');
li.innerHTML = `
<span class="handle">≡</span>
<span class="todo-text"></span>
<div class="btn-group">
<button class="modify-btn">修正</button>
<button class="delete-btn">削除</button>
</div>
`;

const textSpan = li.querySelector('.todo-text');
textSpan.textContent = text;

const modifyBtn = li.querySelector('.modify-btn');

modifyBtn.addEventListener('click', () => {
const isEditing = modifyBtn.classList.contains('saving');
if (!isEditing) {
const currentText = textSpan.textContent;
const inputEl = document.createElement('input');
inputEl.type = 'text';
inputEl.value = currentText;
inputEl.classList.add('edit-input');
li.replaceChild(inputEl, textSpan);
modifyBtn.textContent = '保存';
modifyBtn.classList.add('saving');
inputEl.focus();
inputEl.addEventListener('keypress', (e) => {
if (e.key === 'Enter') modifyBtn.click();
});
} else {
const inputEl = li.querySelector('.edit-input');
const newText = inputEl.value.trim();
textSpan.textContent = inputEl.value === "" ? textSpan.textContent : newText;
li.replaceChild(textSpan, inputEl);
modifyBtn.textContent = '修正';
modifyBtn.classList.remove('saving');
saveTodos();
}
});

li.querySelector('.delete-btn').addEventListener('click', (e) => {
e.stopPropagation();
pendingDeleteTarget = li;

let currentText = "";
const inputEl = li.querySelector('.edit-input');
if (inputEl) currentText = inputEl.value;
else currentText = textSpan.textContent;
confirmTaskName.textContent = currentText;

confirmMemoInput.value = '';
assessBtns.forEach(b => b.classList.remove('selected'));
selectedSymbol = '';

confirmModal.style.display = 'flex';
});

list.appendChild(li);

if (save) {
li.scrollIntoView({ behavior: 'smooth', block: 'end' });
saveTodos();
}
}

loadTodos();
</script>

<style>
/* 猫用レイヤー */
#neko-container {
position: fixed;
pointer-events: none; /* クリックを透過させる */
z-index: 9999; /* 最前面 */
/* width, height は JS で設定します */
/* 滑らかに移動させるためのtransition */
transition: top 3s linear, left 3s linear;
}
#neko-img {
width: 100%;
height: auto;
display: block;
/* ドット絵を拡大したときにぼやけないようにする設定 */
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
-ms-interpolation-mode: nearest-neighbor;
}
</style>

<div id="neko-container">
<img id="neko-img" src="./pic3/neko-rstop4.png" alt="neko">
</div>

<script>
(function() {
const nekoContainer = document.getElementById('neko-container');
const nekoImg = document.getElementById('neko-img');
const basePath = './pic3/';

// ======= 設定項目 =======
const NEKO_SIZE = 96; // 猫のサイズ (px)。元の3倍なら 32 * 3 = 96
const MOVE_DURATION = 3000; // 移動時間 (ms)
const WAIT_DURATION = 3000; // 停止後の待機時間 (ms)
const FRAME_RATE = 150; // アニメーション速度 (ms)
// ========================

// 状態管理
let currentState = 'IDLE'; // IDLE, RUN, STOPPING
let direction = 'r'; // 'r' (right) or 'l' (left)
let frameIndex = 1;
let stopFrameIndex = 1;

// 初期化処理:サイズを設定
nekoContainer.style.width = NEKO_SIZE + 'px';
nekoContainer.style.height = NEKO_SIZE + 'px';

// 初期位置を画面中央あたりにセット
let currentX = window.innerWidth / 2 - NEKO_SIZE / 2;
let currentY = window.innerHeight / 2 - NEKO_SIZE / 2;

updatePosition(currentX, currentY);

// メインループ開始
setTimeout(startBehaviorLoop, 1000);

// フレーム更新タイマー (常に回る)
setInterval(updateFrame, FRAME_RATE);

function startBehaviorLoop() {
// 1. 次の目的地をランダムに決定
// 画面外にはみ出さないように猫のサイズ分マージンを取る
const maxX = window.innerWidth - NEKO_SIZE;
const maxY = window.innerHeight - NEKO_SIZE;

const targetX = Math.max(0, Math.random() * maxX);
const targetY = Math.max(0, Math.random() * maxY);

// 2. 方向判定
direction = targetX > currentX ? 'r' : 'l';

// 3. 移動開始 (RUN状態)
currentState = 'RUN';
currentX = targetX;
currentY = targetY;

// CSS transitionを利用して移動
updatePosition(targetX, targetY);

// 4. 移動完了後の処理予約 (MOVE_DURATION後)
setTimeout(() => {
// 移動完了 -> 停止アニメーション開始 (STOPPING状態)
currentState = 'STOPPING';
stopFrameIndex = 1; // 停止アニメは1から開始

// 5. 待機時間後の処理予約 (停止動作時間 + WAIT_DURATION待機)
// 停止アニメが完了するまでの時間(約600-800ms)を考慮して次へ移動
setTimeout(() => {
currentState = 'IDLE'; // 念のため
startBehaviorLoop(); // 次の移動へ
}, WAIT_DURATION + 800);

}, MOVE_DURATION);
}

function updatePosition(x, y) {
nekoContainer.style.left = x + 'px';
nekoContainer.style.top = y + 'px';
}

function updateFrame() {
if (currentState === 'RUN') {
// 走るアニメーション (1 -> 4 -> 1...)
nekoImg.src = `${basePath}neko-${direction}run${frameIndex}.png`;
frameIndex++;
if (frameIndex > 4) frameIndex = 1;

} else if (currentState === 'STOPPING') {
// 止まるアニメーション (1 -> 4 で止まる)
if (stopFrameIndex <= 4) {
nekoImg.src = `${basePath}neko-${direction}stop${stopFrameIndex}.png`;
stopFrameIndex++;
} else {
// 4まで再生しきったらIDLE状態(4のまま固定)にする
currentState = 'IDLE';
nekoImg.src = `${basePath}neko-${direction}stop4.png`;
}

} else if (currentState === 'IDLE') {
// 待機中は stop4 の画像で固定
nekoImg.src = `${basePath}neko-${direction}stop4.png`;
}
}
})();
</script>

</body>
</html>


使用変数

) { saveTodos -------( Function )
) { const nekoContainer = document.getElementById -------( Function )
3
addBtn
addTodo -------( Function )
alt
assessBtns
autocomplete
b
basePath
blob
bom
btn
btnCancelDelete
btnClearHistory
btnCopyHistory
btnDownloadHistory
btnJustDelete
btnRecordDelete
charset
class
className
closeHistoryBtn
confirmMemoInput
confirmModal
confirmTaskName
container
content
csvContent
currentState
currentText
currentX
currentY
d
direction
display
editToggleBtn
endingDeleteTarget
err
executeDelete -------( Function )
frameIndex
FRAME_RATE
getCurrentTime -------( Function )
h
height
historyBtn
historyData
historyList
historyMenuToggle
historyModal
i
id
innerHTML
input
inputEl
isEditing
isEditMode
item
items
j
key
lang
left
length
li
link
list
loadTodos -------( Function )
m
maxX
maxY
mem
memoHtml
memoText
min
modifyBtn
MOVE_DURATION
name
nekoContainer
nekoImg
NEKO_SIZE
newItem
newText
now
placeholder
randomBtn
renderHistory -------( Function )
ryActionsContainer
safeMemo
safeText
save
saved
saveTodos -------( Function )
saveToHistory -------( Function )
scalable
scale
selectedSymbol
sortable
src
startBehaviorLoop -------( Function )
stopFrameIndex
style
sym
symbolHtml
target
targetX
targetY
taskText
text
textContent
textEl
textSpan
title
todos
top
transform
transition
type
updateFrame -------( Function )
updatePosition -------( Function )
url
val
value
visibility
WAIT_DURATION
width
y
設定項目