尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
T恤DIY定制系统:自研Canvas渲染器与设计快照打通订单全链路
简介这是一份针对移动端场景的T恤在线DIY定制系统源码面向有一定前端基础的学习者、在线定制类产品开发者以及课程设计人群。系统以HTML5绘图画布为核心让用户在T恤模板上自由添加文字、图形、素材并实时预览当前版本中文字尚未完全写入绘图画布这一未闭合点恰好为学习者提供了完整的canvas与文本合成训练场景。压缩包大小约14.19MB整个资源按主页面、图片素材、JavaScript逻辑、字体库、样式表等模块组织便于按需对照阅读代码结构直白也适合直接作为定制类项目的起步骨架。已有797人浏览学习。读者可以获得DIY设计工具的前端交互思路包括模板选择、元素拖拽、字体切换、图层合成等关键环节同时也能对照源码梳理canvas绘图、移动端适配、资源目录规划等实践细节。无论是想快速搭建个性化定制原型、完善T恤设计工作流还是用于课程设计、毕业设计或企业技术预研都很有落地参考价值。1. 先啃DIY设计器拖拽卡顿或坐标错位整个T恤DIY系统就废了做T恤DIY定制系统很多人一上来就排订单、管库存结果评审拿到手第一件事是往T恤上拖几个字试试手感——拖不动、位置乱跳、字转不了角度后面功能再全也白搭。这套方案的开发顺序是反着来的先把核心精力压在T恤DIY设计交互上用自研Canvas渲染器搞定拖拽、缩放旋转、文字编辑、图片上传和图层管理订单、商品、素材这些后端模块反而用的是标准电商套路。整套系统覆盖选款、设计、加购、下单、生产还原的完整链路前端画布和后端订单通过一份设计快照JSON打通。这套方案适合做电商方向毕设或课设的同学照搬结构也适合要做定制类H5的从业者直接改成自己的业务。2. 总体架构设计前后端分离核心是五模块与四张数据表2.1 技术选型前端自研轻量渲染器不引第三方画布库DIY设计器这块主流做法是直接引一个封装好的画布库拖拽、缩放、旋转都是现成的Object。但这类库有两个问题第一定制样式时要去读它的内部源码黑匣子一样答辩时讲不清坐标换算逻辑第二打包体积大移动端首屏加载慢。这套方案选择用原生Canvas API自研一套轻量渲染器只封装自己用到的功能元素渲染、命中检测、拖拽、缩放旋转控制点、辅助线对齐。后端用Node.js Express和前端同语言适合小团队一套技术栈维护。如果课设要求Java把接口层换成Spring Boot即可表结构和接口契约不变。数据库用MySQL存订单和素材Redis只用来存短期购物车会话DIY设计器产生的核心数据是设计快照JSON直接落MySQL的TEXT字段不单独引入文档数据库。这样整条链路只有一套语言、一个数据库部署和答辩演示都省事。2.2 功能模块划分五个模块各自的边界与依赖关系系统拆成五个模块模块之间单向依赖避免循环调用。商品模块管T恤基础款颜色、尺码、基础价和印花素材图案、字体设计器模块是核心负责画布渲染、元素操作和设计快照生成订单模块接收设计快照、计算价格、生成订单素材管理模块给运营用上传图案、设置分类、管理上下架后台管理模块处理订单列表和设计预览图。依赖关系上设计器模块只依赖商品模块提供的素材接口不直接碰订单表订单模块依赖设计器模块产出的快照结构但不反向调设计器。这样分工的好处是课设里可以几个人各做一块联调时只需要对齐设计快照JSON的字段格式不用互相等。我一般会在项目根目录放一份docs/api.md把每个模块的接口和数据结构写清楚后面联调能少吵很多架。2.3 数据库设计四张核心表与字段约束数据库总共四张核心表不搞冗余设计。product表存T恤基础款字段包括sku_id、名称、颜色、尺码、基础价格、状态material表存T恤DIY素材包括素材id、类型图案/字体、URL、分类、上传时间orders表存订单主信息design_snapshot表存设计快照这是DIY系统区别于普通电商表结构的关键。表名关键字段说明productsku_id, name, color, size, base_price, status基础款商品颜色和尺码组合成不同SKUmaterialid, type, url, category_id, statustype区分image/fonturl存永久地址ordersid, order_no, user_id, total_price, status, created_atstatus枚举待支付/待生产/生产中/已完成design_snapshotid, order_id, product_id, snapshot_json, preview_url, statussnapshot_json存画布全部元素preview_url存导出PNG需要注意design_snapshot的order_id和orders的id是1对1关系但要在设计阶段就确认好一个订单可以后续改设计重新生成快照所以快照表要留version和created_at字段否则订单要支持二次编辑时会发现历史版本覆盖丢了没有后悔药可吃。2.4 接口契约设计保存与订单创建的关键接口前后端联调最容易翻车的就是接口字段没对齐。设计保存接口POST /api/design/save请求体就是设计快照JSON后端原样存进design_snapshot表订单创建接口POST /api/order/create传product_id和design_id后端根据快照里的元素数量和面积计算价格。素材列表接口GET /api/materials?categoryimage返回素材数组供设计器加载。这三个接口的字段结构以设计快照JSON为准前端设计器是唯一生产者后端只做存储和校验。我在接口层加了一层简单的schema校验保存快照时校验elements数组非空、每个元素必须有id和type避免脏数据进库。这块用JSON Schema或者手写十来行校验都行关键是要在保存入口挡住格式错误而不是等生产端渲染时才暴露。3. DIY设计器实现画布拖拽、缩放旋转与文字编辑的三步落地3.1 画布初始化与坐标体系设计稿坐标是一切交互的基础DIY设计器首先要解决的是坐标系问题。画布在用户屏幕上会随容器宽度变化如果直接存鼠标的像素坐标换一台设备渲染时所有元素位置全错位。我用的方案是定义一个虚拟设计稿坐标系设计稿固定为750×1000所有元素坐标、尺寸都按设计稿坐标存储渲染时用Canvas的scale换算到实际画布。const DESIGN_W 750 const DESIGN_H 1000 // 鼠标事件坐标换算为设计稿坐标 function toDesignCoord(clientX, clientY) { const rect canvas.getBoundingClientRect() const scaleX DESIGN_W / rect.width const scaleY DESIGN_H / rect.height return { x: (clientX - rect.left) * scaleX, y: (clientY - rect.top) * scaleY } }这个函数的含义是不管画布在屏幕上被缩放了多少触点坐标除以实际显示尺寸再乘设计稿尺寸就得到设计稿坐标系下的位置。后面拖拽、缩放、旋转全部依赖这一个换算函数不做第二套坐标系。初始化画布时把T恤底色模板绘制到Canvas上相当于一个背景图层上面的设计元素都挂在独立的元素数组里不画进背景。3.2 元素渲染与拖拽数组驱动渲染命中检测决定操作对象设计画布上的每一个元素不管是文字还是图片都会落成一个结构相同的对象存入elements数组。渲染时按数组顺序逐个绘制这样数组顺序天然就是图层顺序不需要额外维护zIndex。// 单个设计元素的结构 element { id: elm_001, type: text, // text | image x: 120, y: 180, // 设计稿坐标下的左上角位置 scaleX: 1, scaleY: 1, // 缩放因子 rotation: 0, // 旋转角度度 opacity: 1, content: 毕业纪念, // 文本内容或图片URL fontSize: 28, color: #ffffff, zIndex: 0 // 数组下标渲染时按序绘制 }拖拽交互拆成三步mousedown时记录起始点和元素初始位置mousemove时计算位移差并更新元素坐标mouseup时清空拖拽状态。这里最关键的坑是mousedown时要先做命中检测——从数组末尾往前遍历先命中的是最上层元素避免点到被遮挡的图层。let dragState null canvas.addEventListener(mousedown, (e) { const p toDesignCoord(e.clientX, e.clientY) const hit hitTest(p) // 从最上层元素往下找 if (!hit) return dragState { id: hit.id, startX: p.x, startY: p.y, originX: hit.x, originY: hit.y } }) canvas.addEventListener(mousemove, (e) { if (!dragState) return const p toDesignCoord(e.clientX, e.clientY) updateElement(dragState.id, { x: dragState.originX (p.x - dragState.startX), y: dragState.originY (p.y - dragState.startY) }) renderCanvas() // 全量重绘元素少时性能足够 }) canvas.addEventListener(mouseup, () { dragState null })hitTest的实现要点文字元素用内容尺寸估算包围盒图片元素按图片宽高乘scaleX得到缩放后尺寸然后判断触点是否在包围盒内。全量重绘在元素数量少于50个时完全够用不用做局部脏矩形渲染这是DIY场景和复杂可视化场景的典型区别。3.3 缩放旋转与控制点包围盒四角命中旋转柄独立判定选中元素后要显示控制点用户拖右下角缩放、拖外侧旋转柄旋转。控制点的命中检测要在拖拽命中检测之前做——先判断是否点在控制点上再判断是否点在元素上。// 控制点绘制选中元素后画8个方块 1个旋转柄 function drawControlPoints(ctx, el) { const box getBoundingBox(el) // 考虑旋转后的包围盒 // 四角 四边中点共8个缩放控制点 const points [[box.x, box.y], [box.cx, box.y], [box.right, box.y], ...] // 旋转柄画在顶部外侧单独判定 ctx.fillStyle #ffffff ctx.strokeStyle #333 points.forEach(([px, py]) { ctx.fillRect(px - 4, py - 4, 8, 8) ctx.strokeRect(px - 4, py - 4, 8, 8) }) }缩放时更新scaleX和scaleY旋转时更新rotation围绕元素中心点做旋转矩阵变换。这里容易踩坑的是旋转后的包围盒要重新计算不能用旋转前的宽高直接判定命中。我一般会在元素对象里缓存一个本地坐标系下的尺寸再用rotation做矩阵变换得到屏幕上的包围盒这样每次渲染和命中检测都走同一套变换函数不会出现拖拽时元素位置和鼠标对不上的玄学问题。3.4 文本编辑与图片上传双击进入编辑态素材直达物料库文本元素编辑采用双击进入编辑态的方式双击文字元素后在画布对应位置动态插入一个透明的textarea字体样式和元素保持一致失焦后把内容写回元素并重新渲染。这个交互比弹窗输入框体验好实现也不复杂关键是textarea的定位要复用toDesignCoord的逆运算把设计稿坐标转回屏幕坐标。图片上传走独立的物料接口。用户从本地上传图片后前端先把图片上传到服务器拿到永久URL后再生成图片元素加入画布。不要直接存base64到快照里否则快照体积膨胀到几百KB订单表会很难看。上传接口返回的URL后面要拼上传时间戳做版本管理方便运营替换素材时能追溯历史订单用的是哪一版图案。4. 数据与订单打通设计快照从画布到生产端的流转链路4.1 设计快照JSON画布状态的唯一事实来源设计快照是整个系统的数据枢纽。画布上的一切操作最终都序列化成一份JSON存进design_snapshot表。它的结构必须能完整还原画布状态包括画布尺寸、背景色、元素列表、每个元素的所有属性。{ designId: DS20240001, productId: TS-BLACK-M, canvas: { width: 750, height: 1000, background: #ffffff }, elements: [ { id: elm_002, type: image, x: 260, y: 140, scaleX: 0.65, scaleY: 0.65, rotation: 0, opacity: 1, src: /upload/pattern_20240101_fa3c2b.png, zIndex: 0 }, { id: elm_003, type: text, x: 300, y: 400, scaleX: 1, scaleY: 1, rotation: -8, opacity: 1, content: 青春不散场, fontSize: 36, color: #ffcc00, zIndex: 1 } ] }两个设计要点一是元素的id在创建时生成后不再变化即使元素被删掉历史快照里的id仍然有效二是src字段必须是永久地址我踩过坑存了临时上传地址结果运营后台清理临时文件后历史订单的设计图全裂了。现在只要涉及图片引用一律在写入快照前确认地址已落库。4.2 价格计算基础价、印花面积与促销规则的联动T恤DIY定制和普通商品的价格模型不一样除了基础款价格还要按设计量加价。这套方案的价格公式是订单总价 商品基础价 印花面积费用 印色数费用。印花面积费用按所有元素的包围盒总面积估算印色数按设计里用到的不同颜色数量计。function calcDesignPrice(snapshot, product) { let totalArea 0 const colorSet new Set() snapshot.elements.forEach(el { const box getBoundingBox(el) totalArea box.width * box.height if (el.type text) colorSet.add(el.color) if (el.type image) colorSet.add(img_ el.id) }) const areaPrice Math.ceil(totalArea / 10000) * 5 // 每万平方单位收5元 const colorPrice Math.max(0, colorSet.size - 1) * 3 // 超1色每色加3元 return product.basePrice areaPrice colorPrice }这个计算逻辑放在前端还是后端需要想清楚。我放在后端订单创建接口里算前端展示的价格只是预估最终以下单接口返回为准防止有人改前端参数拿低价。前端设计器里要实时展示预估总价可以调一个计算接口或者本地用同一套函数算一遍但要标注为参考价。4.3 生产端还原JSON用于二次编辑PNG用于实际打样生产端拿什么去印制T恤如果只拿JSON工厂的软件不可能解析你的画布结构所以必须导出设计预览图。我的做法是双轨保存下单时前端把当前画布导出成一张高分辨率PNG存到preview_url同时把JSON存进snapshot_json。JSON的意义在于用户以后想改设计重新下单而PNG用于生产打样和订单列表展示。// 导出高清预览图临时放大画布导出后恢复 function exportPreview() { const exportCanvas document.createElement(canvas) exportCanvas.width 1080 exportCanvas.height 1440 const ctx exportCanvas.getContext(2d) ctx.scale(1080 / DESIGN_W, 1440 / DESIGN_H) renderDesign(ctx) // 复用同一套渲染函数 return exportCanvas.toDataURL(image/png) }导出时注意背景底图和元素的绘制顺序与在线预览保持一致。还有一个细节是T恤底图本身要单独存一份无水印的透明PNG模板导出时才叠加上去否则印出来的衣服上会带屏幕预览的水印框。这个坑我在第一次联调时翻过车工厂打样出来衣服上多了一道灰色边框。5. 高频踩坑清单交互坐标、图层顺序与移动端适配的排查记录5.1 拖拽时元素与鼠标错位一个身位现象在PC端设计器里拖文字元素总比鼠标慢半拍或偏一段距离缩放到不同窗口大小时偏移量还不一样。原因Canvas的CSS宽度和实际渲染宽度不一致但鼠标事件用的是像素坐标没有换算到设计稿坐标系。解决所有交互入口统一走toDesignCoord函数换算坐标不要直接拿clientX减offsetLeft。排查时先在画布中心放一个元素拖到右上角看元素坐标和触点坐标是否一致误差超过2px就检查换算函数。5.2 导出PNG时toDataURL抛SecurityError现象用户上传本地图片后画布上显示正常一导出预览图就报SecurityError导出功能直接不可用。原因用户图片是从第三方图床加载的Canvas被标记为跨域污染toDataURL被浏览器拦截。解决加载素材图片时设置img.crossOrigin anonymous同时要求素材服务器返回Access-Control-Allow-Origin响应头自建的素材上传接口必须把图片转存到自己域名下不要直接存第三方地址。5.3 保存后再打开图层顺序全乱现象设计器里明明把文字调到最上层保存后重新加载图片又把文字盖住了。原因前端用数组顺序渲染后端却单独存了zIndex字段重新序列化时zIndex排序和数组顺序没对齐同值时排序不稳定。解决约定数组下标就是渲染顺序快照里不存zIndex保存时按数组顺序原样写入需要调整图层时重排数组再重新渲染。这样快照JSON里少一个字段反而少一类bug。5.4 移动端拖拽时整个页面跟着滚现象手机浏览器上操作设计器手指一拖动图片页面就开始滚动拖拽完全没法用。原因touchmove事件的默认行为是滚动页面没有阻止默认事件同时也存在双击缩放和单击编辑的冲突。解决给画布容器加上touch-action: none的CSS样式在touchmove监听器里preventDefault单击和双击的区分用250ms延时判定移动距离小于5px算单击否则算拖拽。5.5 后台替换素材后历史订单设计图变裂图现象运营把某张热门图案从素材库删除后所有用过这张图的历史订单详情页都显示碎图。原因设计快照里存的是素材的旧地址素材被替换或删除后地址失效而生产端预览图PNG又没有提前导出。解决下单时同步导出预览PNG并保存到快照表图片URL采用版本化路径素材更新生成新URL旧URL保留指向旧文件不物理删除只有手动清理任务才清。6. 验收与答辩功能清单、演示路径和四个高频提问验收不能只看功能能不能点通要按一条真实使用路径从头走到尾。我每次交付DIY类系统都会强制走一遍验收清单按这个清单逐项打勾再安排答辩演示路径。验收项操作步骤通过标准选款选颜色、尺码加入设计商品信息正确回显到画布拖拽拖文字和图片各一次无偏移、无跳动缩放旋转缩放到150%再转45度控制点命中准确包围盒跟随文字编辑双击修改内容、换色画布即时更新快照同步图片上传本地上传一张图并拖入画布生成永久URL刷新不丢下单完成设计后创建订单价格正确预览图生成订单还原从订单列表进入查看设计画布状态与下单时一致异常验证断网后拖拽页面不白屏有错误提示答辩演示走三条路径第一条是完整的用户链路选款、拖字、传图、缩放旋转、加购下单重点演示设计器的手感第二条是数据链路打开数据库展示design_snapshot表的JSON和preview_url说明数据怎么流转到生产端第三条是后台链路演示素材管理和订单状态流转。三条路径控制在10分钟内讲细节时多讲坐标换算和双轨存储少讲CRUD。评委经常问的四个问题可以提前准备。为什么不用现成画布库自研实现可以精确控制交互细节和打包体积也能讲清楚坐标换算的底层逻辑。为什么存JSON还要再存一张PNGJSON是为了用户二次编辑PNG是为了生产端打样和快速加载职责不同。价格怎么防篡改计算放在后端接口前端价格仅作展示。历史订单素材删了怎么办下单时已导出预览图且素材URL版本化不物理删除。这四个问题答顺了答辩基本就稳了。我后来每次交付这类DIY系统都会强制把第5章的五个坑重新过一遍尤其是坐标换算和导出这两个最隐蔽的点。设计器交互这种功能演示时手感顺滑评委就相信整个系统的工程质量手感一卡后面讲再多也是补救。希望这套从设计器到订单打通的方案能帮到你整套T恤DIY定制的源码和方案文档都在下载包里拿回去对照着拆能省掉前期调研和踩坑的时间。本文还有配套的精品资源点击获取
RELATED

