Vaadin 14中如何用DragTargetPanel实现两图间图片拖拽插入
Vaadin 14 基于DropTargetPanel实现图片间拖拽插入方案
Vaadin 14的Grid组件GridDropMode.BETWEEN能力没有特殊黑魔法,核心是给每一行的相邻间隙绑定独立拖拽落点、实时计算插入位置,你可以用完全一致的逻辑在DropTargetPanel上实现图片拖拽插入两图中间的效果,不需要依赖额外组件。
核心实现步骤
- 重构落点绑定逻辑:不要只给外层DropTargetPanel绑定全局drop事件(这也是当前实现只能把图片拖到列表末尾的根本原因)。初始化图片列表时,给每一张已渲染的图片,额外生成2个宽/高为4px的透明占位条:一个放在图片的上/左侧(对应插入到该图片之前),一个放在下/右侧(对应插入到该图片之后),每个占位条单独注册为DropTarget。列表首尾额外加两个独立占位条,对应插入到列表头部、尾部的场景,和Grid的首尾插入逻辑对齐。
- 拖拽过程的位置判定:给外层DropTargetPanel绑定
dragover客户端监听,拖拽过程中实时获取鼠标坐标,遍历当前所有图片的边界矩形,判定鼠标当前位置距离哪个间隙最近,就把对应位置的占位条设为高亮可见状态,其他占位条隐藏,给用户明确的插入位置提示,视觉效果和Grid拖拽时的插入指示线完全一致。坐标计算时记得叠加容器的滚动偏移量,避免滚动列表后位置判定错位。 - 插入逻辑处理:每个占位条初始化时就预先绑定好对应的插入索引值,比如第i张图片前侧的占位条对应插入索引
i,后侧的对应索引i+1。当图片释放在占位条上时,直接读取这个预存的索引做数据操作:如果是列表内拖拽排序,先把被拖拽的图片从原数据位置移除,注意如果原位置索引小于目标插入索引,插入时索引要减1,避免元素位移导致插入位置错误;如果是从容器外拖入新图片,直接把新图片数据插入到目标索引即可。数据更新后重渲染图片列表,重新给所有图片和占位条绑定事件监听。
核心参考代码
// 竖排图片列表场景的占位条初始化示例 private void attachDropPlaceholder(HtmlContainer anchor, int insertPos, boolean attachBefore) { Div insertIndicator = new Div(); insertIndicator.addClassName("drag-gap-indicator"); // 对应CSS配置:.drag-gap-indicator { height: 4px; width: 100%; background: #1976d2; margin: 2px 0; display: none; } // 给占位条绑定drop逻辑 DropTarget<Div> gapDropTarget = DropTarget.create(insertIndicator, false); gapDropTarget.addDropListener(event -> { ImageModel draggedItem = event.getDragData().orElse(null); if (draggedItem == null) return; // 同列表拖拽先移除原位置元素 int originalPos = imageDataList.indexOf(draggedItem); if (originalPos > -1) { imageDataList.remove(originalPos); } // 修正插入索引:原位置在目标位置前时,移除元素后列表长度减1,索引要做偏移 int finalInsertPos = insertPos; if (originalPos > -1 && originalPos < insertPos) { finalInsertPos--; } imageDataList.add(finalInsertPos, draggedItem); // 重渲染图片列表面板 refreshImagePanel(); }); // 把占位条挂载到对应间隙位置 if (attachBefore) { anchor.getElement().insertSibling(insertIndicator.getElement(), 0); } else { anchor.getElement().getParent().appendChild(insertIndicator.getElement()); } }
注意点:拖拽过程中切换占位条显示状态的逻辑建议写在客户端侧JS监听里,不要每次dragover都发请求到服务端计算位置,否则会有明显卡顿;占位条的宽高不要超过6px,避免遮挡图片本身的拖拽、点击事件。
内容的提问来源于stack exchange,提问作者Marcelo
相关产品推荐
相关产品推荐

