Files
stm32f4_rtt_5.2.0/stm32f407vet6_v2/applications/eat.html
2025-06-19 21:57:12 +08:00

682 lines
30 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<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>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#FF6B6B',
secondary: '#4ECDC4',
accent: '#FFD166',
dark: '#2A2D34',
light: '#F7FFF7'
},
fontFamily: {
inter: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.spinning {
animation: spin 3s cubic-bezier(0.17, 0.67, 0.83, 0.67);
}
.result-appear {
animation: fadeIn 0.5s ease-in-out;
}
.button-press {
animation: press 0.2s ease-in-out;
}
.heart-fall {
animation: heartFall 3s linear forwards;
}
.confetti {
animation: confettiFall 3s ease-in-out forwards;
}
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(calc(360deg * 5 + var(--random-rotation, 0deg))); }
}
@keyframes fadeIn {
0% { opacity: 0; transform: translateY(10px); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes press {
0% { transform: scale(1); }
50% { transform: scale(0.95); }
100% { transform: scale(1); }
}
@keyframes heartFall {
0% { transform: translateY(-10vh) rotate(0deg); opacity: 1; }
100% { transform: translateY(110vh) rotate(720deg); opacity: 0; }
}
@keyframes confettiFall {
0% { transform: translateY(-10vh) rotate(0deg); opacity: 1; }
100% { transform: translateY(110vh) rotate(360deg); opacity: 0; }
}
</style>
</head>
<body class="bg-gradient-to-br from-light to-secondary/10 min-h-screen font-inter text-dark flex flex-col relative overflow-x-hidden">
<header class="bg-white/80 backdrop-blur-md shadow-sm sticky top-0 z-50">
<div class="container mx-auto px-4 py-4 flex justify-between items-center">
<h1 class="text-[clamp(1.5rem,3vw,2.5rem)] font-bold text-primary flex items-center">
<i class="fa-solid fa-cutlery mr-3"></i>
<span>我可爱的老婆今天吃什么呀?(还有隐藏彩蛋等你呦!)</span>
</h1>
<button id="helpBtn" class="p-2 rounded-full hover:bg-gray-100 transition-all duration-300">
<i class="fa-solid fa-question text-dark/70"></i>
</button>
</div>
</header>
<main class="flex-grow container mx-auto px-4 py-8">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- 左侧:食物列表 -->
<div class="bg-white rounded-xl shadow-md p-6 order-2 lg:order-1">
<h2 class="text-xl font-bold mb-4 flex items-center">
<i class="fa-solid fa-list-ul mr-2 text-secondary"></i>
食物列表
</h2>
<div id="foodList" class="space-y-2 max-h-[400px] overflow-y-auto pr-2">
<div class="food-item flex items-center justify-between p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-all">
<span>烤鸭</span>
<button class="delete-btn text-red-400 hover:text-red-600 transition-colors">
<i class="fa-solid fa-trash"></i>
</button>
</div>
<div class="food-item flex items-center justify-between p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-all">
<span>红烧肉</span>
<button class="delete-btn text-red-400 hover:text-red-600 transition-colors">
<i class="fa-solid fa-trash"></i>
</button>
</div>
<div class="food-item flex items-center justify-between p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-all">
<span>炒茄子</span>
<button class="delete-btn text-red-400 hover:text-red-600 transition-colors">
<i class="fa-solid fa-trash"></i>
</button>
</div>
<div class="food-item flex items-center justify-between p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-all">
<span>汉堡</span>
<button class="delete-btn text-red-400 hover:text-red-600 transition-colors">
<i class="fa-solid fa-trash"></i>
</button>
</div>
<div class="food-item flex items-center justify-between p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-all">
<span>奶茶</span>
<button class="delete-btn text-red-400 hover:text-red-600 transition-colors">
<i class="fa-solid fa-trash"></i>
</button>
</div>
</div>
<div class="mt-6">
<h3 class="text-lg font-medium mb-2">添加食物</h3>
<div class="flex">
<input type="text" id="newFoodInput" placeholder="输入食物名称" class="flex-grow px-4 py-2 rounded-l-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-secondary/50 focus:border-secondary transition-all">
<button id="addFoodBtn" class="bg-secondary text-white px-4 py-2 rounded-r-lg hover:bg-secondary/90 transition-colors">
<i class="fa-solid fa-plus"></i>
</button>
</div>
</div>
</div>
<!-- 中间:转盘 -->
<div class="flex flex-col items-center justify-center order-1 lg:order-2">
<div class="relative">
<!-- 转盘 -->
<div id="wheelContainer" class="relative w-[min(80vw,400px)] h-[min(80vw,400px)]">
<canvas id="wheelCanvas" class="w-full h-full rounded-full shadow-lg"></canvas>
<!-- 指针 -->
<div class="absolute top-0 left-1/2 transform -translate-x-1/2 -translate-y-4 z-10">
<div class="w-8 h-12 bg-primary rounded-t-lg flex items-start justify-center shadow-md">
<div class="w-0 h-0 border-l-4 border-r-4 border-b-6 border-l-transparent border-r-transparent border-b-primary transform translate-y-[-10px]"></div>
</div>
</div>
<!-- 中心按钮 -->
<button id="spinBtn" class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-16 h-16 bg-white rounded-full shadow-lg flex items-center justify-center z-10 hover:bg-gray-50 transition-all duration-300 active:button-press">
<span class="text-primary font-bold text-lg"></span>
</button>
</div>
<!-- 结果显示 -->
<div id="resultDisplay" class="mt-8 text-center opacity-0">
<div class="bg-white p-4 rounded-xl shadow-md inline-block transform transition-all">
<h3 class="text-xl font-bold text-dark mb-1">今天就吃</h3>
<p id="resultText" class="text-3xl font-bold text-primary"></p>
</div>
</div>
</div>
</div>
<!-- 右侧:历史记录 -->
<div class="bg-white rounded-xl shadow-md p-6 order-3">
<h2 class="text-xl font-bold mb-4 flex items-center">
<i class="fa-solid fa-history mr-2 text-accent"></i>
历史记录
</h2>
<div id="historyList" class="space-y-3 max-h-[400px] overflow-y-auto pr-2">
<!-- 历史记录将通过JS动态添加 -->
<div class="text-center text-gray-500 italic">暂无记录</div>
</div>
<div class="mt-6">
<button id="clearHistoryBtn" class="w-full py-2 px-4 bg-gray-100 hover:bg-gray-200 rounded-lg text-gray-700 transition-colors flex items-center justify-center">
<i class="fa-solid fa-trash mr-2"></i>
清空历史
</button>
</div>
</div>
</div>
</main>
<footer class="bg-dark text-white py-6 mt-8">
<div class="container mx-auto px-4 text-center">
<p>© 2025 我可爱的老婆今天吃点什么呢?长按转盘随机点餐哦❤</p>
<p class="text-sm mt-2 text-gray-400">做出选择,告别纠结</p>
</div>
</footer>
<!-- 帮助模态框 -->
<div id="helpModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden flex items-center justify-center">
<div class="bg-white rounded-xl shadow-xl max-w-md w-full mx-4 overflow-hidden transform transition-all">
<div class="bg-primary text-white p-4 flex justify-between items-center">
<h3 class="text-xl font-bold">使用帮助</h3>
<button id="closeHelpBtn" class="text-white hover:text-gray-200 transition-colors">
<i class="fa-solid fa-times"></i>
</button>
</div>
<div class="p-6">
<ul class="space-y-4">
<li class="flex">
<div class="bg-primary/10 rounded-full p-2 mr-3">
<i class="fa-solid fa-list-ul text-primary"></i>
</div>
<div>
<h4 class="font-bold">管理食物列表</h4>
<p class="text-gray-600">在左侧输入框添加食物,点击垃圾桶图标删除食物</p>
</div>
</li>
<li class="flex">
<div class="bg-primary/10 rounded-full p-2 mr-3">
<i class="fa-solid fa-play text-primary"></i>
</div>
<div>
<h4 class="font-bold">开始转盘</h4>
<p class="text-gray-600">点击转盘中心的"转"按钮开始随机选择</p>
</div>
</li>
<li class="flex">
<div class="bg-primary/10 rounded-full p-2 mr-3">
<i class="fa-solid fa-history text-primary"></i>
</div>
<div>
<h4 class="font-bold">查看历史</h4>
<p class="text-gray-600">右侧面板会记录最近的选择结果</p>
</div>
</li>
<li class="flex">
<div class="bg-primary/10 rounded-full p-2 mr-3">
<i class="fa-solid fa-star text-primary"></i>
</div>
<div>
<h4 class="font-bold">隐藏彩蛋</h4>
<p class="text-gray-600">连续两次选中同一食物会触发爱心特效,连续三次选中同一食物会触发超级彩蛋!</p>
</div>
</li>
</ul>
</div>
<div class="bg-gray-50 p-4 text-center">
<button id="gotItBtn" class="bg-primary text-white px-6 py-2 rounded-lg hover:bg-primary/90 transition-colors">
知道了
</button>
</div>
</div>
</div>
<!-- 爱心容器 -->
<div id="heartsContainer" class="fixed inset-0 pointer-events-none z-40 hidden"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 获取元素
const canvas = document.getElementById('wheelCanvas');
const ctx = canvas.getContext('2d');
const spinBtn = document.getElementById('spinBtn');
const foodList = document.getElementById('foodList');
const newFoodInput = document.getElementById('newFoodInput');
const addFoodBtn = document.getElementById('addFoodBtn');
const resultDisplay = document.getElementById('resultDisplay');
const resultText = document.getElementById('resultText');
const historyList = document.getElementById('historyList');
const clearHistoryBtn = document.getElementById('clearHistoryBtn');
const helpBtn = document.getElementById('helpBtn');
const helpModal = document.getElementById('helpModal');
const closeHelpBtn = document.getElementById('closeHelpBtn');
const gotItBtn = document.getElementById('gotItBtn');
const heartsContainer = document.getElementById('heartsContainer');
// 设置Canvas尺寸
const setCanvasSize = () => {
const container = canvas.parentElement;
const size = Math.min(container.clientWidth, container.clientHeight);
canvas.width = size;
canvas.height = size;
};
setCanvasSize();
window.addEventListener('resize', setCanvasSize);
// 食物数据
let foods = ['烤鸭', '红烧肉', '炒茄子', '汉堡', '奶茶']; // 修改了食物列表
let isSpinning = false;
let rotation = 0;
let resultIndex = 0;
let animationId = null;
// 历史记录
let history = [];
// 连续选择记录
let consecutiveResults = [];
// 绘制转盘
const drawWheel = () => {
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = Math.min(centerX, centerY) - 20;
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 如果没有食物,显示提示
if (foods.length === 0) {
ctx.fillStyle = '#f0f0f0';
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#999';
ctx.font = 'bold 16px Inter';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('请添加食物', centerX, centerY);
return;
}
// 计算每个扇形的角度
const sliceAngle = (Math.PI * 2) / foods.length;
// 颜色数组
const colors = [
'#FF6B6B', '#4ECDC4', '#FFD166', '#6A0572', '#1A936F',
'#F7B801', '#E63946', '#457B9D', '#1D3557', '#A8DADC'
];
// 绘制每个扇形
foods.forEach((food, index) => {
const color = colors[index % colors.length];
const startAngle = sliceAngle * index + rotation;
const endAngle = startAngle + sliceAngle;
// 绘制扇形
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.closePath();
ctx.fill();
// 添加边框
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.stroke();
// 绘制文字
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(startAngle + sliceAngle / 2);
ctx.fillStyle = '#fff';
ctx.font = 'bold 14px Inter';
ctx.textAlign = 'center';
ctx.fillText(food, radius * 0.7, 0);
ctx.restore();
});
// 绘制中心圆
ctx.fillStyle = '#fff';
ctx.beginPath();
ctx.arc(centerX, centerY, 30, 0, Math.PI * 2);
ctx.fill();
};
// 旋转转盘
const spinWheel = () => {
// 修复检查动画ID并取消现有动画
if (animationId) {
cancelAnimationFrame(animationId);
}
if (isSpinning || foods.length < 2) return;
isSpinning = true;
resultDisplay.style.opacity = '0';
// 优化:根据食物数量调整旋转速度和圈数
const baseSpins = 3;
const spinVariation = Math.floor(Math.random() * 4) + 2;
const totalSpins = baseSpins + spinVariation;
// 优化:根据食物数量调整动画持续时间
const baseDuration = 2000;
const durationVariation = 1000;
const spinDuration = baseDuration + (foods.length * 200) + Math.random() * durationVariation;
const randomRotation = Math.random() * Math.PI * 2;
const targetRotation = rotation + totalSpins * Math.PI * 2 + randomRotation;
// 计算结果索引
const normalizedRotation = targetRotation % (Math.PI * 2);
const sliceAngle = (Math.PI * 2) / foods.length;
resultIndex = Math.floor((Math.PI * 2 - normalizedRotation) / sliceAngle) % foods.length;
// 动画旋转
let startTime = null;
const animateSpin = (timestamp) => {
if (!startTime) startTime = timestamp;
const progress = (timestamp - startTime) / spinDuration;
if (progress < 1) {
// 使用缓动函数使旋转减速
const easeOut = 1 - Math.pow(1 - progress, 3);
rotation = rotation + (targetRotation - rotation) * easeOut * (timestamp - startTime) / 16;
drawWheel();
animationId = requestAnimationFrame(animateSpin);
} else {
rotation = targetRotation;
drawWheel();
isSpinning = false;
animationId = null;
// 显示结果
setTimeout(() => {
resultText.textContent = foods[resultIndex];
resultDisplay.classList.add('result-appear');
resultDisplay.style.opacity = '1';
// 添加到历史记录
addToHistory(foods[resultIndex]);
// 检查连续选择
checkConsecutiveResults(foods[resultIndex]);
}, 500);
}
};
animationId = requestAnimationFrame(animateSpin);
};
// 添加食物
const addFood = () => {
const foodName = newFoodInput.value.trim();
if (foodName && !foods.includes(foodName)) {
foods.push(foodName);
newFoodInput.value = '';
updateFoodList();
drawWheel();
} else if (foods.includes(foodName)) {
newFoodInput.classList.add('border-red-500');
setTimeout(() => {
newFoodInput.classList.remove('border-red-500');
}, 1000);
}
};
// 更新食物列表
const updateFoodList = () => {
foodList.innerHTML = '';
if (foods.length === 0) {
const emptyState = document.createElement('div');
emptyState.className = 'text-center text-gray-500 italic py-4';
emptyState.textContent = '食物列表为空';
foodList.appendChild(emptyState);
return;
}
foods.forEach((food, index) => {
const foodItem = document.createElement('div');
foodItem.className = 'food-item flex items-center justify-between p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-all';
const foodName = document.createElement('span');
foodName.textContent = food;
const deleteBtn = document.createElement('button');
deleteBtn.className = 'delete-btn text-red-400 hover:text-red-600 transition-colors';
deleteBtn.innerHTML = '<i class="fa-solid fa-trash"></i>';
deleteBtn.addEventListener('click', () => {
// 如果删除的是当前结果,重置结果显示
if (index === resultIndex) {
resultDisplay.style.opacity = '0';
}
foods.splice(index, 1);
updateFoodList();
drawWheel();
});
foodItem.appendChild(foodName);
foodItem.appendChild(deleteBtn);
foodList.appendChild(foodItem);
});
};
// 添加到历史记录
const addToHistory = (food) => {
const now = new Date();
const timeString = now.toLocaleTimeString();
history.unshift({
food,
time: timeString
});
// 限制历史记录数量
if (history.length > 10) {
history.pop();
}
updateHistoryList();
};
// 更新历史记录列表
const updateHistoryList = () => {
historyList.innerHTML = '';
if (history.length === 0) {
const emptyState = document.createElement('div');
emptyState.className = 'text-center text-gray-500 italic';
emptyState.textContent = '暂无记录';
historyList.appendChild(emptyState);
return;
}
history.forEach((item) => {
const historyItem = document.createElement('div');
historyItem.className = 'flex items-center justify-between p-3 border-b border-gray-100 last:border-0';
const foodContainer = document.createElement('div');
const foodName = document.createElement('p');
foodName.className = 'font-medium';
foodName.textContent = item.food;
const time = document.createElement('p');
time.className = 'text-sm text-gray-500';
time.textContent = item.time;
foodContainer.appendChild(foodName);
foodContainer.appendChild(time);
historyItem.appendChild(foodContainer);
historyList.appendChild(historyItem);
});
};
// 清空历史记录
const clearHistory = () => {
history = [];
updateHistoryList();
};
// 打开帮助模态框
const openHelpModal = () => {
helpModal.classList.remove('hidden');
helpModal.classList.add('flex');
document.body.style.overflow = 'hidden';
};
// 关闭帮助模态框
const closeHelpModal = () => {
helpModal.classList.add('hidden');
helpModal.classList.remove('flex');
document.body.style.overflow = '';
};
// 检查连续选择
const checkConsecutiveResults = (food) => {
// 添加到连续选择数组
consecutiveResults.push(food);
// 限制数组长度为3
if (consecutiveResults.length > 3) {
consecutiveResults.shift();
}
// 检查是否连续两次相同
if (consecutiveResults.length >= 2 &&
consecutiveResults[consecutiveResults.length - 1] === consecutiveResults[consecutiveResults.length - 2]) {
// 触发爱心特效
createHearts(food);
// 如果连续三次相同,触发彩蛋
if (consecutiveResults.length === 3 &&
consecutiveResults[0] === consecutiveResults[1] &&
consecutiveResults[1] === consecutiveResults[2]) {
triggerEasterEgg(food);
}
}
};
// 创建爱心特效
const createHearts = (food) => {
heartsContainer.innerHTML = '';
heartsContainer.classList.remove('hidden');
// 创建100个爱心
for (let i = 0; i < 100; i++) {
setTimeout(() => {
const heart = document.createElement('div');
heart.className = 'heart-fall absolute text-primary';
heart.style.left = `${Math.random() * 100}vw`;
heart.style.fontSize = `${16 + Math.random() * 24}px`;
heart.style.animationDuration = `${2 + Math.random() * 3}s`;
heart.style.opacity = `${0.5 + Math.random() * 0.5}`;
// 50%概率是爱心50%概率是食物名称
if (Math.random() > 0.5) {
heart.innerHTML = '❤️';
} else {
heart.innerHTML = food;
}
heartsContainer.appendChild(heart);
// 动画结束后移除
setTimeout(() => {
heart.remove();
}, 5000);
}, i * 30);
}
// 3秒后隐藏容器
setTimeout(() => {
heartsContainer.classList.add('hidden');
}, 5000);
};
// 触发彩蛋
const triggerEasterEgg = (food) => {
// 显示提示
alert(`恭喜触发彩蛋!看来你真的很想吃${food},那就别犹豫啦!`);
// 把转盘上所有选项都变成这个食物
const originalFoods = [...foods];
foods = Array(foods.length).fill(food);
// 重新绘制转盘
drawWheel();
// 3秒后恢复原来的食物列表
setTimeout(() => {
foods = originalFoods;
drawWheel();
}, 3000);
};
// 事件监听
spinBtn.addEventListener('click', spinWheel);
addFoodBtn.addEventListener('click', addFood);
newFoodInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') addFood();
});
clearHistoryBtn.addEventListener('click', clearHistory);
helpBtn.addEventListener('click', openHelpModal);
closeHelpBtn.addEventListener('click', closeHelpModal);
gotItBtn.addEventListener('click', closeHelpModal);
// 点击模态框外部关闭
helpModal.addEventListener('click', (e) => {
if (e.target === helpModal) {
closeHelpModal();
}
});
// 初始化
updateFoodList();
updateHistoryList();
drawWheel();
// 修复添加resize事件处理
window.addEventListener('resize', () => {
setCanvasSize();
drawWheel();
});
});
</script>
</body>
</html>