Build a Vue3 Flowchart Editor from Scratch: Node Dragging, Bezier Curves, and Box Selection
For Western developers building low-code platforms, workflow engines, or data orchestration tools, this project offers a production-ready blueprint for one of the most complex UI components. The Service class pattern and event bus architecture provide a clean, maintainable approach to managing the intense interactivity that flowchart editors demand.
Building a flowchart editor from scratch is the best way to truly understand the core interactions behind low-code and workflow tools. A new open-source Vue3 project demonstrates this by implementing node dragging, Bezier curve connections, box selection, and infinite canvas features entirely from the ground up.
The project's architecture separates each complex interaction into its own Service class—DragElementNodeService, DrawLineService, RectangleSelect, and ScrollParent—keeping the Vue component clean and focused on event dispatching. A global event bus (mitt) broadcasts mouse events to all services, solving the common problem of losing drag events when the cursor leaves the node.
Key implementation details include: recording mouse offset relative to the node on drag start to prevent jumping, multi-select linkage that moves all selected nodes together with boundary protection, cubic Bezier curves that create professional-looking connections with automatic snapping to valid target points, and a rectangular selection that detects fully enclosed nodes and their associated connections for bulk operations.
The Service class pattern is a pragmatic alternative to complex state machines for managing multi-interaction UI components—each service owns a slice of the interaction logic and communicates via events.
Recording mouse offset on drag start is a small detail that dramatically improves UX; many amateur implementations skip this and cause node jumping.
The decision to use a single global event listener rather than per-service listeners is a smart tradeoff—it prevents event loss during fast interactions and centralizes event management.
Bezier curve control point placement is an underappreciated design decision; the 'down then left' approach here mimics how professional tools like draw.io handle connections.
The connection rule validation (no self-connections, type matching, single connection per pair) shows that even a 'simple' flowchart needs a surprising amount of business logic.
Using toRaw() before serializing Vue reactive data is a best practice that many developers overlook, leading to serialization errors with Proxy objects.
The auto-scroll implementation with a global flag is a clean solution to a common problem—preventing scroll interference when the user is just hovering near the edge.