基于VanillaJS的拖拽排序组件添加拖拽动画求助
Hey there! Awesome work getting that drag-and-drop grid up and running—it’s already functional, so adding smooth animations will take it to the next level. Let’s implement two key animations to match polished drag-and-drop UX: a floating preview of the dragged item, a placeholder to show where the item will land, plus smooth position transitions when items swap.
Key Animations We’ll Add
- Drag Preview: A scaled, semi-transparent clone of the item that follows your mouse while dragging
- Drop Placeholder: A dashed placeholder that appears when hovering over a target, letting you know where the item will slot in
- Smooth Swap Transition: CSS transitions to make item position changes feel fluid instead of abrupt
Updated CSS
.icons { margin: auto; border: 1px solid black; width: 600px; height: 200px; display: grid; grid-template-columns: auto auto auto; grid-gap:20px; } .icons span { text-align: center; padding: 20px; background-color: blueviolet; font-size: 18px; border-radius: 8px; cursor: grab; /* Add smooth transition for position/color changes */ transition: transform 0.3s ease, background-color 0.3s ease; } .icons span:active { cursor: grabbing; } /* Drag preview element */ .drag-preview { position: absolute; pointer-events: none; opacity: 0.7; transform: scale(1.1); transition: transform 0.2s ease; z-index: 1000; } /* Drop placeholder for target position */ .drop-placeholder { background-color: rgba(0,0,0,0.1); border: 2px dashed #666; border-radius: 8px; transition: all 0.2s ease; }
Updated JavaScript
var icons=[ {order:1,name:"tel",bg:'red'}, {order:2,name:"facebook",bg:'green'}, {order:3,name:"whatsup",bg:'blue'}, {order:4,name:"waze",bg:'yellow'}, {order:5,name:"gallery",bg:'purple'}, {order:6,name:"insta",bg:'grey'} ]; let dragPreview = null; // Track the drag preview element function rendericons(icons) { var zone=document.getElementById("icons"); var sorticons = icons.slice(0); sorticons.sort(function(a,b) {return a.order - b.order; }); var html=""; for(var x in sorticons) { html+="<span style='background-color:"+sorticons[x]['bg']+"' id='"+sorticons[x]['order']+"' draggable='true'>"+sorticons[x]['name']+"</span>"; } zone.innerHTML=html; } rendericons(icons); document.addEventListener("dragstart", function(event) { event.dataTransfer.setData("Text", event.target.id); event.target.style.opacity = "0.4"; // Create a preview element that follows the mouse dragPreview = event.target.cloneNode(true); dragPreview.classList.add("drag-preview"); document.body.appendChild(dragPreview); updateDragPreviewPosition(event); }); document.addEventListener("drag", function(event) { // Update preview position as mouse moves updateDragPreviewPosition(event); }); document.addEventListener("dragend", function(event) { event.target.style.opacity = "1"; // Clean up preview and placeholders if (dragPreview) { document.body.removeChild(dragPreview); dragPreview = null; } document.querySelectorAll(".drop-placeholder").forEach(el => el.remove()); }); // Handle placeholder on drag enter document.addEventListener("dragenter", function(event) { if (event.target.tagName === "SPAN" && event.target.id !== event.dataTransfer.getData("Text")) { const placeholder = document.createElement("span"); placeholder.classList.add("drop-placeholder"); event.target.parentNode.insertBefore(placeholder, event.target); } }); document.addEventListener("dragover", function(event) { event.preventDefault(); }); // Remove placeholder when leaving the target document.addEventListener("dragleave", function(event) { if (event.target.tagName === "SPAN") { const placeholder = event.target.previousElementSibling; if (placeholder && placeholder.classList.contains("drop-placeholder")) { placeholder.remove(); } } }); document.addEventListener("drop", function(event) { event.preventDefault(); var data = event.dataTransfer.getData("Text"); var itemtoreplace=event.target.id; replaceitems(data,itemtoreplace); }); function replaceitems(data,itemtoreplace) { if(itemtoreplace!="icons"){ // Clean up placeholder before re-rendering document.querySelectorAll(".drop-placeholder").forEach(el => el.remove()); var beforeindex=0; var replaceindex=0; for(x in icons) { if(icons[x]['order']==data) beforeindex=x; if(icons[x]['order']==itemtoreplace) replaceindex=x; } icons[beforeindex]['order']=itemtoreplace; icons[replaceindex]['order']=data; rendericons(icons); } } // Helper to update preview position function updateDragPreviewPosition(event) { if (dragPreview) { // Center preview on mouse cursor dragPreview.style.left = `${event.clientX - dragPreview.offsetWidth / 2}px`; dragPreview.style.top = `${event.clientY - dragPreview.offsetHeight / 2}px`; } }
Updated HTML (no changes needed, included for completeness)
<div id="icons" class="icons droptarget"></div>
How It Works
- Drag Preview: When you start dragging, we clone the active element, style it to look like a floating preview, and update its position every time the mouse moves. This gives clear visual feedback of what you’re dragging.
- Drop Placeholder: When hovering over another item, a dashed placeholder appears to indicate where the dragged item will be placed. It disappears when you move away from the target.
- Smooth Transitions: We added
transitionto the grid items so when their positions swap (via re-rendering), they move smoothly instead of jumping. The preview also scales slightly for extra emphasis during the drag.
内容的提问来源于stack exchange,提问作者davidi sheleg
相关产品推荐
相关产品推荐

