news 2026/9/15 18:12:10

移动端点餐H5 DEMO详解:触摸滑动、购物车与订单流程实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
移动端点餐H5 DEMO详解:触摸滑动、购物车与订单流程实现

简介:一款基于HTML5、JavaScript与CSS构建的移动端点餐系统DEMO,聚焦餐饮App的菜单浏览、菜品分类、购物车、订单评价等核心场景,适合Web前端初学者、移动端开发入门者,也适合需要课程设计或毕业设计原型的高校学生,代码可直接运行并二次扩展。压缩包共93个文件,大小仅1.23MB,包含23个HTML页面、7个CSS样式表、7个JavaScript脚本、30个PNG、22个JPG与3个GIF图片,另有一个.project工程配置文件;HTML负责页面结构与内容分区,CSS承担界面配色、尺寸和移动端响应式布局,JS实现轮播图、触屏滑动、购物车增删、订单联动等交互逻辑,完整演示了前端三要素如何协同工作。当前已有778人学习下载,文件目录按功能拆分清晰,适合快速定位菜单页、购物车页、订单页等模块,作为移动端页面工程和交互设计的入门模板,也能充当课程设计或毕业设计的蓝本,帮助读者快速梳理出一套点餐应用的前端实现思路。

1. 移动端点餐H5:为什么这套DEMO值得拆开看

移动端点餐最麻烦的不是菜单展示,而是购物车状态同步和触摸滚动。最近拆了一套用HTML5、CSS、JavaScript直接手写的APP点餐系统DEMO,压缩包只有几百KB,跑起来却覆盖了菜单分类、商品详情、购物车、订单提交、退款和评价全部流程。有意思的是它还在用jQuery 1.3.2和touchSlider这类老组件,但正因为老,代码逻辑直观,没有框架黑盒,适合想搞懂移动端页面交互原理的开发者。无论是做毕设还是接手传统Web项目,这套DEMO都能提供不少可复用的页面骨架和交互写法。下面对照源码,把布局、触摸事件和订单流程的关键处逐一说明。

2. 从文件结构到CSS分层:点餐系统的页面骨架怎么组织

点餐系统不像后台管理系统,页面多、跳转快,每一屏承载的内容相对单一。这套DEMO用多个HTML文件组织页面,而不是做成SPA,这种结构在传统Web项目里非常常见,好处是页面间解耦,一个文件坏了不影响其他页面。先看清文件组织,再动CSS,会顺畅很多。

2.1 文件清单:每个页面在点餐流程里负责什么

解压后先别急着开浏览器,把文件树理一遍。压缩包里的页面和样式是配套的,页面命名已经透露出业务划分。

├── index.html # 首页:搜索、轮播、分类入口 ├── recommend2.html # 推荐菜品列表 ├── vegetableplate.html # 素菜分类页 ├── meatdish.html # 荤菜分类页 ├── hot2.html # 热销榜 ├── new2.html # 新品列表 ├── discount2.html # 折扣菜品 ├── snack2.html # 小吃分类 ├── goodsdetails.html # 商品详情 ├── shopcar.html # 购物车 ├── order.html # 订单提交 ├── refund.html # 退款说明/申请 ├── evaluate.html # 评价页面 ├── css/ │ ├── common.css # 基础重置与公用样式 │ ├── index.css # 首页专属样式 │ ├── subpage.css # 二级页面通用样式 │ ├── menu.css # 菜单页样式 │ ├── shopcar.css # 购物车样式 │ └── order.css # 订单样式 ├── js/ │ ├── jquery-1.3.2.js │ ├── touchslider.dev.js │ ├── touchScroll.js │ ├── run.js │ ├── shopcar.js │ └── order.js └── img/ # 菜品图、按钮图、背景图

从这个结构能看出,页面职责是按点餐动作拆的:浏览菜单、看详情、加购、下单、售后。每个HTML对应一个具体场景,页面标题和文件名一一对应,后期维护时想改哪个流程直接找同名文件即可。img目录里cp1.jpgcp11.jpg是菜品图,top_bg.pngsearch_bg.png这类是界面背景元素,量不大,但说明了图片资源和页面样式是紧密绑定的。

2.2 CSS分层:common.css到shopcar.css各管什么

