draggable="true"仅触发浏览器视觉拖影而不改变DOM位置,真正流畅位移动画需手动用transform+transition实现,并注意避免累积、精准drop后过渡收尾及移动端touch适配。

draggable="true" 本身不触发任何位移动画
加了 draggable="true" 只会让浏览器启用拖拽光标、生成半透明拖影,但元素 DOM 位置和 CSS 样式完全不变。你看到的“拖动效果”是浏览器渲染层临时合成的视觉反馈,松手后元素立刻回弹——这不是 bug,是原生 API 的设计逻辑。要实现真正流畅的位移动画,必须手动接管位置更新,并配合 CSS 过渡或 transform 动画。
用 transform + transition 实现平滑移动
直接改 style.left/style.top 会频繁触发 layout,卡顿明显;transform: translate() 走 GPU 合成路径,性能更好。关键点在于:每次更新前先重置 transform,再设新值,避免累积叠加:
- 在
dragstart中记录初始getBoundingClientRect(),并保存当前transform值(如有) - 在
dragover或自定义拖动循环中,计算目标坐标后写入:el.style.transform = `translate(${x}px, ${y}px)` - 提前在 CSS 中加过渡:
transition: transform 0.15s cubic-bezier(0.25, 0.1, 0.25, 1); - drop 后别急着清除 transform——留着它,等 transition 结束再重置,否则动画中断
drop 后如何衔接动画收尾
松手瞬间的坐标往往不够精准(比如鼠标抖动、落点判断延迟),直接跳到最终位置会显得生硬。建议:
- 在
drop回调里先暂停 transition:el.style.transition = 'none' - 立即用
getBoundingClientRect()计算目标容器内精确插入点,调用insertBefore()或append()改变 DOM 顺序 - 强制重排后再恢复 transition:
el.offsetHeight; el.style.transition = '' - 最后设回自然位置:
el.style.transform = '',让 CSS 动画自动收回到布局定位
移动端 touch 模拟时动画更难稳住
iOS Safari 对 draggable 基本不可用,必须用 touchstart/touchmove 替代。这时要注意:
立即学习“前端免费学习笔记(深入)”;
-
touchmove中必须e.preventDefault()阻止页面滚动,但会导致惯性滚动失效——若需保留,得自己实现 momentum 算法 - 触摸点坐标要用
e.touches[0].clientX,不是e.clientX - 高频 touchmove 下直接设 style 不够顺滑,建议用
requestAnimationFrame节流更新 transform - CSS
will-change: transform在部分安卓 WebView 中反而拖慢,真机测试比文档靠谱



















