家常菜抽奖转盘.html

prompt:

任务:制作单文件离线家常菜抽奖转盘HTML工具,纯前端,无后端,PC/手机自适应。

【硬性约束】

  1. 所有代码写在同一个HTML文件,保存后双击浏览器直接打开,不拆分任何文件
  2. 使用Tailwind CSS CDN + Font Awesome图标库,不引入额外第三方JS库
  3. UI风格:简约清爽,转盘动画顺滑,移动端优先适配触摸操作
  4. 代码保留关键注释,不压缩代码,方便后续自行新增菜品、修改分类

【功能需求】

  1. 转盘设计:转盘上只绘制【菜品分类】,不要罗列大量菜名,避免界面拥挤难看。
    预设分类:素菜、荤菜、下饭菜、汤品、面食。
  2. 抽奖逻辑:点击按钮启动转盘动画,转盘停止选中分类;再从该分类菜品池随机抽取一道家常菜。
  3. 结果展示:弹窗/卡片展示抽中的菜名 + 简短易懂的家常做法。
  4. 内置菜品库:每个分类预置若干家常菜,附带简要做法,方便后续自行扩充。
  5. 交互细节:转动过程有过渡动画,禁止重复点击触发多次转盘。

【禁止事项】

不要登录、不要云服务、不要广告、不要多余特效。

输出要求:只返回完整HTML源码,附带简短使用说明。

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<meta name="theme-color" content="#f97316" />
<title>家常菜抽奖转盘 · 今天吃什么</title>

<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Font Awesome 图标 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" />

<script>
  tailwind.config = {
    theme: {
      extend: {
        colors: { brand: { 50: '#fff7ed', 100: '#ffedd5', 500: '#f97316', 600: '#ea580c', 700: '#c2410c' } },
        boxShadow: { card: '0 1px 2px rgba(15,23,42,.04), 0 10px 28px -14px rgba(15,23,42,.18)' }
      }
    }
  }
</script>