这套DEMO的CSS按“公用层 -> 页面层”划分,虽然没用SCSS变量和BEM命名,但文件边界很清楚。看一个CSS文件不要只看类名,要看它被哪些页面引用,以及里面定义的组件是否跨页面复用。

CSS文件主要职责引用范围
common.css重置默认边距、字体、公共按钮、价格颜色、通用弹层全部页面
index.css首页搜索栏、轮播区、分类宫格、推荐位布局首页及部分二级页
subpage.css列表页头图、返回栏、卡片间距菜单、详情、评价等二级页
menu.css菜品分类标签、菜品卡片、加号按钮各分类菜单页
shopcar.css购物车列表、步进器、底部结算栏购物车页
order.css订单表单、配送方式、提交按钮订单提交页

调试时可以遵循一个习惯:全局性的间距和颜色改common.css,单个页面的布局改对应页面CSS,避免到处覆盖。这套DEMO中2common.css这类文件存在冗余,但常见做法是保持一个公用样式表,后续新页面直接复用。

2.3 响应式布局怎么写:从固定宽度到媒体查询

点餐页面的核心使用场景是手机,但这套DEMO里没有引入rem或vw方案,大量宽度使用百分比和固定像素混合。对于快速交付的DEMO,这种做法能跑,但遇到不同屏幕适配会吃力。常见做法是保留原有结构,在CSS末尾追加媒体查询,把关键字体、触控按钮和底部结算栏单独适配。

@media screen and (max-width: 480px) { .food-card .price { font-size: 14px; } .food-card .cart-btn { width: 32px; height: 32px; } .cart-bar { width: 100%; bottom: 0; left: 0; } .category-nav { overflow-x: auto; -webkit-overflow-scrolling: touch; } }

这段代码做了三件事:在480px以下屏幕缩小价格字号,让加购按钮保持足够触控面积,把购物车结算栏固定在底部并禁止横向溢出。-webkit-overflow-scrolling: touch是iOS上启用惯性滚动必须写的属性,否则手指滑动列表会显得生硬。如果后续要适配iPhone SE这类小屏,把max-width: 480px改成min-width: 320pxmax-width: 374px分段控制即可。

2.4 HTML5语义化在DEMO里的实际落点

虽然源码里很多地方还是div嵌套,但改造时优先用headerfootersectionnav替换高频区域。首页的结构可以这样理解:顶部header放搜索和定位,中间section放轮播,下面nav放分类入口,底部footer固定放购物车入口。语义化不是给机器看的,而是让后来维护的人扫一眼结构就知道页面分成几块。这套DEMO的index.html虽然写成了div,但类名已经接近语义化,比如search_bgcategory-nav,顺着类名就能找到对应区域。

3. 触摸滑动与购物车状态:JavaScript交互层拆解

页面布局只能解决“看起来像点餐系统”,真正的使用体验取决于两个交互:触摸滑动是否跟手,购物车状态是否即时更新。这套DEMO用的是jQuery 1.3.2配合两个老牌组件——touchslider.dev.js负责轮播滑动,touchScroll.js负责列表区域滚动。这两个组件几十行代码就实现了核心功能,搞懂它们对理解移动端触摸事件很有帮助。

3.1 touchSlider和touchScroll:老组件的调用方式

轮播组件在这套DEMO里一般用在首页展示位,调用方式比较固定。从源码看,touchSlider.dev.js内部封装了touchstarttouchmovetouchend三个事件,通过计算位移量切换容器位置。