相关推荐

AI痕迹太重怎么办?一套可落地的降AI率改稿实操指南

AI痕迹太重怎么办?一套可落地的降AI率改稿实操指南

做内容时间久了,应该都有过这种体验:AI写初稿确实快,但发布完总觉得缺点什么——评论区没动静,甚至原本活跃的账号突然被“冷落”。这段时间“AI痕迹”成了做分享内容的朋友聊天时的高频词,很多平台和合作方也开始关注…

📅 2026/10/17 3:13:59
RAG调优实战:从F1 0.42到0.89的30天方法论

RAG调优实战:从F1 0.42到0.89的30天方法论

做RAG调优这件事,我踩过的坑不比任何人少。去年我接手一个内部知识库项目,检索增强生成系统的F1值一开始只有0.42,辛辛苦苦调了一个月,把最终成绩干到0.89。那段时间我每天做的事,就是把“玄学”变成“旋钮”——凡是能…

📅 2026/10/17 3:13:59
Android FTP服务器Demo:手机秒变局域网文件中转站

Android FTP服务器Demo:手机秒变局域网文件中转站

简介:一份演示在安卓设备上搭建FTP服务器的工程样例,适合需要实现局域网文件共享、远程上传下载的移动开发人员参考。项目基于Apache FtpServer组件完成,以简洁的代码结构展示了ftp服务从初始化、启动到停止的完整调用链,方便快速…

