Frontend Development 14 min read

Implementation of Component Drag‑and‑Drop in the Wukong Low‑Code Visual Platform

This article explains how the Wukong low‑code visual platform implements component drag‑and‑drop, covering material‑to‑canvas dragging, intra‑canvas movement, resizing via eight handles, alignment guide generation, performance optimizations using CSS transforms, and component encapsulation with Vue 3.

Zhengtong Technical Team
Zhengtong Technical Team
Zhengtong Technical Team
Implementation of Component Drag‑and‑Drop in the Wukong Low‑Code Visual Platform

The article begins with a historical overview of data visualization and introduces Wukong as a low‑code platform that relies heavily on drag‑and‑drop interactions for building large‑screen visualizations.

It then details the technical implementation, focusing on three main aspects: component dragging, component resizing, and alignment guides.

Component Dragging

Dragging from the material area to the canvas uses native HTML5 drag events. The dragstart event is bound to the material element, and the drop event on the canvas adds the component at the drop coordinates:

// 内容区添加组件
onDrop(e: DragEvent) {
  this.isDragDroping = false;
  this.addCard(e.offsetX, e.offsetY);
}

During dragging, drag and dragover events fire continuously; preventing the default behavior of dragover enables the canvas to accept the drop:

function handleDragOver(e) {
  // 阻止默认的重置行为
  e.preventDefault();
}
// 也可以直接在元素上添加如下代码
@dragover.prevent="() => {}"

Intra‑canvas dragging tracks mouse movements. On mousedown the initial position is recorded, mousemove updates the component’s left and top values using the difference between current and previous mouse coordinates, and mouseup ends the operation:

onMousedown = (evt: MouseEvent) => {
  mouseClickPosition.mouseX = evt.pageX;
  mouseClickPosition.mouseY = evt.pageY;
  document.documentElement.addEventListener("mousemove", mousemove, true);
  document.documentElement.addEventListener("mouseup", mouseup, true);
};

mousemove = (evt: MouseEvent) => {
  left.value = left.value + (evt.pageX - mouseClickPosition.mouseX);
  top.value = top.value + (evt.pageY - mouseClickPosition.mouseY);
  mouseClickPosition.mouseX = evt.pageX;
  mouseClickPosition.mouseY = evt.pageY;
};

Component Resizing

Resizing is achieved with eight handles (tl, tm, tr, mr, br, bm, bl, ml). Each handle registers a mousedown event that starts a resize operation, adjusting width or height based on mouse movement. Example for the top‑right handle:

const ononMousedownHandle = (item: any, evt: any) => {
  if (evt.stopPropagation) evt.stopPropagation();
  handle.value = item;
  mouseClickPosition.mouseX = evt.pageX;
  mouseClickPosition.mouseY = evt.pageY;
  document.documentElement.addEventListener("mousemove", handleResize, true);
  document.documentElement.addEventListener("mouseup", handleResizeUp, true);
};

handleResize = (evt: MouseEvent) => {
  if (handle.value === "tr" && evt.pageY - mouseClickPosition.mouseY < height.value) {
    width.value = width.value + (evt.pageX - mouseClickPosition.mouseX);
    height.value = height.value + (mouseClickPosition.mouseY - evt.pageY);
    top.value = top.value + (evt.pageY - mouseClickPosition.mouseY);
    mouseClickPosition.mouseX = evt.pageX;
    mouseClickPosition.mouseY = evt.pageY;
  }
};

Alignment Guides

During dragging, auxiliary lines are generated to assist alignment. The system compares the moving component’s top and left values with those of other components on the canvas (retrieved via parentNode.childNodes ) and creates guide lines when the values are within a ±4 px tolerance. Sample code for vertical alignment:

if (top.value > nodeTop - 4 && top.value < nodeTop + 4) {
  top.value = nodeTop;
  rtlShow = true;
  if (left.value > nodeLeft) {
    rtlStyle.tlWidth = left.value - nodeLeft + width.value;
    rtlStyle.tlLeft = nodeLeft;
  } else {
    rtlStyle.tlWidth = nodeLeft - left.value + nodeWidth;
    rtlStyle.tlLeft = left.value;
  }
  rtlStyle.tlTop = top.value;
  rtlStyle.tlHeight = 1;
  rtlStyle.tlDisplay = "inline-block";
}

These guides support various alignment cases such as top‑to‑top, top‑to‑bottom, bottom‑to‑bottom, bottom‑to‑top, and center‑to‑center, improving layout precision.

Performance Optimizations

The implementation prefers CSS transform: translate(...) over absolute positioning because it leverages GPU acceleration, reduces CPU calculations, and creates a new compositing layer, resulting in smoother interactions.

Component Encapsulation

To avoid adding drag logic to each material component individually, the drag functionality is encapsulated into a reusable Vue component. The component renders the eight handles and a slot for the actual content, separating drag behavior from the visual elements:

<div :class="['vue-drag-resize', { active: enabled }]" ref="vdrag" :style="style" @mousedown="onMousedown" @click.stop.prevent="onClick">
  <div v-for="(item, index) in handles" :key="index" :style="handleStyle" :class="['handle', 'handle-' + item]" @mousedown.stop.prevent="ononMousedownHandle(item, $event)"></div>
  <slot></slot>
</div>

Finally, the article concludes that drag‑and‑drop is a core interaction in low‑code platforms, and the upcoming grouping feature will further enhance layout efficiency.

frontendlow-codecomponentalignmentvisualizationVue3Drag and Drop
Zhengtong Technical Team
Written by

Zhengtong Technical Team

How do 700+ nationwide projects deliver quality service? What inspiring stories lie behind dozens of product lines? Where is the efficient solution for tens of thousands of customer needs each year? This is Zhengtong Digital's technical practice sharing—a bridge connecting engineers and customers!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.