var bannerSlider = touchSlider({ slide: '.banner-list', // 轮播容器选择器 dot: '.banner-dot', // 底部指示器选择器 auto: 3000, // 自动轮播间隔,0表示关闭 effect: 'slide' // slide: 滑动切换,fade: 淡入淡出 });

参数里auto是按毫秒计算的,3000代表3秒自动切换一张。DEMO里常见的坑是轮播图数量不固定时,dot指示器和slide容器没有同步刷新,解决方案是在动态添加轮播项后重新调用一次初始化函数。effect参数如果改成fade,触摸滑动的效果会变成半透明过渡,但组件内部依赖的位移计算逻辑不同,不建议轮播图超过5张时使用。

区域滚动用touchScroll.js,它的作用是固定一个容器高度,让内部内容独立滚动,菜单分类页常用。

new TouchScroll({ wrap: document.getElementById('categoryWrap'), target: document.querySelector('.category-inner'), vertical: true });

wrap是外层容器,必须有固定高度;target是实际滚动的子元素,高度如果小于外层,则滚动失效。vertical: true表示纵向滚动,横向滚动设为false后,需要配合overflow-x: auto才能正常滑动。这两个组件都依赖原生DOM事件,不额外引入库,性能上比setInterval实现平滑很多。

3.2 购物车逻辑:商品加入、数量变更与总价计算

购物车是点餐系统的核心状态,shopcar.js里维护了一个简单的对象字典。代码不长,但把增删改查和总价计算都收纳在了一起,后期想接入后端接口也很容易替换。

var Cart = { items: {}, add: function(id, name, price, num) { if (this.items[id]) { this.items[id].num += num; } else { this.items[id] = { name: name, price: price, num: num }; } this.render(); }, reduce: function(id) { if (!this.items[id]) return; this.items[id].num--; if (this.items[id].num <= 0) { delete this.items[id]; } this.render(); }, total: function() { var sum = 0; for (var k in this.items) { sum += this.items[k].price * this.items[k].num; } return sum.toFixed(2); }, render: function() { var box = document.getElementById('cartList'); if (!box) return; box.innerHTML = ''; for (var k in this.items) { var item = this.items[k]; var li = document.createElement('li'); li.innerHTML = '<span>' + item.name + '</span>' + '<button onclick="Cart.reduce(\'' + k + '\')">-</button>' + '<i>' + item.num + '</i>' + '<button onclick="Cart.add(\'' + k + '\',\'' + item.name + '\',\'' + item.price + '\',1)">+</button>'; box.appendChild(li); } document.getElementById('totalPrice').innerHTML = this.total(); } };

这里用items对象作为商品字典,键为菜品ID,值为包含名称、价格和数量的子对象。total()将每个商品的价格乘数量累加,最后用toFixed(2)保留两位小数。注意reduce里对数量减到0的商品执行了delete,购物车列表就不会出现数量为0的项目。内联onclick在DEMO里没问题,但正式项目建议改成事件绑定,避免字符串拼接错误。如果菜品价格有小数,price必须以数字类型传入,否则字符串拼接会出现“5.9+1”等于“5.91”这种错误。

3.3 事件委托与动态菜品列表

菜单页面里的菜品卡片往往通过循环生成,直接给每个按钮绑定click事件会产生大量监听器,而且后续动态添加商品时事件会丢失。常见做法是使用事件委托,把监听器挂到外层列表容器上。但注意,这套DEMO用的是jQuery 1.3.2,还没有.on()方法,得用.delegate()

$('#menuList').delegate('.add-btn', 'click', function() { var btn = $(this); Cart.add( btn.attr('data-id'), btn.attr('data-name'), parseFloat(btn.attr('data-price')), 1 ); });

delegate接收三个参数:子元素选择器、事件类型、处理函数。事件冒泡到#menuList时由框架判断触发元素是否匹配.add-btn。这里用parseFloat转换价格,就是为了避免购物车计算时的字符串类型问题。>function validateOrder(form) { var receiver = form.receiver.value.trim(); var phone = form.phone.value.trim(); var address = form.address.value.trim(); var errors = []; if (receiver.length < 2) { errors.push('收货人姓名至少2个字符'); } if (!/^1\d{10}$/.test(phone)) { errors.push('手机号必须是11位数字'); } if (address === '') { errors.push('配送地址不能为空'); } if (errors.length > 0) { alert(errors.join('\n')); return false; } return true; }

trim()去掉首尾空格避免用户误触空格导致校验失败。正则/^1\d{10}$/要求必须1开头,后面10位数字,一共11位,简单够用。如果要支持座机或400电话,这个正则需要再放宽。校验不通过时alert集中提示比逐字段红框更容易让新手理解,但生产环境一般会换成页面内联提示,因为alert会被移动端浏览器拦截。

4.2 订单状态流转:用数组代替散落的if else

订单提交后,前端往往需要根据状态显示不同文案,比如“待支付”“制作中”“配送中”“已完成”。直接在页面里写几十个if else很难维护,更清晰的做法是维护一个状态数组,用索引或标识映射到DOM。

var ORDER_STATUS = [ { key: 'pending', label: '待支付', cssClass: 'status-gray' }, { key: 'paid', label: '已支付', cssClass: 'status-blue' }, { key: 'making', label: '制作中', cssClass: 'status-orange' }, { key: 'delivery', label: '配送中', cssClass: 'status-blue' }, { key: 'done', label: '已完成', cssClass: 'status-green' }, { key: 'refund', label: '已退款', cssClass: 'status-red' } ]; function renderOrderStatus(statusKey) { var status = null; for (var i = 0; i < ORDER_STATUS.length; i++) { if (ORDER_STATUS[i].key === statusKey) { status = ORDER_STATUS[i]; break; } } if (status) { document.getElementById('orderStatus').innerHTML = status.label; document.getElementById('orderStatus').className = status.cssClass; } }

这个写法把“状态文案”和“状态样式”集中在一起,新增一个状态只需要往数组里加一项。renderOrderStatus只负责根据key查找并更新DOM,代码里没有业务判断。如果后端返回的状态码是数字,把数组里的key改成数字即可。这套DEMO的order.js里没有这个封装,是常见的重构方向。

4.3 评价交互:星级选择与文本提交

评价页面evaluate.html的核心是星级选择,通常由五个li标签组成,点击某个星级后,前面的节点全部激活。这里用jQuery遍历下标来控制样式类。

function setRating(score) { $('#starList li').each(function(index) { if (index < score) { $(this).addClass('active'); } else { $(this).removeClass('active'); } }); $('#ratingScore').val(score); }

score是用户点击的星级数,从1到5。each回调里的index从0开始,所以index < score的判断能正确点亮前score个星星。#ratingScore是隐藏域,用来在表单提交时携带评分值。注意评价文本区域一般还需要做字数限制,常见做法是监听keyup事件实时统计字符数,超过规定长度时截断。

退款页面refund.html相对简单,主要是展示退款原因和协议说明,点击提交后回到订单列表。整个订单、评价、退款链路,前端只负责展示和校验,真正的状态持久化需要配合后端接口。但学习这套DEMO时,先用localStorage把订单数据存本地,就能完整走通整个流程。

5. 支付倒计时与hash跳转:让DEMO在演示时不露怯

静态DEMO最尴尬的环节是点击“提交订单”之后,不知道该跳去哪。演示时观众只能看着你打开console手动改数据。与其等着露出破绽,不如直接加一个支付成功页,再用hash做页面跳转,十几行代码就能让流程看起来像真正的产品。

(function() { var remain = 60; var timer = setInterval(function() { remain--; var dom = document.getElementById('countdown'); if (dom) { dom.innerHTML = remain; } if (remain <= 0) { clearInterval(timer); location.hash = '#/order'; } }, 1000); window.addEventListener('hashchange', function() { var hash = location.hash; if (hash === '#/pay-success') { remain = 60; } }); })();

这段代码放在支付成功页里,页面加载后开始倒计时,每秒更新页面上的countdown节点,倒计时归零自动跳转到订单列表。location.hash改变会触发hashchange事件,所以当用户通过浏览器前进后退回到支付成功页时,倒计时会重置为60秒,避免继续使用旧计时器。要实现这个流程,先建一个pay-success.html,在里面放一个<span id="countdown">60</span>,然后让order.html提交按钮的点击事件末尾加上location.hash = '#/pay-success'

如果不想增加新页面,还可以在现有order.html里放一个隐藏的div,倒计时结束后直接把divdisplay切换为block,用样式覆盖原本的提交按钮。这种做法适合演示环境下不想增加多余文件的场景。最后补一个细节:倒计时期间要给用户一个终止操作,比如“取消支付”按钮,否则产品经理会追着你要补单逻辑。把取消按钮绑定到clearInterval(timer)即可,注意清理定时器后,不要再次触发hashchange里的重置逻辑。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 18:10:46

stc89c52驱动的小型清扫机器人系统设计|毕业设计项目|毕设项目|单片机项目|物联网专业|毕业设计

一、项目介绍 摘 要 一台能自主规避障碍、边行进边吸尘的小型机器人&#xff0c;设计并实现了一款基于STC89C52单片机的小型清扫机器人系统。系统以STC89C52单片机作为核心控制器&#xff0c;将E18-D80NK红外避障传感器、L298N电机驱动模块、BD681风扇驱动电路与按键输入电路有…

作者头像 李华