滑鼠練習原始碼
滑鼠練習程式 是利用 Gemini 產生的程式,以下為 html 原始碼。
需要的人請自行取用。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>滑鼠練習程式</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.task-container {
display: none;
}
.task-container.active {
display: flex;
}
.point-circle {
transition: all 0.2s ease-in-out;
}
.drag-source {
cursor: grab;
transition: transform 0.1s ease-out;
}
.drag-source:active {
cursor: grabbing;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen flex items-center justify-center p-4">
<div id="app" class="bg-white p-8 rounded-2xl shadow-xl w-full max-w-2xl text-center">
<h1 class="text-3xl md:text-4xl font-bold text-gray-800 mb-2">滑鼠練習程式</h1>
<p class="text-gray-600 mb-8">讓我們來練習滑鼠的基本操作。<br>每完成10次練習,會顯示你的過關時間</p>
<!-- Welcome Screen -->
<div id="welcome-screen" class="space-y-6">
<p class="text-gray-700 text-lg">選擇您想練習的操作:</p>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<button onclick="startTask('click')" class="bg-blue-500 text-white font-bold py-3 px-6 rounded-xl shadow-lg hover:bg-blue-600 transition-colors">
點一下練習
</button>
<button onclick="startTask('double-click')" class="bg-green-500 text-white font-bold py-3 px-6 rounded-xl shadow-lg hover:bg-green-600 transition-colors">
點兩下練習
</button>
<button onclick="startTask('right-click')" class="bg-purple-500 text-white font-bold py-3 px-6 rounded-xl shadow-lg hover:bg-purple-600 transition-colors">
右鍵練習
</button>
<button onclick="startTask('drag')" class="bg-indigo-500 text-white font-bold py-3 px-6 rounded-xl shadow-lg hover:bg-indigo-600 transition-colors">
拖曳練習
</button>
</div>
</div>
<!-- Task Containers -->
<div id="click-task" class="task-container flex flex-col items-center">
<h2 class="text-2xl font-bold mb-4 text-blue-700">點一下練習</h2>
<p class="text-gray-600 mb-4">請在圓圈出現時,用滑鼠左鍵「點一下」。</p>
<div id="click-area" class="w-full h-80 bg-gray-200 rounded-xl flex items-center justify-center relative cursor-crosshair">
<div id="click-target" class="point-circle w-16 h-16 bg-blue-400 rounded-full absolute"></div>
</div>
<p id="click-score" class="text-lg font-semibold mt-4">點擊次數:0</p>
<button onclick="endTask(false)" class="mt-6 bg-red-500 text-white font-bold py-2 px-4 rounded-xl shadow-lg hover:bg-red-600 transition-colors">
結束練習
</button>
</div>
<div id="double-click-task" class="task-container flex flex-col items-center">
<h2 class="text-2xl font-bold mb-4 text-green-700">點兩下練習</h2>
<p class="text-gray-600 mb-4">請在圓圈出現時,用滑鼠左鍵「點兩下」。</p>
<div id="double-click-area" class="w-full h-80 bg-gray-200 rounded-xl flex items-center justify-center relative cursor-crosshair">
<div id="double-click-target" class="point-circle w-16 h-16 bg-green-400 rounded-full absolute"></div>
</div>
<p id="double-click-score" class="text-lg font-semibold mt-4">點擊次數:0</p>
<button onclick="endTask(false)" class="mt-6 bg-red-500 text-white font-bold py-2 px-4 rounded-xl shadow-lg hover:bg-red-600 transition-colors">
結束練習
</button>
</div>
<div id="right-click-task" class="task-container flex flex-col items-center">
<h2 class="text-2xl font-bold mb-4 text-purple-700">右鍵練習</h2>
<p class="text-gray-600 mb-4">請在圓圈出現時,用滑鼠「右鍵」點擊。</p>
<div id="right-click-area" class="w-full h-80 bg-gray-200 rounded-xl flex items-center justify-center relative cursor-crosshair">
<div id="right-click-target" class="point-circle w-16 h-16 bg-purple-400 rounded-full absolute"></div>
</div>
<p id="right-click-score" class="text-lg font-semibold mt-4">點擊次數:0</p>
<button onclick="endTask(false)" class="mt-6 bg-red-500 text-white font-bold py-2 px-4 rounded-xl shadow-lg hover:bg-red-600 transition-colors">
結束練習
</button>
</div>
<!-- New Drag Task Container -->
<div id="drag-task" class="task-container flex flex-col items-center">
<h2 class="text-2xl font-bold mb-4 text-indigo-700">拖曳練習</h2>
<p class="text-gray-600 mb-4">請將藍色圓圈拖曳到指定的虛線框內。</p>
<div id="drag-area" class="w-full h-80 bg-gray-200 rounded-xl flex items-center justify-center relative">
<div id="drag-source" class="drag-source w-16 h-16 bg-indigo-400 rounded-full absolute top-10 left-10"></div>
<div id="drop-target" class="w-24 h-24 border-4 border-dashed border-indigo-600 rounded-xl absolute"></div>
</div>
<p id="drag-score" class="text-lg font-semibold mt-4">成功次數:0</p>
<button onclick="endTask(false)" class="mt-6 bg-red-500 text-white font-bold py-2 px-4 rounded-xl shadow-lg hover:bg-red-600 transition-colors">
結束練習
</button>
</div>
<!-- Result Screen -->
<div id="result-screen" class="hidden space-y-4">
<h2 class="text-2xl font-bold text-gray-800">練習結束!</h2>
<p id="final-score" class="text-xl font-semibold text-gray-700"></p>
<button onclick="resetApp()" class="mt-4 bg-gray-400 text-white font-bold py-2 px-4 rounded-xl shadow-lg hover:bg-gray-500 transition-colors">
回到主頁
</button>
</div>
</div>
<script>
// 狀態變數
let currentTask = '';
let score = 0;
let targetInterval;
let synth;
let isDragging = false;
let startTime; // 新增:開始時間變數
const WINNING_SCORE = 10;
// 重置所有事件監聽器,確保每個練習關卡都能獨立運作
function resetEventListeners() {
// 點擊練習
const clickTarget = document.getElementById('click-target');
clickTarget.onclick = null;
// 點兩下練習
const doubleClickTarget = document.getElementById('double-click-target');
doubleClickTarget.ondblclick = null;
// 右鍵練習
const rightClickTarget = document.getElementById('right-click-target');
rightClickTarget.oncontextmenu = null;
const rightClickArea = document.getElementById('right-click-area');
rightClickArea.oncontextmenu = null;
// 拖曳練習
const dragArea = document.getElementById('drag-area');
const dragSource = document.getElementById('drag-source');
dragArea.onmousemove = null;
dragArea.onmouseup = null;
dragSource.onmousedown = null;
dragArea.onmouseleave = null;
// 清除計時器
clearInterval(targetInterval);
}
// 啟動練習
function startTask(task) {
// 每次開始新任務前,先清除所有舊的事件監聽器和計時器
resetEventListeners();
currentTask = task;
score = 0;
startTime = Date.now(); // 新增:記錄開始時間
// 修正:使用 Tone.PolySynth 替換 Tone.Synth 以支援和弦播放
if (!synth) {
synth = new Tone.PolySynth().toDestination();
}
// 由於瀏覽器安全政策,音效必須在使用者互動後啟動
Tone.start();
// 隱藏所有畫面,只顯示選擇的練習
document.getElementById('welcome-screen').style.display = 'none';
document.getElementById('click-task').style.display = 'none';
document.getElementById('double-click-task').style.display = 'none';
document.getElementById('right-click-task').style.display = 'none';
document.getElementById('drag-task').style.display = 'none';
document.getElementById('result-screen').style.display = 'none';
document.getElementById(`${task}-task`).style.display = 'flex';
document.getElementById(`${task}-score`).textContent = (task === 'drag' ? '成功次數:0' : '點擊次數:0');
// 根據不同任務設定不同的事件監聽器
if (task === 'click' || task === 'double-click' || task === 'right-click') {
const target = document.getElementById(`${task}-target`);
const area = document.getElementById(`${task}-area`);
if (task === 'click') {
target.onclick = (e) => handleEvent('click', e);
} else if (task === 'double-click') {
target.ondblclick = (e) => handleEvent('double-click', e);
} else if (task === 'right-click') {
target.oncontextmenu = (e) => {
e.preventDefault();
handleEvent('right-click', e);
};
area.oncontextmenu = (e) => e.preventDefault();
}
moveTarget(target, area);
targetInterval = setInterval(() => moveTarget(target, area), 1500);
} else if (task === 'drag') {
const source = document.getElementById('drag-source');
const area = document.getElementById('drag-area');
const target = document.getElementById('drop-target');
// 初始化拖曳目標和來源的位置
moveDragElements(source, target, area);
// 註冊拖曳事件監聽器
source.onmousedown = (e) => {
isDragging = true;
// 防止圖片拖曳
e.preventDefault();
// 紀錄滑鼠在圓圈內的偏移量
const offsetX = e.clientX - source.getBoundingClientRect().left;
const offsetY = e.clientY - source.getBoundingClientRect().top;
area.onmousemove = (moveEvent) => {
if (isDragging) {
const newX = moveEvent.clientX - area.getBoundingClientRect().left - offsetX;
const newY = moveEvent.clientY - area.getBoundingClientRect().top - offsetY;
source.style.left = `${newX}px`;
source.style.top = `${newY}px`;
}
};
area.onmouseup = () => {
isDragging = false;
area.onmousemove = null;
const dropTargetRect = target.getBoundingClientRect();
const dragSourceRect = source.getBoundingClientRect();
// 檢查圓圈是否在虛線框內
if (
dragSourceRect.left >= dropTargetRect.left &&
dragSourceRect.right <= dropTargetRect.right &&
dragSourceRect.top >= dropTargetRect.top &&
dragSourceRect.bottom <= dropTargetRect.bottom
) {
score++;
document.getElementById('drag-score').textContent = `成功次數:${score}`;
synth.triggerAttackRelease("G4", "8n");
// 成功後重置位置
moveDragElements(source, target, area);
// 新增:檢查是否達成過關分數
if (score >= WINNING_SCORE) {
endTask(true);
}
} else {
// 失敗後回到初始位置
source.style.left = '10px';
source.style.top = '10px';
}
};
};
// 在拖曳區域外放開滑鼠時也停止拖曳
area.onmouseleave = () => {
if (isDragging) {
isDragging = false;
area.onmousemove = null;
}
};
}
}
// 處理點擊事件
function handleEvent(task, e) {
const target = document.getElementById(`${task}-target`);
if (e.target.id === target.id) {
score++;
document.getElementById(`${task}-score`).textContent = `點擊次數:${score}`;
// 播放音效
synth.triggerAttackRelease("C4", "8n");
// 成功後移動圓圈
moveTarget(target, document.getElementById(`${task}-area`));
// 新增:檢查是否達成過關分數
if (score >= WINNING_SCORE) {
endTask(true);
}
}
}
// 專為點擊練習所設計,使圓圈隨機移動
function moveTarget(target, area) {
const areaRect = area.getBoundingClientRect();
const targetRect = target.getBoundingClientRect();
const newX = Math.random() * (areaRect.width - targetRect.width);
const newY = Math.random() * (areaRect.height - targetRect.height);
target.style.left = `${newX}px`;
target.style.top = `${newY}px`;
}
// 專為拖曳練習所設計,移動拖曳目標和來源
function moveDragElements(source, target, area) {
const areaRect = area.getBoundingClientRect();
const targetRect = target.getBoundingClientRect();
// 設定虛線框的隨機位置
const margin = 20;
const newTargetX = Math.random() * (areaRect.width - targetRect.width - margin * 2) + margin;
const newTargetY = Math.random() * (areaRect.height - targetRect.height - margin * 2) + margin;
target.style.left = `${newTargetX}px`;
target.style.top = `${newTargetY}px`;
// 設定拖曳圓圈的固定初始位置
source.style.left = '10px';
source.style.top = '10px';
}
// 結束練習
function endTask(isVictory) {
resetEventListeners(); // 停止圓圈移動
document.getElementById('click-task').style.display = 'none';
document.getElementById('double-click-task').style.display = 'none';
document.getElementById('right-click-task').style.display = 'none';
document.getElementById('drag-task').style.display = 'none';
const resultScreen = document.getElementById('result-screen');
const finalScore = document.getElementById('final-score');
let taskText = '';
if (currentTask === 'click') taskText = '點一下練習';
if (currentTask === 'double-click') taskText = '點兩下練習';
if (currentTask === 'right-click') taskText = '右鍵練習';
if (currentTask === 'drag') taskText = '拖曳練習';
// 根據是否過關顯示不同訊息和播放不同音效
if (isVictory) {
const elapsedTime = (Date.now() - startTime) / 1000; // 計算經過時間
finalScore.textContent = `恭喜過關!您花了 ${elapsedTime.toFixed(2)} 秒完成!`;
synth.triggerAttackRelease(["C4", "E4", "G4"], "1s"); // 播放和弦音效
} else {
finalScore.textContent = `在 ${taskText} 中,您成功點擊了 ${score} 次!`;
}
resultScreen.style.display = 'block';
}
// 重置程式
function resetApp() {
document.getElementById('result-screen').style.display = 'none';
document.getElementById('welcome-screen').style.display = 'block';
}
// 初始載入時隱藏所有任務畫面
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('click-task').style.display = 'none';
document.getElementById('double-click-task').style.display = 'none';
document.getElementById('right-click-task').style.display = 'none';
document.getElementById('drag-task').style.display = 'none';
document.getElementById('result-screen').style.display = 'none';
});
</script>
</body>
</html>