原生 draggable 无法支撑拖拽式页面编辑器,因其不记录位置、不管理层级、不支持嵌套;需用 position: absolute + mouse/touch 事件手动控制坐标、尺寸与 zindex,并仅存储结构元数据而非 html 字符串。

原生 draggable 属性做不了真正的拖拽式页面编辑器——它只负责“搬运”,不记录位置、不管理层级、不支持嵌套容器,硬上等于在画布上扔沙子,松手就散。
为什么 dragstart/drop 一用就崩
HTML5 拖放 API 的设计目标是文件上传、列表排序这类单次动作,不是 UI 构建工具:
-
dataTransfer只能传字符串或Blob,无法安全序列化组件配置或 DOM 结构 -
drop触发时,目标区域只是个空<div>,你得自己算坐标、判父子关系、插节点——逻辑全在 JS 里裸奔<li>Safari 对 <code>text/html类型的setData支持极差,移动端基本不可用 - 拖拽过程中的
ghost image是浏览器自动生成的快照,无法控制尺寸、遮罩或响应式缩放 - 给可拖拽元素设
position: absolute,初始left/top由 JS 动态写入(比如从localStorage读) - 监听
mousedown→ 记录起始偏移;mousemove→ 实时更新left/top;mouseup→ 保存最终坐标到状态对象 - 必须加
user-select: none,否则拖动时文字被选中会打断流程 - 移动端需额外处理
touchstart/touchmove,且要禁用touch-action: none防止页面缩放冲突 - 每个模块用唯一
id标识,例如"header-1"、"chart-2" - 状态对象里只记:
{ id: "chart-2", x: 120, y: 80, width: 320, height: 240, zIndex: 10 } - 渲染时用 JS 动态生成 DOM,再根据状态对象批量 apply 样式,避免
innerHTML拼接 - 如果支持组件化,把
type: "button"、props: { text: "提交" }也存进去,而不是存渲染后的结果 - 没限制拖拽边界:元素拖出视口后,
left变成负几千,下次加载直接看不见 —— 要在mousemove里做Math.max(0, Math.min(containerWidth - elWidth, newX)) - 多个模块重叠时
zIndex管理混乱:不能靠 DOM 顺序,必须用数字字段显式控制,每次拖拽结束时统一重排所有元素的zIndex - 没做 transform 偏移补偿:如果父容器用了
transform: scale(0.8)或rotate(),鼠标坐标要反向换算,否则拖动错位
position: absolute + mouse/touch 才是可控起点
想真正控制每个模块的位置、尺寸、堆叠顺序,必须绕过原生 API,用底层事件组合实现:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
布局数据别存 HTML 字符串,只存结构元数据
把整个 <div class="card"></div> 存进 localStorage 是自找麻烦——改个 class 名或删个属性,下次加载直接白屏。
上线后最容易挂的三个边界点
功能做完不等于能用,真实用户操作会立刻暴露设计盲区:
最常被忽略的是 transform 坐标换算和 zIndex 的动态重排——这两个点不处理,用户拖两下就发现“怎么又回到后面去了”“怎么越拖越歪”。










