[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>美食大转盘 - 点餐助手</title>
<style>
/* 全局样式变量 */
:root {
--primary-color: #ff6b6b;
--primary-light: #ff8e8e;
--secondary-color: #ffd93d;
--accent-color: #6c5ce7;
--danger-color: #ff3838;
--warning-color: #ff9f1a;
--text-primary: #2d3436;
--text-secondary: #636e72;
--bg-gradient: linear-gradient(135deg, #fdfcfb 0%, #e2d1c3 100%);
}
/* 左上角控制栏 - 悬停显示 */
.top-controls-area {
position: fixed;
top: 0;
left: 0;
width: 40px;
height: 40px;
z-index: 1000;
transition: all 0.3s ease;
border-radius: 0 0 20px 0;
overflow: hidden;
}
.top-controls-area:hover {
width: 220px;
height: 90px;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 107, 107, 0.2);
}
.top-controls {
display: flex;
gap: 0.75rem;
padding: 0.75rem;
opacity: 0;
transform: translateX(-100%);
transition: all 0.3s ease 0.1s;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
.top-controls-area:hover .top-controls {
opacity: 1;
transform: translateX(0);
}
/* 悬停提示区域 */
.hover-trigger {
position: absolute;
top: 0;
left: 0;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.1);
border-radius: 0 0 20px 0;
cursor: pointer;
transition: all 0.3s ease;
}
.hover-trigger:hover {
background: rgba(255, 107, 107, 0.1);
}
.hover-trigger::before {
content: "⋮⋮";
font-size: 16px;
font-weight: bold;
color: var(--primary-color);
transform: rotate(90deg);
}
/* 控制栏按钮样式调整 */
.top-controls .btn {
padding: 0.6rem 1rem;
font-size: 0.9rem;
min-width: 90px;
flex: 1;
max-width: 100px;
}
@media (max-width: 768px) {
.top-controls-area {
width: 35px;
height: 35px;
}
.top-controls-area:hover {
width: 180px;
height: 80px;
}
.hover-trigger::before {
font-size: 14px;
}
.top-controls .btn {
padding: 0.5rem 0.8rem;
font-size: 0.8rem;
min-width: 80px;
max-width: 85px;
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
min-height: 100vh;
background: var(--bg-gradient);
padding: 1rem;
color: var(--text-primary);
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 2rem;
padding-bottom: 1.5rem;
}
.header h1 {
font-size: clamp(2rem, 5vw, 3rem);
font-weight: 800;
color: var(--text-primary);
margin: 0.5rem 0;
}
.header p {
color: var(--text-secondary);
font-size: 1.2rem;
margin-top: 1rem;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.shortcut-hint {
display: inline-block;
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e8e 100%);
color: white;
padding: 0.5rem 1.5rem;
border-radius: 50px;
font-size: 0.9rem;
margin-top: 0.5rem;
}
.main-grid {
display: grid;
grid-template-columns: 350px 1fr;
gap: 2rem;
align-items: start;
}
.main-grid.menu-empty {
grid-template-columns: 1fr;
}
.main-grid.menu-empty .list-panel {
display: none;
}
@media (max-width: 900px) {
.main-grid {
grid-template-columns: 1fr;
}
.main-grid.menu-empty .list-panel {
display: none;
}
.main-grid .list-panel {
order: 2;
}
.main-grid .wheel-panel {
order: 1;
}
}
.panel {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 1.5rem;
border: none;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.2);
position: relative;
overflow: hidden;
}
.panel::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 5px;
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e8e 100%);
}
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
}
.panel-header h2 {
font-size: 1.5rem;
font-weight: 700;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 0.5rem;
}
.count-badge {
font-size: 0.85rem;
font-weight: 700;
color: white;
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e8e 100%);
padding: 0.3rem 0.8rem;
border-radius: 20px;
box-shadow: 0 4px 8px rgba(255, 107, 107, 0.3);
}
.input-group {
margin-bottom: 1.5rem;
}
.input-label {
color: var(--text-primary);
margin-bottom: 0.5rem;
display: block;
font-weight: 600;
}
.input-row {
display: flex;
gap: 0.75rem;
}
input, textarea {
background: white;
border: 2px solid rgba(255, 107, 107, 0.2);
border-radius: 12px;
padding: 0.85rem 1.2rem;
color: var(--text-primary);
outline: none;
transition: all 0.3s;
width: 100%;
font-size: 1rem;
}
input:focus, textarea:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(255, 107, 107, 0.2);
}
.input-row input {
flex: 1;
}
.btn {
padding: 0.85rem 1.5rem;
border: none;
border-radius: 12px;
font-weight: 700;
cursor: pointer;
transition: all 0.3s;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
font-size: 1rem;
position: relative;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.btn-primary {
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e8e 100%);
color: white;
}
.btn-primary:hover:not(:disabled) {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(255, 107, 107, 0.4);
}
.btn-success {
background: linear-gradient(135deg, #ffd93d 0%, #ffed6b 100%);
color: var(--text-primary);
}
.btn-secondary {
background: white;
color: var(--text-primary);
border: 2px solid rgba(255, 107, 107, 0.2);
}
.btn-danger {
background: linear-gradient(135deg, #ff3838 0%, #ff6b6b 100%);
color: white;
}
.btn-danger:hover:not(:disabled) {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(255, 56, 56, 0.4);
}
.btn-warning {
background: linear-gradient(135deg, #ff9f1a 0%, #ffd93d 100%);
color: var(--text-primary);
}
.btn-info {
background: linear-gradient(135deg, #6c5ce7 0%, #a29bfe 100%);
color: white;
}
.btn-info:hover:not(:disabled) {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(108, 92, 231, 0.4);
}
.punishment-list {
max-height: 400px;
overflow-y: auto;
margin-bottom: 1.5rem;
padding-right: 0.5rem;
}
.punishment-item {
background: white;
padding: 1.2rem;
border-radius: 12px;
margin-bottom: 0.75rem;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.3s;
color: var(--text-primary);
border: 2px solid transparent;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.punishment-item:hover {
background: #f9f9f9;
transform: translateX(8px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.2);
}
.punishment-item .remove-btn {
background: transparent;
border: 2px solid rgba(255, 107, 107, 0.2);
color: #b2bec3;
cursor: pointer;
font-size: 1.2rem;
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s;
}
.punishment-item .remove-btn:hover {
background: var(--danger-color);
border-color: var(--danger-color);
color: white;
transform: rotate(90deg);
}
/* 菜品图标样式 - 保持原有样式 */
.food-icon {
font-size: 1.2rem;
margin-right: 0.5rem;
display: inline-block;
}
.food-name {
font-size: 1rem;
font-weight: 500;
}
/* 今日推荐图标样式 - 彩色图标 */
.winner-icon {
font-size: 2.2rem;
margin-right: 0.75rem;
display: inline-block;
filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.2));
animation: bounce 2s infinite;
/* 确保图标有色彩,不使用背景裁剪 */
color: inherit;
background: none;
-webkit-background-clip: initial;
background-clip: initial;
}
@keyframes bounce {
0%, 20%, 53%, 80%, 100% { transform: translateY(0); }
40%, 43% { transform: translateY(-10px); }
70% { transform: translateY(-5px); }
90% { transform: translateY(-2px); }
}
/* 转盘上的菜品图标 - 保持原有样式 */
.wheel-food-icon {
font-size: 1.5rem;
margin-bottom: 5px;
display: block;
}
.wheel-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
}
.wheel-wrapper {
position: relative;
margin-bottom: 2.5rem;
width: 100%;
max-width: 550px;
aspect-ratio: 1/1;
}
.wheel-pointer {
position: absolute;
top: -20px;
left: 50%;
transform: translateX(-50%);
z-index: 20;
width: 50px;
height: 50px;
background: #ffd700;
border: 4px solid white;
border-radius: 50%;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
color: var(--text-primary);
}
.wheel-pointer::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border-left: 12px solid transparent;
border-right: 12px solid transparent;
border-top: 18px solid #ffd700;
}
.wheel-svg {
width: 100%;
height: 100%;
transition-property: transform;
transition-timing-function: cubic-bezier(0.15, 0, 0.20, 1);
filter: drop-shadow(0 10px 20px rgba(0, 0, 0, 0.2));
}
/* 中心固定区域 */
.wheel-center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 120px;
height: 120px;
border-radius: 50%;
background: radial-gradient(circle, #ffd700 0%, #ffa500 100%);
border: 6px solid white;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: bold;
color: white;
z-index: 10;
pointer-events: none;
text-shadow: 0 2px 3px rgba(0, 0, 0, 0.2);
}
.control-buttons {
display: flex;
gap: 1rem;
margin-bottom: 2.5rem;
flex-wrap: wrap;
justify-content: center;
}
.result-display {
background: linear-gradient(135deg, #ff9a9e 0%, #fad0c4 99%, #fad0c4 100%);
color: var(--text-primary);
padding: 2rem 3rem;
border-radius: 20px;
text-align: center;
font-weight: 700;
box-shadow: 0 15px 30px rgba(255, 154, 158, 0.4);
min-width: 320px;
transform: scale(0.9);
opacity: 0;
transition: all 0.5s;
}
.result-display.show {
transform: scale(1);
opacity: 1;
}
.result-display h3 {
font-size: 1.5rem;
margin-bottom: 0.75rem;
color: var(--text-primary);
}
.result-display .punishment-text {
font-size: 2rem;
font-weight: 800;
word-break: break-word;
line-height: 1.4;
/* 移除背景渐变,确保文字颜色正常 */
color: var(--primary-color);
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
.add-menu-prompt {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 3rem;
background: rgba(255, 255, 255, 0.9);
border-radius: 20px;
margin-bottom: 2rem;
text-align: center;
border: 3px dashed var(--accent-color);
}
.add-menu-prompt h3 {
background: linear-gradient(135deg, #ff6b6b 0%, #6c5ce7 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
margin-bottom: 1rem;
font-size: 2rem;
}
.add-menu-prompt p {
color: var(--text-secondary);
margin-bottom: 2rem;
max-width: 500px;
font-size: 1.1rem;
}
.celebration-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
z-index: 2000;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
.celebration-content {
background: white;
border-radius: 30px;
padding: 3rem;
max-width: 35rem;
width: 100%;
text-align: center;
position: relative;
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
background: var(--bg-gradient);
z-index: 100;
}
.celebration-close {
position: absolute;
top: 1.5rem;
right: 1.5rem;
width: 3rem;
height: 3rem;
background: white;
border: 2px solid rgba(255, 107, 107, 0.2);
border-radius: 50%;
color: var(--text-secondary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.8rem;
transition: all 0.3s;
z-index: 101;
}
.celebration-close:hover {
background: var(--primary-color);
color: white;
border-color: var(--primary-color);
}
.celebration-emoji {
font-size: 5rem;
margin-bottom: 1.5rem;
display: block;
}
.celebration-title {
font-size: 2.2rem;
font-weight: 800;
color: var(--text-primary);
margin-bottom: 2rem;
background: linear-gradient(135deg, #ff6b6b 0%, #6c5ce7 100%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.celebration-result-box {
background: white;
border-radius: 20px;
padding: 2.5rem;
margin-bottom: 2.5rem;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
.celebration-result-label {
font-size: 1.2rem;
color: var(--text-secondary);
margin-bottom: 0.75rem;
font-weight: 600;
}
.celebration-result-text {
font-size: 2.2rem;
font-weight: 800;
color: var(--primary-color);
line-height: 1.3;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
.celebration-btn {
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e8e 100%);
color: white;
border: none;
padding: 1.2rem 4rem;
border-radius: 15px;
font-weight: 700;
font-size: 1.2rem;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 8px 20px rgba(255, 107, 107, 0.4);
position: relative;
z-index: 101;
}
.celebration-btn:hover {
transform: translateY(-5px);
box-shadow: 0 12px 25px rgba(255, 107, 107, 0.5);
}
/* 烟花画布 */
.fireworks-canvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 99;
}
.file-import-note {
color: var(--text-secondary);
font-size: 0.9rem;
margin-top: 0.5rem;
padding-left: 0.5rem;
}
.clear-all-section {
display: flex;
justify-content: flex-end;
margin-top: 1.5rem;
padding-top: 1.5rem;
}
.confirm-clear-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
z-index: 2000;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
.confirm-clear-content {
background: white;
border-radius: 25px;
padding: 3rem;
max-width: 30rem;
width: 100%;
text-align: center;
position: relative;
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
background: var(--bg-gradient);
}
.confirm-clear-icon {
font-size: 4rem;
margin-bottom: 1.5rem;
display: block;
color: var(--warning-color);
}
.confirm-clear-title {
font-size: 1.8rem;
font-weight: 800;
color: var(--text-primary);
margin-bottom: 1.2rem;
}
.confirm-clear-message {
font-size: 1.1rem;
color: var(--text-secondary);
margin-bottom: 2.5rem;
line-height: 1.6;
}
.confirm-clear-count {
font-weight: 800;
color: var(--danger-color);
font-size: 1.5rem;
}
.confirm-clear-buttons {
display: flex;
gap: 1.2rem;
justify-content: center;
}
.confirm-clear-btn {
padding: 1rem 2.5rem;
border: none;
border-radius: 12px;
font-weight: 700;
font-size: 1.1rem;
cursor: pointer;
transition: all 0.3s;
min-width: 140px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.confirm-clear-btn.confirm {
background: linear-gradient(135deg, #ffd93d 0%, #ffed6b 100%);
color: var(--text-primary);
}
.confirm-clear-btn.confirm:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(255, 217, 61, 0.4);
}
.confirm-clear-btn.cancel {
background: white;
color: var(--text-primary);
border: 2px solid rgba(255, 107, 107, 0.2);
}
.confirm-clear-btn.cancel:hover {
background: #f8f9fa;
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
}
.hidden {
display: none;
}
@media (max-width: 768px) {
.panel {
padding: 1.5rem;
}
.header h1 {
font-size: clamp(1.8rem, 5vw, 2.5rem);
}
.header p {
font-size: 1.1rem;
}
.control-buttons {
flex-direction: column;
align-items: center;
}
.btn {
width: 100%;
max-width: 300px;
}
.celebration-content {
padding: 2rem;
}
.celebration-title {
font-size: 1.8rem;
}
.celebration-result-text {
font-size: 1.8rem;
}
.confirm-clear-content {
padding: 2rem;
}
.add-menu-prompt {
padding: 2rem;
}
.result-display {
min-width: auto;
width: 100%;
padding: 1.5rem;
}
.result-display .punishment-text {
font-size: 1.8rem;
}
.winner-icon {
font-size: 1.8rem;
margin-right: 0.5rem;
}
.wheel-center {
width: 100px;
height: 100px;
font-size: 20px;
}
}
</style>
</head>
<body>
<!-- 左上角控制栏 - 悬停显示 -->
<div class="top-controls-area">
<div class="hover-trigger"></div>
<div class="top-controls">
<button class="btn btn-primary" id="themeToggleBtn">
<span>切换主题</span>
</button>
<button class="btn btn-info" id="menuToggleBtn">
<span>显示菜单</span>
</button>
</div>
</div>
<div class="container">
<!-- 头部标题 -->
<div class="header">
<h1>美食大转盘</h1>
<p>还在为"吃什么"发愁吗?让转盘帮你决定今天的美食!</p>
<div class="shortcut-hint">💡 提示:按空格键可以快速开始点餐</div>
</div>
<div class="main-grid" id="mainGrid">
<!-- 菜品管理面板 -->
<div class="panel list-panel">
<div class="panel-header">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<h2>我的美食菜单</h2>
<div class="count-badge" id="punishmentCount">0项</div>
</div>
<button class="btn btn-danger" id="clearAllPunishmentsBtn">
一键清空
</button>
</div>
<div class="input-group">
<label class="input-label">添加新菜品:</label>
<div class="input-row">
<input type="text" id="punishmentInput" placeholder="例如:麻辣香锅、披萨、寿司..." autocomplete="off">
<button class="btn btn-primary" id="addPunishmentBtn">
添加
</button>
</div>
</div>
<!-- 文件导入功能 -->
<div class="input-group">
<label class="input-label">导入菜品列表(TXT文件):</label>
<div class="input-row">
<input type="file" id="importFile" accept=".txt" style="flex: 1; padding: 0.5rem;">
<button class="btn btn-secondary" id="importBtn">
导入
</button>
</div>
<div class="file-import-note">
每行一个菜品,支持批量导入
</div>
</div>
<div class="punishment-list" id="punishmentList">
<!-- 菜品项目动态加载 -->
</div>
<div class="clear-all-section">
<button class="btn btn-warning" id="resetToDefaultBtn">
恢复默认菜单
</button>
</div>
</div>
<!-- 转盘控制面板 -->
<div class="panel wheel-panel">
<div class="wheel-container">
<!-- 当菜单为空时的提示 -->
<div id="addMenuPrompt" class="add-menu-prompt hidden">
<h3>菜单空空如也</h3>
<p>请先添加一些菜品到菜单中,然后就可以使用转盘点餐了!</p>
<div style="display: flex; gap: 1rem; flex-wrap: wrap; justify-content: center;">
<button class="btn btn-info" id="showMenuPanelBtn">
显示菜单面板
</button>
<button class="btn btn-primary" id="addFirstItemBtn">
添加第一个菜品
</button>
<button class="btn btn-secondary" id="importQuickBtn">
快速导入
</button>
</div>
</div>
<div class="wheel-wrapper">
<!-- 转盘指针 -->
<div class="wheel-pointer">➤</div>
<!-- SVG转盘 -->
<svg id="wheelSvg" class="wheel-svg" viewBox="-250 -250 500 500">
<defs>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="0" stdDeviation="8" flood-color="rgba(0,0,0,0.2)"/>
</filter>
</defs>
<!-- 转盘扇形区域(动态生成) -->
<g id="wheelSections"></g>
</svg>
<!-- 固定不动的中心区域 -->
<div class="wheel-center">点餐</div>
</div>
<!-- 控制按钮区域 -->
<div class="control-buttons">
<button class="btn btn-primary" id="startBtn" style="min-width: 180px; padding: 1.2rem;">
<span id="startBtnText">开始点餐</span>
</button>
<button class="btn btn-secondary" id="resetBtn" style="padding: 1.2rem;">
重置转盘
</button>
</div>
<!-- 结果展示区域 -->
<div id="resultDisplay" class="result-display">
<h3>今日推荐</h3>
<div class="punishment-text" id="winnerPunishment">
<span class="winner-icon">🍽️</span>
<span>等待选择...</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 庆祝弹窗 -->
<div id="celebrationModal" class="celebration-modal hidden">
<!-- 烟花画布 -->
<canvas id="fireworksCanvas" class="fireworks-canvas"></canvas>
<div class="celebration-content">
<button class="celebration-close" id="closeCelebrationBtn">×</button>
<div class="celebration-inner">
<span class="celebration-emoji">🍽️</span>
<h2 class="celebration-title">今日美食已选定!</h2>
<div class="celebration-result-box">
<div class="celebration-result-label">转盘推荐:</div>
<div class="celebration-result-text" id="celebrationPunishment">
<span class="winner-icon">🍽️</span>
<span>等待选择...</span>
</div>
</div>
<button class="celebration-btn" id="confirmCelebrationBtn">
确定
</button>
</div>
</div>
</div>
<!-- 一键清空确认弹窗 -->
<div id="confirmClearModal" class="confirm-clear-modal hidden">
<div class="confirm-clear-content">
<span class="confirm-clear-icon">⚠️</span>
<h2 class="confirm-clear-title">确认清空菜单</h2>
<div class="confirm-clear-message">
您确定要清空所有 <span class="confirm-clear-count" id="clearCount">0</span> 个菜品吗?<br>
此操作无法撤销,所有自定义和导入的菜品将被永久删除。
</div>
<div class="confirm-clear-buttons">
<button class="confirm-clear-btn cancel" id="cancelClearBtn">
取消
</button>
<button class="confirm-clear-btn confirm" id="confirmClearBtn">
确定清空
</button>
</div>
</div>
</div>
<script>
// 默认菜品列表(带图标)
const defaultPunishmentsWithIcons = [
{name: '披萨', icon: '🍕'},
{name: '汉堡', icon: '🍔'},
{name: '拉面', icon: '🍜'},
{name: '寿司', icon: '🍣'},
{name: '麻辣香锅', icon: '🥘'},
{name: '火锅', icon: '🍲'},
{name: '沙拉', icon: '🥗'},
{name: '咖喱饭', icon: '🍛'},
{name: '便当', icon: '🍱'},
{name: '三明治', icon: '🥪'},
{name: '意大利面', icon: '🍝'},
{name: '墨西哥卷饼', icon: '🌯'},
{name: '饺子', icon: '🥟'},
{name: '炸虾', icon: '🍤'},
{name: '冰淇淋', icon: '🍦'}
];
// 常用菜品图标映射
const foodIcons = {
'披萨': '🍕', '汉堡': '🍔', '拉面': '🍜', '寿司': '🍣',
'麻辣香锅': '🥘', '火锅': '🍲', '沙拉': '🥗', '咖喱饭': '🍛',
'便当': '🍱', '三明治': '🥪', '意大利面': '🍝', '墨西哥卷饼': '🌯',
'饺子': '🥟', '炸虾': '🍤', '冰淇淋': '🍦', '烧烤': '🍢',
'炸鸡': '🍗', '牛排': '🥩', '米饭': '🍚', '面条': '🍜',
'面包': '🍞', '蛋糕': '🍰', '甜甜圈': '🍩', '咖啡': '☕',
'茶': '🍵', '果汁': '🧃', '啤酒': '🍺', '红酒': '🍷',
'鸡尾酒': '🍸', '水果': '🍎', '蔬菜': '🥦', '海鲜': '🦞',
'烤肉': '🥓', '汤': '🍲', '炒饭': '🍛', '煎饺': '🥟',
'寿司卷': '🍣', '天妇罗': '🍤', '章鱼烧': '🐙', '关东煮': '🍢',
'拉面': '🍜', '乌冬面': '🍜', '荞麦面': '🍜', '冷面': '🍜',
'炒面': '🍜', '拌面': '🍜', '刀削面': '🍜', '米粉': '🍜',
'河粉': '🍜', '粉丝': '🍜', '米线': '🍜', '土豆粉': '🍜',
'麻辣烫': '🍲', '串串香': '🍢', '烤鱼': '🐟', '水煮鱼': '🐟',
'酸菜鱼': '🐟', '清蒸鱼': '🐟', '红烧肉': '🥩', '糖醋排骨': '🥩',
'宫保鸡丁': '🍗', '麻婆豆腐': '🥘', '回锅肉': '🥩', '鱼香肉丝': '🥩',
'东坡肉': '🥩', '叫花鸡': '🍗', '北京烤鸭': '🦆', '盐水鸭': '🦆',
'烧鹅': '🦆', '白切鸡': '🍗', '口水鸡': '🍗', '辣子鸡': '🍗',
'大盘鸡': '🍗', '椒盐虾': '🍤', '蒜蓉虾': '🍤', '油焖大虾': '🍤',
'清蒸蟹': '🦀', '香辣蟹': '🦀', '小龙虾': '🦞', '生蚝': '🦪',
'扇贝': '🐚', '海参': '🌊', '鲍鱼': '🐚', '鱼翅': '🦈',
'燕窝': '🐦', '佛跳墙': '🍲', '四喜丸子': '🥩', '狮子头': '🥩',
'梅菜扣肉': '🥩', '粉蒸肉': '🥩', '蚂蚁上树': '🍜', '毛血旺': '🥘',
'夫妻肺片': '🥩', '蒜泥白肉': '🥩', '水煮肉片': '🥩', '酸汤肥牛': '🥩',
'干锅牛蛙': '🐸', '干锅菜花': '🥦', '干锅土豆片': '🥔', '干锅千页豆腐': '🥘',
'烤羊排': '🥩', '烤羊肉串': '🍢', '烤牛肉串': '🍢', '烤鸡翅': '🍗',
'烤鱿鱼': '🦑', '烤生蚝': '🦪', '烤扇贝': '🐚', '烤茄子': '🍆',
'烤玉米': '🌽', '烤红薯': '🍠', '煎饼果子': '🥞', '肉夹馍': '🥙',
'凉皮': '🥣', '热干面': '🍜', '炸酱面': '🍜', '担担面': '🍜',
'烩面': '🍜', '臊子面': '🍜', '油泼面': '🍜', '阳春面': '🍜',
'葱油拌面': '🍜', '重庆小面': '🍜', '宜宾燃面': '🍜', '兰州拉面': '🍜',
'山西刀削面': '🍜', '武汉热干面': '🍜', '北京炸酱面': '🍜', '四川担担面': '🍜',
'陕西臊子面': '🍜', '河南烩面': '🍜', '江苏阳春面': '🍜', '上海葱油拌面': '🍜',
'广东云吞面': '🍜', '福建沙县拌面': '🍜', '台湾牛肉面': '🍜', '香港车仔面': '🍜'
};
// 从默认菜品列表中提取名称
const defaultPunishmentNames = defaultPunishmentsWithIcons.map(item => item.name);
// 从LocalStorage加载数据
let punishments = JSON.parse(localStorage.getItem('wheelPunishments')) || [...defaultPunishmentNames];
// 转盘状态控制变量
let isSpinning = false;
let currentRotation = 0;
let currentPunishment = '';
// 菜单面板状态 - 默认设置为false(隐藏菜单)
let menuVisible = false;
// 主题状态
let isLuxuryTheme = true;
// 转盘扇形颜色列表
const luxuryColors = [
'#ff6b6b', '#ffd93d', '#6c5ce7', '#00b894', '#fd79a8',
'#e17055', '#00cec9', '#a29bfe', '#fab1a0', '#74b9ff',
'#55efc4', '#ffeaa7', '#dfe6e9', '#fd9644', '#9b59b6'
];
const simpleColors = [
'#4a90e2', '#50c878', '#ff8c42', '#9c5bdf', '#ff6b6b',
'#20b2aa', '#ffa726', '#7e8c8d', '#e91e63', '#009688',
'#673ab7', '#ff5722', '#795548', '#607d8b', '#3f51b5'
];
// 当前使用的颜色数组
let currentColors = luxuryColors;
// 烟花效果相关变量
let fireworks = [];
let particles = [];
let animationId = null;
let canvas, ctx;
// 烟花颜色
const fireworkColors = [
'#FF0000', '#FF4500', '#FFD700', '#00FF00', '#1E90FF',
'#8A2BE2', '#FF1493', '#FF6B6B', '#FFD93D', '#6C5CE7'
];
// 获取菜品图标
function getFoodIcon(foodName) {
// 首先检查是否有精确匹配的图标
if (foodIcons[foodName]) {
return foodIcons[foodName];
}
// 如果没有精确匹配,检查是否包含关键字
for (const [key, icon] of Object.entries(foodIcons)) {
if (foodName.includes(key)) {
return icon;
}
}
// 默认图标
return '🍽️';
}
// 获取转盘显示的菜品图标(特殊处理,只显示图标)
function getWheelFoodIcon(foodName) {
const icon = getFoodIcon(foodName);
// 为转盘上的图标创建SVG文本
return icon;
}
// 初始化烟花画布
function initFireworks() {
canvas = document.getElementById('fireworksCanvas');
ctx = canvas.getContext('2d');
// 设置画布尺寸
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
}
// 烟花粒子类
class Particle {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
this.velocity = {
x: (Math.random() - 0.5) * 10,
y: (Math.random() - 0.5) * 10
};
this.alpha = 1;
this.decay = Math.random() * 0.02 + 0.01;
this.size = Math.random() * 3 + 1;
this.gravity = 0.1;
}
update() {
this.velocity.y += this.gravity;
this.x += this.velocity.x;
this.y += this.velocity.y;
this.alpha -= this.decay;
}
draw() {
ctx.save();
ctx.globalAlpha = this.alpha;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
// 烟花类
class Firework {
constructor(x, y) {
this.x = x;
this.y = y;
this.color = fireworkColors[Math.floor(Math.random() * fireworkColors.length)];
this.particles = [];
this.exploded = false;
// 创建爆炸粒子
for (let i = 0; i < 100; i++) {
this.particles.push(new Particle(this.x, this.y, this.color));
}
}
update() {
if (!this.exploded) {
this.exploded = true;
}
for (let i = this.particles.length - 1; i >= 0; i--) {
this.particles[i].update();
if (this.particles[i].alpha <= 0) {
this.particles.splice(i, 1);
}
}
}
draw() {
this.particles.forEach(particle => particle.draw());
}
isFinished() {
return this.particles.length === 0;
}
}
// 创建烟花
function createFirework(x, y) {
fireworks.push(new Firework(x, y));
}
// 随机创建烟花
function createRandomFirework() {
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height * 0.5;
createFirework(x, y);
}
// 在弹窗周围创建一圈烟花
function createCelebrationFireworks() {
const modal = document.getElementById('celebrationModal');
const content = document.querySelector('.celebration-content');
if (!modal || !content) return;
const modalRect = modal.getBoundingClientRect();
const contentRect = content.getBoundingClientRect();
// 在弹窗周围创建烟花
const positions = [
// 左上角
{x: contentRect.left - 50, y: contentRect.top - 50},
// 右上角
{x: contentRect.right + 50, y: contentRect.top - 50},
// 左下角
{x: contentRect.left - 50, y: contentRect.bottom + 50},
// 右下角
{x: contentRect.right + 50, y: contentRect.bottom + 50},
// 上方中间
{x: contentRect.left + contentRect.width / 2, y: contentRect.top - 100},
// 下方中间
{x: contentRect.left + contentRect.width / 2, y: contentRect.bottom + 100},
// 左侧中间
{x: contentRect.left - 100, y: contentRect.top + contentRect.height / 2},
// 右侧中间
{x: contentRect.right + 100, y: contentRect.top + contentRect.height / 2}
];
positions.forEach(pos => {
createFirework(pos.x, pos.y);
});
// 随机创建一些烟花
setTimeout(() => {
for (let i = 0; i < 5; i++) {
setTimeout(() => createRandomFirework(), i * 300);
}
}, 500);
}
// 动画循环
function animateFireworks() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 更新和绘制所有烟花
for (let i = fireworks.length - 1; i >= 0; i--) {
fireworks[i].update();
fireworks[i].draw();
if (fireworks[i].isFinished()) {
fireworks.splice(i, 1);
}
}
// 随机创建新烟花
if (fireworks.length < 10 && Math.random() < 0.05) {
createRandomFirework();
}
animationId = requestAnimationFrame(animateFireworks);
}
// 开始烟花效果
function startFireworks() {
if (animationId) {
cancelAnimationFrame(animationId);
}
fireworks = [];
createCelebrationFireworks();
animateFireworks();
}
// 停止烟花效果
function stopFireworks() {
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
fireworks = [];
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
// 切换主题
function toggleTheme() {
const body = document.body;
const themeToggleBtn = document.getElementById('themeToggleBtn');
isLuxuryTheme = !isLuxuryTheme;
if (isLuxuryTheme) {
body.classList.remove('simple-theme');
body.classList.add('luxury-theme');
currentColors = luxuryColors;
} else {
body.classList.remove('luxury-theme');
body.classList.add('simple-theme');
currentColors = simpleColors;
}
updateWheel();
localStorage.setItem('foodWheelTheme', isLuxuryTheme ? 'luxury' : 'simple');
}
// 加载主题偏好
function loadThemePreference() {
const savedTheme = localStorage.getItem('foodWheelTheme');
if (savedTheme === 'simple') {
toggleTheme();
}
}
// 更新菜品项目计数显示
function updatePunishmentCount() {
const countElement = document.getElementById('punishmentCount');
const count = punishments.length;
countElement.textContent = `${count}项`;
}
// 保存菜品列表到LocalStorage
function savePunishments() {
localStorage.setItem('wheelPunishments', JSON.stringify(punishments));
}
// 更新界面布局状态 - 修改:默认隐藏菜单
function updateLayoutState() {
const mainGrid = document.getElementById('mainGrid');
const addMenuPrompt = document.getElementById('addMenuPrompt');
const menuToggleBtn = document.getElementById('menuToggleBtn');
const wheelPanel = document.querySelector('.wheel-panel');
// 检查菜单是否为空
if (punishments.length === 0) {
mainGrid.classList.add('menu-empty');
wheelPanel.classList.add('full-view');
addMenuPrompt.classList.remove('hidden');
menuToggleBtn.textContent = '显示菜单';
} else {
// 菜品不为空时,根据menuVisible决定是否显示菜单
if (menuVisible) {
mainGrid.classList.remove('menu-empty');
menuToggleBtn.textContent = '隐藏菜单';
} else {
mainGrid.classList.add('menu-empty');
menuToggleBtn.textContent = '显示菜单';
}
wheelPanel.classList.remove('full-view');
addMenuPrompt.classList.add('hidden');
}
updatePunishmentCount();
}
// 切换菜单面板显示/隐藏
function toggleMenuPanel() {
const mainGrid = document.getElementById('mainGrid');
const menuToggleBtn = document.getElementById('menuToggleBtn');
if (punishments.length > 0) {
menuVisible = !menuVisible;
updateLayoutState();
}
}
// 显示一键清空确认弹窗
function showClearConfirmation() {
if (isSpinning) {
alert('转盘旋转时不能清空菜品!');
return;
}
if (punishments.length === 0) {
alert('当前没有菜品可清空!');
return;
}
document.getElementById('clearCount').textContent = punishments.length;
document.getElementById('confirmClearModal').classList.remove('hidden');
}
// 隐藏一键清空确认弹窗
function hideClearConfirmation() {
document.getElementById('confirmClearModal').classList.add('hidden');
}
// 执行一键清空操作
function clearAllPunishments() {
if (isSpinning) {
alert('转盘旋转时不能清空菜品!');
return;
}
const previousCount = punishments.length;
punishments = [];
updatePunishmentList();
updateLayoutState();
updateWheel();
savePunishments();
currentRotation = 0;
currentPunishment = '';
const wheelSvg = document.getElementById('wheelSvg');
wheelSvg.style.transitionDuration = '0ms';
wheelSvg.style.transform = 'rotate(0deg)';
const resultDisplay = document.getElementById('resultDisplay');
resultDisplay.classList.remove('show');
closeCelebration();
alert(`已清空 ${previousCount} 个菜品!`);
hideClearConfirmation();
}
// 恢复默认菜单
function resetToDefaultMenu() {
if (isSpinning) {
alert('转盘旋转时不能恢复默认菜单!');
return;
}
if (!confirm('确定要恢复默认菜单吗?当前所有菜品将被替换为默认菜品。')) {
return;
}
punishments = [...defaultPunishmentNames];
updatePunishmentList();
updateLayoutState();
updateWheel();
savePunishments();
alert('已恢复默认菜单!');
}
// 导入TXT文件并解析菜品列表
function importPunishmentsFromTxt(file) {
const reader = new FileReader();
reader.onload = function(e) {
try {
const content = e.target.result;
const newPunishments = content.split(/\r?\n/)
.map(item => item.trim())
.filter(item => item.length > 0);
if (newPunishments.length === 0) {
alert('文件为空或格式不正确!');
return;
}
const uniqueNewPunishments = newPunishments.filter(
item => !punishments.includes(item)
);
if (uniqueNewPunishments.length === 0) {
alert('所有菜品已存在!');
return;
}
punishments.push(...uniqueNewPunishments);
updatePunishmentList();
updateLayoutState();
updateWheel();
savePunishments();
alert(`成功导入 ${uniqueNewPunishments.length} 个新菜品!${newPunishments.length - uniqueNewPunishments.length > 0 ? `有 ${newPunishments.length - uniqueNewPunishments.length} 个重复菜品已跳过。` : ''}`);
} catch (error) {
console.error('导入文件时出错:', error);
alert('文件解析失败,请检查文件格式!');
}
};
reader.onerror = function() {
alert('读取文件失败!');
};
reader.readAsText(file);
}
// 快速添加第一个菜品
function addFirstItem() {
const item = prompt('请输入第一个菜品名称:', '披萨');
if (item && item.trim()) {
addPunishmentItem(item.trim());
}
}
// 快速导入示例菜品
function quickImportExample() {
if (confirm('要导入示例菜品列表吗?')) {
punishments = [...defaultPunishmentNames];
updatePunishmentList();
updateLayoutState();
updateWheel();
savePunishments();
alert('已导入示例菜品列表!');
}
}
// 显示菜单面板
function showMenuPanel() {
const mainGrid = document.getElementById('mainGrid');
menuVisible = true;
updateLayoutState();
document.getElementById('menuToggleBtn').textContent = '隐藏菜单';
document.getElementById('punishmentInput').focus();
}
// DOM加载完成事件
document.addEventListener('DOMContentLoaded', function() {
loadThemePreference();
updatePunishmentList();
updateLayoutState(); // 默认会隐藏菜单
updateWheel();
initFireworks();
// 输入框回车事件
const input = document.getElementById('punishmentInput');
input.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
addPunishment();
}
});
// 绑定按钮事件
document.getElementById('themeToggleBtn').addEventListener('click', toggleTheme);
document.getElementById('menuToggleBtn').addEventListener('click', toggleMenuPanel);
document.getElementById('addPunishmentBtn').addEventListener('click', addPunishment);
document.getElementById('importBtn').addEventListener('click', function() {
const fileInput = document.getElementById('importFile');
if (fileInput.files.length > 0) {
importPunishmentsFromTxt(fileInput.files[0]);
fileInput.value = '';
} else {
alert('请先选择文件!');
}
});
document.getElementById('startBtn').addEventListener('click', startGame);
document.getElementById('resetBtn').addEventListener('click', resetWheel);
document.getElementById('resetToDefaultBtn').addEventListener('click', resetToDefaultMenu);
document.getElementById('closeCelebrationBtn').addEventListener('click', closeCelebration);
document.getElementById('confirmCelebrationBtn').addEventListener('click', closeCelebration);
// 新增按钮事件
document.getElementById('showMenuPanelBtn').addEventListener('click', showMenuPanel);
document.getElementById('addFirstItemBtn').addEventListener('click', addFirstItem);
document.getElementById('importQuickBtn').addEventListener('click', quickImportExample);
// 一键清空相关事件
document.getElementById('clearAllPunishmentsBtn').addEventListener('click', showClearConfirmation);
document.getElementById('cancelClearBtn').addEventListener('click', hideClearConfirmation);
document.getElementById('confirmClearBtn').addEventListener('click', clearAllPunishments);
// 点击确认弹窗外部关闭弹窗
document.getElementById('confirmClearModal').addEventListener('click', function(e) {
if (e.target === this) {
hideClearConfirmation();
}
});
// 处理动态生成的删除按钮
document.getElementById('punishmentList').addEventListener('click', function(e) {
if (e.target.classList.contains('remove-btn')) {
const item = e.target.closest('.punishment-item');
const index = Array.from(this.children).indexOf(item);
if (index !== -1) {
removePunishment(index);
}
}
});
// 键盘事件监听 - 空格键开始点餐
document.addEventListener('keydown', function(e) {
if (e.code === 'Space' || e.key === ' ') {
e.preventDefault();
const activeElement = document.activeElement;
const isInputFocused = activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA';
const isModalOpen = !document.getElementById('celebrationModal').classList.contains('hidden') ||
!document.getElementById('confirmClearModal').classList.contains('hidden');
if (!isInputFocused && !isModalOpen && !isSpinning) {
startGame();
}
}
});
});
// 添加新菜品项目
function addPunishment() {
const input = document.getElementById('punishmentInput');
const name = input.value.trim();
if (!name) {
alert('请输入菜品名称!');
return;
}
if (name.length > 30) {
alert('菜品名称过长,请控制在30字以内!');
return;
}
addPunishmentItem(name);
}
// 添加菜品项目
function addPunishmentItem(name) {
if (!punishments.includes(name)) {
punishments.push(name);
document.getElementById('punishmentInput').value = '';
document.getElementById('punishmentInput').focus();
updatePunishmentList();
updateLayoutState();
updateWheel();
savePunishments();
} else {
alert('该菜品已存在!');
}
}
// 删除菜品项目
function removePunishment(index) {
if (isSpinning) {
alert('转盘旋转时不能删除菜品!');
return;
}
if (confirm('确定要删除这个菜品吗?')) {
punishments.splice(index, 1);
updatePunishmentList();
updateLayoutState();
updateWheel();
savePunishments();
}
}
// 更新菜品列表UI
function updatePunishmentList() {
const list = document.getElementById('punishmentList');
if (punishments.length === 0) {
list.innerHTML = '<div class="empty-state">菜品菜单是空的,请先添加菜品!</div>';
return;
}
list.innerHTML = punishments.map((punishment, index) => {
const icon = getFoodIcon(punishment);
const isActive = currentPunishment === punishment;
return `
<div class="punishment-item ${isActive ? 'active' : ''}">
<div style="display: flex; align-items: center;">
<span class="food-icon">${icon}</span>
<span class="food-name">${punishment}</span>
</div>
<button class="remove-btn" title="删除此项">×</button>
</div>
`;
}).join('');
}
// 更新转盘SVG图形
function updateWheel() {
const sectionsGroup = document.getElementById('wheelSections');
sectionsGroup.innerHTML = '';
// 空转盘状态
if (punishments.length === 0) {
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', 'M 0 0 L 240 0 A 240 240 0 1 1 -240 0 Z');
path.setAttribute('fill', '#f5f5f5');
path.setAttribute('stroke', '#e0e0e0');
path.setAttribute('stroke-width', '3');
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
text.setAttribute('x', '0');
text.setAttribute('y', '0');
text.setAttribute('fill', '#999');
text.setAttribute('font-size', '28');
text.setAttribute('font-weight', '600');
text.setAttribute('text-anchor', 'middle');
text.setAttribute('dominant-baseline', 'middle');
text.textContent = '请添加菜品';
sectionsGroup.appendChild(path);
sectionsGroup.appendChild(text);
return;
}
// 计算每个扇形的角度
const sectionAngle = 360 / punishments.length;
const radius = 240;
// 生成每个扇形
punishments.forEach((punishment, index) => {
const startAngle = index * sectionAngle;
const endAngle = (index + 1) * sectionAngle;
const startAngleRad = (startAngle * Math.PI) / 180;
const endAngleRad = (endAngle * Math.PI) / 180;
const largeArcFlag = sectionAngle > 180 ? 1 : 0;
const x1 = Math.cos(startAngleRad) * radius;
const y1 = Math.sin(startAngleRad) * radius;
const x2 = Math.cos(endAngleRad) * radius;
const y2 = Math.sin(endAngleRad) * radius;
const pathData = `M 0 0 L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${x2} ${y2} Z`;
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('d', pathData);
path.setAttribute('fill', currentColors[index % currentColors.length]);
path.setAttribute('stroke', '#fff');
path.setAttribute('stroke-width', '3');
path.setAttribute('filter', 'url(#shadow)');
// 计算文本位置
const midAngle = (startAngle + endAngle) / 2;
const textRadius = radius * 0.65;
const textX = Math.cos((midAngle * Math.PI) / 180) * textRadius;
const textY = Math.sin((midAngle * Math.PI) / 180) * textRadius;
let fontSize = 22;
if (punishments.length > 15) fontSize = 16;
else if (punishments.length > 10) fontSize = 18;
else if (punishments.length > 8) fontSize = 20;
// 创建包含图标和文字的文本元素 - 放在同一行
const textElement = document.createElementNS('http://www.w3.org/2000/svg', 'text');
textElement.setAttribute('x', textX);
textElement.setAttribute('y', textY);
textElement.setAttribute('fill', '#fff');
textElement.setAttribute('font-size', fontSize);
textElement.setAttribute('font-weight', '700');
textElement.setAttribute('text-anchor', 'middle');
textElement.setAttribute('dominant-baseline', 'middle');
textElement.setAttribute('transform', `rotate(${midAngle}, ${textX}, ${textY})`);
textElement.setAttribute('filter', 'url(#shadow)');
// 创建图标tspan(图标字体大一些)
const iconSpan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
iconSpan.setAttribute('font-size', fontSize + 8);
iconSpan.textContent = getWheelFoodIcon(punishment);
// 创建文字tspan
const nameSpan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan');
nameSpan.setAttribute('dx', '0.2em'); // 图标和文字之间的间距
const maxChars = 6;
let displayText = punishment;
if (punishment.length > maxChars) {
displayText = punishment.substring(0, maxChars) + '..';
}
nameSpan.textContent = displayText;
// 将图标和文字添加到文本元素中
textElement.appendChild(iconSpan);
textElement.appendChild(nameSpan);
sectionsGroup.appendChild(path);
sectionsGroup.appendChild(textElement);
});
}
// 开始转盘游戏
function startGame() {
if (punishments.length === 0) {
alert('请先添加菜品!');
return;
}
if (isSpinning) return;
isSpinning = true;
currentPunishment = '';
const startBtn = document.getElementById('startBtn');
const startBtnText = document.getElementById('startBtnText');
startBtn.disabled = true;
startBtnText.textContent = '点餐中...';
const resultDisplay = document.getElementById('resultDisplay');
resultDisplay.classList.remove('show');
const randomDuration = 4000 + Math.random() * 4000;
const randomDegreeOffset = Math.floor(Math.random() * 360);
const randomSpins = 5 + Math.floor(Math.random() * 5);
const finalRotation = currentRotation + (randomSpins * 360) + randomDegreeOffset;
currentRotation = finalRotation;
const wheelSvg = document.getElementById('wheelSvg');
wheelSvg.style.transitionDuration = `${randomDuration}ms`;
wheelSvg.style.transform = `rotate(${currentRotation}deg)`;
setTimeout(() => {
isSpinning = false;
const POINTER_ANGLE = 270;
const actualRotation = currentRotation % 360;
let winningAngle = (POINTER_ANGLE - actualRotation);
winningAngle = winningAngle % 360;
if (winningAngle < 0) {
winningAngle += 360;
}
const sectionAngle = 360 / punishments.length;
const winningIndex = Math.floor(winningAngle / sectionAngle);
currentPunishment = punishments[winningIndex] || punishments[0];
startBtn.disabled = false;
startBtnText.textContent = '再次点餐';
// 更新结果显示 - 确保图标是彩色表情符号
const winnerIcon = getFoodIcon(currentPunishment);
document.getElementById('winnerPunishment').innerHTML = `
<span class="winner-icon">${winnerIcon}</span>
<span>${currentPunishment}</span>
`;
resultDisplay.classList.add('show');
// 更新庆祝弹窗结果
document.getElementById('celebrationPunishment').innerHTML = `
<span class="winner-icon">${winnerIcon}</span>
<span>${currentPunishment}</span>
`;
// 更新列表高亮
updatePunishmentList();
// 显示庆祝弹窗
showCelebration();
}, randomDuration);
}
// 重置转盘
function resetWheel() {
if (isSpinning) {
alert('转盘旋转时不能重置!');
return;
}
if (!confirm('确定要重置转盘吗?')) return;
currentRotation = 0;
currentPunishment = '';
const wheelSvg = document.getElementById('wheelSvg');
wheelSvg.style.transitionDuration = '0ms';
wheelSvg.style.transform = 'rotate(0deg)';
const resultDisplay = document.getElementById('resultDisplay');
resultDisplay.classList.remove('show');
// 重置结果显示
document.getElementById('winnerPunishment').innerHTML = `
<span class="winner-icon">🍽️</span>
<span>等待选择...</span>
`;
// 重置庆祝弹窗结果
document.getElementById('celebrationPunishment').innerHTML = `
<span class="winner-icon">🍽️</span>
<span>等待选择...</span>
`;
updatePunishmentList();
closeCelebration();
}
// 显示庆祝弹窗
function showCelebration() {
const modal = document.getElementById('celebrationModal');
modal.classList.remove('hidden');
// 启动烟花效果
startFireworks();
}
// 关闭庆祝弹窗
function closeCelebration() {
document.getElementById('celebrationModal').classList.add('hidden');
// 停止烟花效果
stopFireworks();
}
</script>
</body></html>