拖放过程可取消且能回到原位
别名: 取消拖放 · drag cancel · return to origin
概念解释
拖放应支持取消并回到原位(drag cancellation and return to origin):用户在移动阶段发现对象或目的地不对时,可以退出拖动而不改变任何已有状态,对象带着一段回弹动画飞回起点。取消不同于把对象放到无效区域后弹出报错——报错是提交发生之后才发现不该提交,取消是提交发生之前主动收回,两者对状态的影响完全不同:前者往往已经产生了副作用需要撤销,后者理论上不该留下任何痕迹。
机制
要做到"不留痕迹",关键在拾起阶段绝不能真的移动底层数据,只能建立一个临时的视觉绑定:对象的显示层跟着指针走,但它在列表、文件系统或文档里的真实位置原封不动,直到放下那一刻才提交。这样一来,取消只需要销毁这个临时绑定,不需要额外的回滚逻辑。反过来,如果实现图省事,在拾起时就乐观地把对象从来源列表里摘掉(常见于某些跨列表拖拽的即时视觉反馈实现),取消就变成了"把已经发生的删除撤销回去"——这时如果来源列表在拖动期间被其他操作(后台同步、协作者的并发编辑)改变了,简单的撤销会对不上原来的位置,只能靠额外的状态恢复逻辑硬凑,出错概率也随之上升。
触发取消的方式有几种,彼此不能互相替代:桌面上按 Escape 键是最直接的显式取消命令;把对象拖回起点附近松手,靠的是命中判定把"回到来源"当成一个特殊的有效目的地;释放在一个专门划出来的安全取消区(比如某些系统把拖出窗口边界之外视为取消);触屏上没有键盘,只能依赖后两种。
边界
取消只能收回还没提交的操作,不能掩盖拖动过程中已经发生的副作用。跨应用拖拽如果在移动阶段就触发了文件复制、网络上传或自动保存(这类操作有时会在悬停超过一定时间后预先启动,为的是让放下时响应更快),取消时这些已经开始的动作需要单独说明能不能中止,不能假装什么都没发生。基于浏览器原生 HTML5 拖放 API 实现的网页应用是另一类边界:拖放会话的开始与结束由浏览器接管,应用代码能收到的事件有限,键盘 Escape 是否触发取消、取消后浏览器是否补发一致的收尾事件,取决于具体浏览器实现,不能假定所有平台行为一致。
怎么落地
- 拾起阶段只建立视觉绑定,真实数据在放下前不改变;这条实现原则本身就决定了取消是否廉价可靠。
- 为鼠标、触摸、键盘和辅助输入提供各自可达的取消触发方式,触屏至少要有"拖回原处"和"划出安全区"两种。
- 已经产生真实副作用的拖动(上传、跨应用复制、自动保存)单独设计取消或中止提示,不与普通拖放共用同一套反馈。
- 验证办法:在拾起后、移动中途、抵达无效目标附近三个时间点分别触发取消,对比取消前后的数据快照是否完全一致,并检查选择状态与键盘焦点是否也一并恢复。