📅 2026/10/17 3:13:59
MORE NEWS

更多资讯

📰

LangGraph多智能体动态任务分配与专家调度实战

多智能体系统里最容易让人头疼的问题,不是单个智能体写不出来,而是多个智能体凑到一起之后,到底听谁的、怎么分工、任务来了往哪儿派。我自己在搭这一类系统时踩过不少坑,后来用LangGraph把能力路由这块理顺了,才算真正…

📰

AirLLM分层加载:单卡8GB显存跑70B大模型实战

1. 单卡跑大模型这件事,到底卡在哪第一次看到 airllm 这个项目标题的时候,我的直觉是:又是一个“在消费级显卡上跑大模型”的尝试。这类项目这两年层出不穷,但真正能跑通、跑稳、跑出可用速度的并不多。airllm 的核心思路很直接—…

📰

明明加了锁却还是超卖?分布式锁的边界与事务时序陷阱

生产环境凌晨一点,某电商团队的库存模块突然出现超卖。所有人第一反应是分布式锁没加好,排查了半天却发现锁的日志一切正常:同一个商品ID,加锁、释放记录完全成对出现,Redis里也没看到异常。可数据就是对不上&#xff…

📰

Spring Boot乐器培训管理系统实战:从数据库设计到部署调试

搞乐器培训管理系统这类 Spring Boot 项目,我前后做了好几版。单看需求列表,无非是学员管理、课程排期、教师排班、缴费记录、课时统计,挺常规的。但真正把这些功能串成一套能稳定跑起来的系统,里面的角色权限划分、业务状态流转、…

📰

Ranger Admin 2.0 Tar包部署指南:Hadoop权限治理实战

简介:面向大数据平台运维与开发人员的Ranger Admin 2.0控制台tar包,旨在解决官方文档多侧重安装说明、却缺乏可直接部署的编译后制品问题。Ranger作为Hadoop生态中的集中式权限管理组件,其编译过程依赖较多且耗时较长,该tar包直接…

📰

告别无标题项目:从混沌想法到可交付项目的完整流程

新建一个空白文档,系统默认给它取名叫“无标题”。你盯着那块纯白区域,脑子里好像有很多事,可真要写点什么,光标闪了半天也落不下一个字。我见过太多项目就死在这一步:保存的时候叫“无标题”,三个月后打开…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