大数跨境

告别吃饭纠结!一款自制前端小工具,帮你决定中午吃什么

告别吃饭纠结!一款自制前端小工具,帮你决定中午吃什么 wordpress知识
2026-10-03
22

告别选择困难:原生JS实现“中午吃什么”随机抽签工具

面对“中午吃什么”这一终极难题,本文将介绍一款基于纯原生HTML、CSS和JavaScript开发的前端小工具。该工具无需任何框架依赖,具备菜品滚动抽签动画、自定义管理及本地数据持久化功能,双击即可运行,既适合解决日常用餐纠结,也适宜前端新手作为实战练手项目。

核心功能亮点

  • 抽签滚动动画:菜品名称快速闪动,最终平稳定格,模拟真实抽奖体验。
  • 完整菜品管理:支持用户自定义添加或删除菜品,灵活配置菜单。
  • 本地数据持久化:利用localStorage存储数据,刷新或关闭页面后内容不丢失。
  • 交互防抖处理:抽签过程中禁用按钮,防止重复触发导致逻辑错误。
  • 极简响应式设计:UI自适应移动端与电脑端,视觉清爽简洁。

完整代码实现

<!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>
        *{margin:0;padding:0;box-sizing:border-box;font-family:system-ui, sans-serif;}
        body{
            min-height:100vh;
            background: linear-gradient(135deg,#fff7e6,#ffe8cc);
            display:flex;flex-direction:column;align-items:center;justify-content:center;
            padding:20px;
        }
        .card{
            background:#fff;border-radius:20px;padding:30px;
            box-shadow:0 8px 24px rgba(0,0,0,0.08);
            max-width:460px;width:100%;
        }
        h2{color:#333;margin-bottom:20px;font-size:24px;text-align:center;}
        .result{
            font-size:34px;font-weight:bold;color:#e67700;min-height:50px;margin:20px 0;text-align:center;
        }
        .btn{
            background:#ff9500;color:#fff;border:none;border-radius:12px;
            padding:13px 28px;font-size:17px;cursor:pointer;transition:0.2s;
        }
        .btn:hover{background:#e68600;transform: translateY(-2px);}
        .btn:disabled{background:#ffbc69;cursor:not-allowed;transform:none;}
        .btn-wrap{text-align:center;margin-bottom:20px;}

        .add-row{display:flex;gap:8px;margin-bottom:16px;}
        #addInput{
            flex:1;padding:11px;border:1px solid #ddd;border-radius:10px;font-size:15px;
        }
        .add-btn{
            background:#2b85e4;color:white;border:none;border-radius:10px;padding:0 16px;cursor:pointer;
        }

        .food-list{
            max-height:220px;overflow-y:auto;border:1px solid #eee;border-radius:10px;padding:10px;
        }
        .food-item{
            display:flex;justify-content:space-between;align-items:center;
            padding:8px 6px;border-bottom:1px solid #f3f3f3;
        }
        .food-item:last-child{border-bottom:none;}
        .del-btn{
            color:#f54b4b;background:none;border:none;cursor:pointer;font-size:14px;
        }
    </style>
</head>
<body>
    <div class="card">
        <h2>中午吃什么?</h2>
        <div class="result" id="foodResult">点击抽签</div>
        <div class="btn-wrap">
            <button class="btn" id="randomBtn">开始抽签!</button>
        </div>

        <div class="add-row">
            <input id="addInput" placeholder="输入菜品,点击添加">
            <button class="add-btn" id="addBtn">添加</button>
        </div>

        <div class="food-list" id="foodListBox"></div>
    </div>

    <script>
        const defaultFood = ['黄焖鸡','麻辣烫','兰州拉面','水饺','盖浇饭','螺蛳粉','牛肉汤','烤肉饭','汉堡','馄饨','炒菜米饭','米线'];
        let foodList = [];

        const randomBtn = document.getElementById('randomBtn');
        const foodResult = document.getElementById('foodResult');
        const addInput = document.getElementById('addInput');
        const addBtn = document.getElementById('addBtn');
        const foodListBox = document.getElementById('foodListBox');

        // 读取本地存储
        function loadData(){
            const save = localStorage.getItem("foodList");
            if(save){
                foodList = JSON.parse(save);
            }else{
                foodList = [...defaultFood];
            }
        }
        function saveData(){
            localStorage.setItem("foodList", JSON.stringify(foodList));
        }

        // 渲染菜品列表
        function renderList(){
            foodListBox.innerHTML = '';
            foodList.forEach((item,index)=>{
                const div = document.createElement('div');
                div.className = "food-item";
                div.innerHTML = `<span>${item}</span><button class="del-btn" data-i="${index}">删除</button>`;
                foodListBox.appendChild(div);
            })
        }

        // 删除菜品
        foodListBox.addEventListener('click',e=>{
            if(e.target.classList.contains('del-btn')){
                const idx = Number(e.target.dataset.i);
                foodList.splice(idx,1);
                saveData();
                renderList();
            }
        })

        // 添加菜品
        addBtn.onclick = ()=>{
            const val = addInput.value.trim();
            if(!val) return;
            foodList.push(val);
            saveData();
            renderList();
            addInput.value = '';
        }
        addInput.addEventListener('keydown',e=>{
            if(e.key === 'Enter') addBtn.click();
        })

        // 抽签闪动动画
        randomBtn.onclick = function(){
            if(foodList.length === 0){
                foodResult.innerText = "请添加菜品!";
                return;
            }
            randomBtn.disabled = true;
            let count = 0;
            const maxLoop = 30;
            const timer = setInterval(()=>{
                const rndIdx = Math.floor(Math.random() * foodList.length);
                foodResult.innerText = foodList[rndIdx];
                count++;
                if(count >= maxLoop){
                    clearInterval(timer);
                    const finalIdx = Math.floor(Math.random() * foodList.length);
                    foodResult.innerText = foodList[finalIdx];
                    randomBtn.disabled = false;
                }
            },80)
        }

        loadData();
        renderList();
    </script>
</body>
</html>

技术实现解析

数据持久化机制

工具利用浏览器的localStorage API进行数据存储。由于localStorage仅支持字符串格式,代码中通过JSON.stringify将数组转换为字符串存入,读取时再使用JSON.parse还原为数组对象。页面初始化时优先读取缓存数据,确保了用户自定义菜单在刷新或关闭页面后依然保留。

动态列表渲染策略

采用遍历数组动态生成DOM结构的方式渲染菜品列表。每次执行添加或删除操作后,清空容器并重新渲染整个列表视图。这种“数据驱动视图”的逻辑简洁明了,便于维护且易于理解。

抽签动画原理

动画效果通过setInterval定时器实现。在指定时间内快速随机抽取数组中的菜品名称并更新显示,营造出“闪动”视觉效果。当循环达到预定次数后,清除定时器并输出最终的随机结果,同时恢复按钮状态,模拟出真实的抽奖落地感。

该工具完全基于原生前端技术栈构建,涵盖了动画交互、数据管理及本地存储等核心能力,无需服务器部署,本地打开即可使用。不仅有效解决了日常用餐的选择困难症,也是前端开发者学习基础DOM操作与本地存储的优质实战案例。

#前端小工具

【声明】内容源于网络
0
0
wordpress知识
各类跨境出海行业相关资讯
内容 370
粉丝 0
wordpress知识 各类跨境出海行业相关资讯
总阅读11.7k
粉丝0
内容370