<style>
  html, body { -webkit-tap-highlight-color: transparent; }
  body { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif; }

  /* 转盘尺寸:手机上按视口宽度自适应,PC 上最大 360px,保证不拥挤 */
  .wheel { width: min(86vw, 360px); aspect-ratio: 1 / 1; }
  /* 滚动条:转盘旋转由 JS 设置 transform,过渡曲线让停止更自然 */
  .wheel-spin { will-change: transform; }
  /* 指针轻微呼吸感(静态,不做花哨特效) */
  .safe-bottom { padding-bottom: calc(1.25rem + env(safe-area-inset-bottom)); }

  @keyframes pop { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
  .animate-pop { animation: pop .28s ease-out both; }
  @keyframes pulseRing { 0%,100% { transform: scale(1); opacity: .55; } 50% { transform: scale(1.04); opacity: .9; } }
  .spin-ready { animation: pulseRing 1.8s ease-in-out infinite; }
</style>
</head>

<body class="min-h-screen bg-gradient-to-b from-brand-50 via-white to-slate-50 text-slate-800 antialiased">
<div class="max-w-md mx-auto px-4 pt-5 safe-bottom">

  <!-- ===================== 标题 ===================== -->
  <header class="text-center mb-4">
    <div class="inline-flex items-center justify-center w-12 h-12 rounded-2xl bg-brand-500 text-white text-xl shadow-card mb-2.5">
      <i class="fa-solid fa-utensils"></i>
    </div>
    <h1 class="text-lg font-semibold tracking-tight">今天吃什么?</h1>
    <p class="text-xs text-slate-500 mt-1">转盘抽分类 · 再从分类里随机挑一道家常菜</p>
  </header>

  <!-- ===================== 转盘 ===================== -->
  <section class="flex flex-col items-center">
    <div class="relative">
      <!-- 外圈装饰:白环 + 阴影,让转盘更立体 -->
      <div class="wheel relative rounded-full p-2.5 bg-white shadow-card">
        <!-- 指针:固定在正上方(12 点方向),不随转盘旋转 -->
        <div class="absolute left-1/2 -translate-x-1/2 -top-1 z-20">
          <div class="w-0 h-0 border-l-[9px] border-r-[9px] border-t-[18px] border-l-transparent border-r-transparent border-t-brand-600 drop-shadow"></div>
        </div>

        <!-- 转盘本体:背景由 JS 生成 conic-gradient(只画分类,不画菜名) -->
        <div id="wheel" class="wheel-spin absolute inset-2.5 rounded-full overflow-hidden ring-1 ring-black/5"
             style="transform: rotate(0deg);">
          <!-- 分类文字标签由 JS 注入,按扇区中心角度定位,保持水平方便阅读 -->
        </div>

        <!-- 中心按钮:点击即开始抽奖(与主按钮同一入口,受防重复点击保护) -->
        <button id="btnCenter" aria-label="开始抽奖"
          class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10 w-[23%] aspect-square rounded-full bg-white shadow-lg grid place-items-center text-brand-600 active:scale-95 transition">
          <i id="centerIcon" class="fa-solid fa-play text-lg"></i>
        </button>
      </div>
    </div>

    <!-- 主按钮 -->
    <button id="btnSpin" class="mt-6 w-full max-w-[280px] inline-flex items-center justify-center gap-2 bg-brand-500 hover:bg-brand-600 active:scale-[.99] disabled:opacity-60 disabled:active:scale-100 text-white font-medium rounded-2xl px-6 py-3.5 shadow-card transition">
      <i id="spinIcon" class="fa-solid fa-rotate"></i><span id="spinText">开始抽奖</span>
    </button>

    <!-- 分类概览:显示每个分类的菜品数量,点击可查看包含哪些菜(纯展示,不参与抽奖) -->
    <div id="catChips" class="mt-4 flex flex-wrap justify-center gap-1.5"></div>
  </section>

  <!-- ===================== 结果卡片 ===================== -->
  <section id="resultCard" class="hidden mt-5 bg-white rounded-3xl shadow-card p-4 sm:p-5 border border-slate-100">
    <div class="flex items-start gap-3">
      <div id="resIcon" class="w-11 h-11 shrink-0 rounded-2xl bg-brand-50 text-brand-600 grid place-items-center text-lg">
        <i class="fa-solid fa-bowl-food"></i>
      </div>
      <div class="min-w-0 flex-1">
        <div class="flex items-center gap-2 flex-wrap">
          <span id="resDish" class="text-base font-semibold">---</span>
          <span id="resCat" class="text-[11px] px-2 py-0.5 rounded-md bg-slate-100 text-slate-500">---</span>
        </div>
        <p class="text-[11px] text-slate-400 mt-1 flex items-center gap-1">
          <i class="fa-solid fa-lightbulb"></i>家常做法
        </p>
        <p id="resHow" class="text-sm text-slate-600 mt-1.5 leading-relaxed">---</p>
      </div>
    </div>
    <div class="mt-4 grid grid-cols-2 gap-2.5">
      <button id="btnReroll" class="inline-flex items-center justify-center gap-1.5 rounded-xl border border-slate-200 px-3 py-2.5 text-sm text-slate-600 active:bg-slate-50 transition">
        <i class="fa-solid fa-shuffle text-xs"></i>换一道
      </button>
      <button id="btnAgain" class="inline-flex items-center justify-center gap-1.5 rounded-xl bg-slate-900 text-white px-3 py-2.5 text-sm active:scale-[.99] transition">
        <i class="fa-solid fa-rotate-right text-xs"></i>再转一次
      </button>
    </div>
  </section>

  <!-- ===================== 分类菜品速查(折叠) ===================== -->
  <section class="mt-5">
    <button id="btnToggleLib" class="w-full flex items-center justify-between bg-white rounded-2xl shadow-card px-4 py-3 text-sm text-slate-600 active:bg-slate-50 transition">
      <span class="flex items-center gap-2"><i class="fa-solid fa-book-open text-brand-500"></i>菜品库速查</span>
      <i id="libArrow" class="fa-solid fa-chevron-down text-xs text-slate-400 transition-transform"></i>
    </button>
    <div id="libBox" class="hidden mt-2.5 space-y-2.5"></div>
  </section>

  <footer class="text-center text-[11px] text-slate-400 mt-6">
    <i class="fa-solid fa-circle-info mr-1"></i>纯前端离线运行,菜品数据写在文件顶部,可直接自行增删
  </footer>
</div>

<script>
/* =========================================================================
 *  家常菜抽奖转盘 ------ 单文件 / 纯前端 / 无后端
 *  结构:① 菜品库(可自行增删)② 分类与配色 ③ 转盘绘制 ④ 抽奖逻辑 ⑤ 交互绑定
 * ========================================================================= */

/* ------------------------------------------------------------------
 * ① 菜品库:CATEGORIES 里定分类,DISHES 里放菜名 + 家常做法
 *    ➜ 想加菜:往对应分类的数组里加一条 { name: '菜名', how: '一句话做法' } 即可,
 *      转盘与统计会自动适配(数量标签、概率都会跟着变)。
 * ------------------------------------------------------------------ */
const DISHES = {
  '素菜': [
    { name: '清炒时蔬',     how: '青菜洗净切段,热油爆香蒜末,大火快炒 1 分钟,加盐出锅。' },
    { name: '蒜蓉西兰花',   how: '西兰花掰小朵焯水 1 分钟;蒜末爆香,下西兰花翻炒,盐调味。' },
    { name: '手撕包菜',     how: '包菜手撕成块,干辣椒蒜片爆锅,大火爆炒,沿锅边淋一点香醋。' },
    { name: '干煸豆角',     how: '豆角切段小火慢煸至表皮起皱,加蒜末、生抽、少许肉末炒匀。' },
    { name: '醋溜白菜',     how: '白菜帮斜切,热油爆香花椒,下白菜大火炒,出锅前烹香醋和少许糖。' },
    { name: '凉拌黄瓜',     how: '黄瓜拍裂切段,加蒜末、生抽、香醋、香油拌匀,冷藏 10 分钟更爽口。' },
    { name: '蒜蓉菠菜',     how: '菠菜焯水去涩过凉,挤干水分,蒜末热油一泼,加盐和生抽拌匀。' },
    { name: '家常豆腐',     how: '北豆腐切块煎至两面金黄,加生抽、蚝油、少量水焖 2 分钟收汁。' },
    { name: '香菇青菜',     how: '香菇切片先炒出香,下青菜同炒,加蚝油和少许水淀粉勾薄芡。' },
    { name: '番茄炒蛋',     how: '鸡蛋炒至半凝盛出,番茄炒出汁回锅同炒,加少许糖提鲜、盐调味。' }
  ],
  '荤菜': [
    { name: '红烧肉',       how: '五花肉焯水切块,炒糖色上色,加生抽老抽、葱姜八角,小火炖 40 分钟收汁。' },
    { name: '糖醋排骨',     how: '排骨焯水后煎香,按 1 料酒 2 酱油 3 醋 4 糖调汁,焖 20 分钟大火收汁。' },
    { name: '可乐鸡翅',     how: '鸡翅两面煎金黄,倒可乐没过鸡翅,加姜片和生抽,中火焖 15 分钟收浓。' },
    { name: '宫保鸡丁',     how: '鸡丁上浆滑炒,花生米炸香,用葱段、干辣椒、糖醋酱油调汁快炒裹匀。' },
    { name: '土豆炖牛肉',   how: '牛肉焯水后与土豆块同炖 40 分钟,加生抽、八角,炖到牛肉软烂、土豆起沙。' },
    { name: '清蒸鲈鱼',     how: '鲈鱼改刀铺姜丝,水开大火蒸 8 分钟,倒掉腥水,淋蒸鱼豉油再泼热油。' },
    { name: '回锅肉',       how: '五花肉煮至断生切片,煸出油至卷曲,加豆瓣酱、青蒜苗、甜面酱炒香。' },
    { name: '葱爆羊肉',     how: '羊肉片用蛋清淀粉抓匀,大火快炒变色盛出,爆香大葱回锅,淋料酒生抽。' },
    { name: '啤酒鸭',       how: '鸭块焯水煸香,加一罐啤酒和生抽、姜蒜,焖 30 分钟至肉烂收汁。' },
    { name: '卤味拼盘',     how: '鸡蛋、豆干、鸡翅加卤料包和生抽冰糖,小火卤 30 分钟,浸泡过夜更入味。' }
  ],
  '下饭菜': [
    { name: '麻婆豆腐',     how: '豆腐焯水去豆味,肉末炒香加豆瓣酱,下豆腐轻推,撒花椒粉和蒜苗。' },
    { name: '鱼香肉丝',     how: '肉丝上浆滑炒,配木耳胡萝卜丝,用糖醋酱油淀粉调成鱼香汁翻炒裹匀。' },
    { name: '酸辣土豆丝',   how: '土豆切丝泡水去淀粉,热油爆干辣椒花椒,大火快炒,出锅淋香醋。' },
    { name: '辣椒炒肉',     how: '五花肉片煸出油,下青椒大火炒至虎皮,加豆豉和生抽,锅气足最下饭。' },
    { name: '梅干菜扣肉',   how: '五花肉煮后煎皮上色切片,铺梅干菜蒸 40 分钟,倒扣脱模淋原汁。' },
    { name: '剁椒鱼头',     how: '鱼头铺剁椒和姜蒜,大火蒸 10 分钟,撒葱花泼热油,鲜辣开胃。' },
    { name: '水煮肉片',     how: '肉片上浆,豆芽垫底,麻辣汤底烫熟肉片连汤倒入,最后泼一勺热辣椒油。' },
    { name: '干锅花菜',     how: '花菜掰小朵煎至微焦,加五花肉片、蒜片、豆瓣酱和孜然炒香。' },
    { name: '酱爆茄子',     how: '茄条裹薄淀粉煎软,加黄豆酱、蒜末、少许糖炒匀,撒葱花出锅。' },
    { name: '酸豆角炒肉末', how: '酸豆角切碎挤水,与肉末、干辣椒同炒,咸香微辣,拌饭一绝。' }
  ],
  '汤品': [
    { name: '番茄蛋花汤',   how: '番茄炒出红汁加水煮开,淋入蛋液画圈,加盐、香油,撒葱花。' },
    { name: '紫菜蛋花汤',   how: '水开下紫菜,关火后淋蛋液静置 5 秒再搅,加虾皮、香油和盐。' },
    { name: '冬瓜排骨汤',   how: '排骨焯水后炖 30 分钟,下冬瓜块再煮 10 分钟,只加盐和姜片,清甜解腻。' },
    { name: '玉米胡萝卜汤', how: '排骨焯水,与甜玉米、胡萝卜同炖 40 分钟,出锅前加盐,无需多余调料。' },
    { name: '菌菇豆腐汤',   how: '香菇、金针菇、白玉菇加水煮开,下嫩豆腐块煮 5 分钟,加盐和胡椒粉。' },
    { name: '酸辣汤',       how: '木耳、笋丝、豆腐丝煮开,加生抽香醋和大量白胡椒,最后水淀粉勾芡。' },
    { name: '丝瓜蛋汤',     how: '丝瓜滚刀块炒软,加水煮开淋蛋液,加盐和枸杞,清甜爽口。' },
    { name: '萝卜牛腩汤',   how: '牛腩焯水与白萝卜同炖 1 小时,加姜片和八角,汤浓萝卜甜。' },
    { name: '海带豆腐汤',   how: '海带结先煮 10 分钟,下豆腐煮 5 分钟,加盐、香油和蒜末。' },
    { name: '青菜丸子汤',   how: '肉馅加蛋清淀粉搅上劲,挤丸子下锅煮浮起,下青菜,加盐和白胡椒。' }
  ],
  '面食': [
    { name: '阳春面',       how: '碗底放生抽、猪油、葱花,冲入高汤,面条煮熟捞入,简单却最香。' },
    { name: '番茄鸡蛋面',   how: '番茄炒出汁加水煮成汤底,下面条煮熟,卧一个荷包蛋,酸香暖胃。' },
    { name: '炸酱面',       how: '肉末加黄豆酱、甜面酱小火炸 10 分钟,拌上手擀面,配黄瓜丝。' },
    { name: '葱油拌面',     how: '小火把葱段熬到金黄,留葱油,加生抽老抽糖调汁,拌入煮好的面条。' },
    { name: '小馄饨',       how: '肉馅加姜末、蛋清搅上劲,包成小馄饨,清汤煮熟撒紫菜虾皮。' },
    { name: '韭菜鸡蛋饺子', how: '韭菜切碎拌炒散的鸡蛋,加香油和盐,包好后水开点三次凉水煮熟。' },
    { name: '红烧牛肉面',   how: '牛腩炖软作浇头,面条煮熟捞碗,浇牛肉汤,加香菜和辣油。' },
    { name: '家常炒面',     how: '面条煮八分熟过凉,加肉丝、青菜、豆芽大火快炒,生抽老抽调味。' },
    { name: '麻酱凉面',     how: '面条煮熟过冰水,用芝麻酱、生抽、醋、蒜水调汁拌匀,加黄瓜丝。' },
    { name: '葱油饼',       how: '面团抹葱油撒葱花卷起压扁,小火两面烙至金黄,分层酥脆。' }
  ]
};

/* ② 分类配置:顺序即转盘扇区顺序(从 12 点方向顺时针)
 *    bg 为扇区配色,text 为文字色,icon 为 Font Awesome 图标 */
const CATEGORIES = [
  { name: '素菜',   icon: 'fa-leaf',            bg: '#bbf7d0', text: '#166534' },
  { name: '荤菜',   icon: 'fa-drumstick-bite',  bg: '#fed7aa', text: '#9a3412' },
  { name: '下饭菜', icon: 'fa-pepper-hot',      bg: '#fecaca', text: '#991b1b' },
  { name: '汤品',   icon: 'fa-bowl-food',       bg: '#bfdbfe', text: '#1e40af' },
  { name: '面食',   icon: 'fa-bread-slice',     bg: '#fde68a', text: '#92400e' }
];

const SEG = 360 / CATEGORIES.length;   // 每个扇区角度:5 个分类 → 72°
const SPIN_MS = 4200;                  // 转动时长(毫秒)
const SPIN_TURNS = 5;                  // 额外整圈数,保证转动过程有充分动画

/* ---------- DOM 引用 ---------- */
const $ = id => document.getElementById(id);
const wheel = $('wheel');

let rotation = 0;        // 转盘当前累计旋转角度(deg,只增不减,保证动画连贯)
let spinning = false;    // 防重复点击的唯一开关
let lastPick = null;     // 上一次抽中的结果 { category, dish }

/* ------------------------------------------------------------------
 * ③ 绘制转盘:背景用 conic-gradient 画扇区,文字单独定位(保持水平)
 *    from -SEG/2 让第 0 个扇区的中心正对 12 点方向的指针
 * ------------------------------------------------------------------ */
function buildWheel() {
  const stops = CATEGORIES.map((c, i) => `${c.bg} ${i * SEG}deg ${(i + 1) * SEG}deg`).join(', ');
  wheel.style.background = `conic-gradient(from ${-SEG / 2}deg, ${stops})`;

  // 扇区中心相对 12 点方向的角度:0°, 72°, 144° ...
  // 用三角函数换算成百分比坐标,标签不旋转、水平显示,手机上更好读
  const R = 30; // 圆心到标签的距离(占半径百分比)
  wheel.innerHTML = CATEGORIES.map((c, i) => {
    const rad = (i * SEG) * Math.PI / 180;
    const left = 50 + R * Math.sin(rad);
    const top  = 50 - R * Math.cos(rad);
    return `
      <div class="absolute -translate-x-1/2 -translate-y-1/2 text-center select-none pointer-events-none"
           style="left:${left}%; top:${top}%">
        <i class="fa-solid ${c.icon} text-[15px] sm:text-base block mb-0.5" style="color:${c.text}"></i>
        <span class="block text-[13px] sm:text-sm font-semibold leading-tight" style="color:${c.text}">${c.name}</span>
      </div>`;
  }).join('');
}

/* 分类 chips:展示每个分类有多少道菜 */
function buildChips() {
  $('catChips').innerHTML = CATEGORIES.map(c => `
    <span class="inline-flex items-center gap-1 text-[11px] px-2.5 py-1 rounded-full bg-white border border-slate-200 text-slate-500">
      <i class="fa-solid ${c.icon}" style="color:${c.text}"></i>${c.name}
      <b class="font-semibold text-slate-700">${(DISHES[c.name] || []).length}</b>
    </span>`).join('');
}

/* 菜品库速查:可折叠查看全部菜名 */
function buildLibrary() {
  $('libBox').innerHTML = CATEGORIES.map(c => `
    <div class="bg-white rounded-2xl shadow-card p-3.5">
      <div class="flex items-center gap-2 mb-2">
        <i class="fa-solid ${c.icon} text-xs" style="color:${c.text}"></i>
        <span class="text-sm font-medium">${c.name}</span>
        <span class="text-[11px] text-slate-400">${(DISHES[c.name] || []).length} 道</span>
      </div>
      <div class="flex flex-wrap gap-1.5">
        ${(DISHES[c.name] || []).map(d => `<span class="text-[11px] px-2 py-1 rounded-lg bg-slate-50 text-slate-600 border border-slate-100">${d.name}</span>`).join('')}
      </div>
    </div>`).join('');
}

/* ------------------------------------------------------------------
 * ④ 抽奖逻辑
 * ------------------------------------------------------------------ */

/** 计算转盘应停到的角度:使选中分类的中心对准顶部指针 */
function computeRotation(currentRotation, index) {
  const cur = ((currentRotation % 360) + 360) % 360;      // 归一化到 [0,360)
  const target = (360 - index * SEG) % 360;               // 扇区 i 中心对准指针所需的角度
  let delta = target - cur;
  if (delta <= 0) delta += 360;                           // 始终顺时针多转,避免来回摆动
  // 在扇区内加一点随机偏移(±28°,扇区半宽 36°),避免每次都停在正中间,更真实
  const jitter = (Math.random() * 2 - 1) * 28;
  return currentRotation + SPIN_TURNS * 360 + delta + jitter;
}

/** 从分类菜品池随机取一道,尽量不与上一道重复(池子只有 1 道时除外) */
function pickDish(categoryName) {
  const pool = DISHES[categoryName] || [];
  if (!pool.length) return null;
  let i = Math.floor(Math.random() * pool.length);
  if (pool.length > 1 && lastPick && pool[i].name === lastPick.dish.name) {
    i = (i + 1 + Math.floor(Math.random() * (pool.length - 1))) % pool.length;
  }
  return pool[i];
}

/** 开始抽奖:按钮点击的唯一入口,内部用 spinning 保证不会重复触发 */
function startSpin() {
  if (spinning) return;                 // ★ 防重复点击
  spinning = true;
  setSpinUI(true);
  $('resultCard').classList.add('hidden');

  const index = Math.floor(Math.random() * CATEGORIES.length);  // 先随机决定分类
  rotation = computeRotation(rotation, index);

  wheel.style.transition = `transform ${SPIN_MS}ms cubic-bezier(.16,.84,.24,1)`;
  wheel.style.transform = `rotate(${rotation}deg)`;

  // 动画结束后再揭晓结果(监听 transform 结束,避免其它属性误触发)
  const onEnd = e => {
    if (e.propertyName !== 'transform') return;
    wheel.removeEventListener('transitionend', onEnd);
    const category = CATEGORIES[index];
    const dish = pickDish(category.name);
    lastPick = { category: category.name, dish };
    showResult(category, dish);
    spinning = false;
    setSpinUI(false);
  };
  wheel.addEventListener('transitionend', onEnd);
}

/** 转动中禁用按钮并切换图标文案 */
function setSpinUI(isSpinning) {
  $('btnSpin').disabled = isSpinning;
  $('btnCenter').disabled = isSpinning;
  $('spinText').textContent = isSpinning ? '转盘转动中...' : '开始抽奖';
  $('spinIcon').className = isSpinning ? 'fa-solid fa-spinner fa-spin' : 'fa-solid fa-rotate';
  $('centerIcon').className = isSpinning ? 'fa-solid fa-spinner fa-spin text-lg' : 'fa-solid fa-play text-lg';
  $('btnSpin').classList.toggle('spin-ready', !isSpinning);
}

/** 展示结果卡片:菜名 + 家常做法 */
function showResult(category, dish) {
  if (!dish) return;
  $('resIcon').style.background = category.bg;
  $('resIcon').style.color = category.text;
  $('resIcon').innerHTML = `<i class="fa-solid ${category.icon}"></i>`;
  $('resDish').textContent = dish.name;
  $('resCat').textContent = category.name;
  $('resCat').style.background = category.bg;
  $('resCat').style.color = category.text;
  $('resHow').textContent = dish.how;

  const card = $('resultCard');
  card.classList.remove('hidden');
  card.classList.remove('animate-pop');
  void card.offsetWidth;              // 强制重排,使动画能重复播放
  card.classList.add('animate-pop');
  card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}

/* ------------------------------------------------------------------
 * ⑤ 交互绑定
 * ------------------------------------------------------------------ */
$('btnSpin').addEventListener('click', startSpin);      // 主按钮
$('btnCenter').addEventListener('click', startSpin);    // 中心圆按钮

// 换一道:不转转盘,直接在当前分类里重抽
$('btnReroll').addEventListener('click', () => {
  if (spinning || !lastPick) return;
  const category = CATEGORIES.find(c => c.name === lastPick.category);
  const dish = pickDish(lastPick.category);
  lastPick = { category: lastPick.category, dish };
  showResult(category, dish);
});

// 再转一次:重新走完整转盘流程
$('btnAgain').addEventListener('click', startSpin);

// 菜品库折叠
$('btnToggleLib').addEventListener('click', () => {
  const box = $('libBox');
  box.classList.toggle('hidden');
  $('libArrow').style.transform = box.classList.contains('hidden') ? '' : 'rotate(180deg)';
});

// 空格键也能转(PC 便利)
document.addEventListener('keydown', e => {
  if (e.code === 'Space' && !spinning) { e.preventDefault(); startSpin(); }
});

/* ---------- 初始化 ---------- */
buildWheel();
buildChips();
buildLibrary();
setSpinUI(false);
</script>
</body>
</html>
相关推荐
志尊宝4 小时前
Vue3 零基础每日笔记(035):什么是 Composables——mixin 之死与逻辑复用新方案
笔记·vue·html·前端开发·软件开发
xy34534 小时前
Axure 9.0 中继器创建与设置步骤
前端·ui·html·axure·原型·产品设计
志尊宝5 小时前
Vue3 零基础每日笔记(030):动态组件与 keep-alive——切换组件与页面缓存
笔记·vue·html·前端开发·软件开发
志尊宝1 天前
Vue3 零基础每日笔记(026):emit 子传父——defineEmits 与事件校验
前端·javascript·html·vue3
IMPYLH1 天前
HTML 的 <section> 元素
前端·html
csuzhucong1 天前
混元一号、二号、三号
html
用户298698530141 天前
Python 将 HTML 转换为 Word 文档的实践指南
python·html·api
IMPYLH1 天前
HTML 的 <select> 元素
前端·html
前端·柱子1 天前
Chaikin‘s Corner Cutting 算法 应用canvas绘制平滑的曲线
前端·html