preface
Hello, in the first two articles, we walked into the world of front-end low code and revealed the core of low code - the implementation of page designer. When revealing the page designer, we focused on sharing the component drag method of sequential layout. In the comment of that article [2], a little partner asked about the implementation of free layout. In this article, we will share the implementation principle of free layout drag and realize the simplest demo of free drag of designer components.
How to make elements support dragging
The core of realizing the free drag of components is the newly added global attribute draggable attribute in html5, which specifies whether the element can be dragged. The attribute values are as follows:
- true: Specifies the draggable of the element
- false: Specifies that the element cannot be dragged
- auto: uses the browser's default behavior
When we add the draggable attribute to the element tag, the element can be dragged.
<div draggable > Draggable element </div> Copy code
Drag event
Event classification
When the element can be dragged, we can use the element drag event to control the start end logic of the drag. The drag event is mainly divided into two categories. One category is triggered by dragging the element:
- dragstart: triggered when the mouse clicks on an element and starts moving
- drag: triggered continuously during dragging
- Drag: triggered when the mouse is released after dragging
The other is that when an element is dragged to a target element, the target element will trigger:
- Drawer: triggered when an element is dragged onto a target
- dragover: drag the element in the target element and trigger it continuously
- dragleave: triggered when leaving the target element
- Drop: triggered when you drop an element into the target element and release the mouse
Drag drop behavior
In the drag event, we will get the dragged event object (e). In the drag object, we can get an important attribute dataTransfer. We can control the placement behavior of the dragged element through the dropEffect attribute of dataTransfer. The description of its value is as follows.
- none: you cannot drag and drop elements here
- Move: move to target
- Copy: copy to target
- Link: the target opens the drag element (the drag element must be a link and have a URL)
Implementation of page designer
Let's implement the simplest demo of page designer component drag according to the above knowledge points. First, let's define the component list and canvas area
<template> <div> <!-- List of components on the left --> <div class="left"> <div class="left-item" v-for="item in list1" :key="item.code" draggable > {{ item.name }} </div> </div> <!-- Canvas area --> <div class="targetContent" ref="targetContent"> <div class="item" v-for="item in list2" :key="item.id" :ref="item.id" :style="{ top: `${item.top - 16}px`, left: `${item.left - 85}px`, 'z-index': `${item.zIndex}` }" > <template v-if="item.code === 'MyInput'"> <a-input></a-input> </template> </div> </div> </div> </template> Copy code
The component list and the pages in the canvas are traversed and rendered through list1 and list respectively.
<script> import _ from "lodash"; export default { data() { return { list1: [ { code: "MyInput", name: "Input box", props: {} } ], list2: [], }; } } </script> Copy code
Let's analyze how to drag the components in the component list to the canvas. As mentioned above, a series of events can be set for the dragged elements and target elements, so we can set the dragstart event for each component when rendering the component list. In this event, we need to do the following:
- Set the placement behavior of the dragged element to move, that is, move.
- When the component passes through the target element, the default behavior must be blocked, otherwise drop cannot be triggered.
- Set the drop behavior when the component leaves the target element as no drag and drop, that is, none.
- Drag the element to add the element to the canvas when the target element is released, that is, add the component metadata to list2, and the metadata corresponding to the element also records the coordinate position of the component in the screen.
Then listen to the above actions in the Draft event.
Let's implement the above process through code. First, when traversing the component list, add the dragstart and drag events of the component.
@dragstart="e => dragstart(e, item)" @dragend="dragend" Copy code
The following is the implementation of these two events.
dragstart(e, item) { this.dragItem = item; // Set element placement behavior - move this.$refs.targetContent.addEventListener("dragenter", this.dragenter); // The default behavior must be blocked when the target element passes, otherwise drop cannot be triggered this.$refs.targetContent.addEventListener("dragover", this.dragover); // Set the placement behavior of the element when leaving the target element -- you can't drag and drop this.$refs.targetContent.addEventListener("dragleave", this.dragleave); // Drag the element to add the element to the canvas when the target element is released this.$refs.targetContent.addEventListener("drop", this.drop); }, dragend(e) { this.$refs.targetContent.removeEventListener("dragenter", this.dragenter); this.$refs.targetContent.removeEventListener("dragover", this.dragover); this.$refs.targetContent.removeEventListener("dragleave", this.dragleave); this.$refs.targetContent.removeEventListener("drop", this.drop); }, dragenter(e) { e.dataTransfer.dropEffect = "move"; }, dragover(e) { e.preventDefault(); }, dragleave(e) { e.dataTransfer.dropEffect = "none"; }, drop(e) { const { code } = this.dragItem; this.list2.push({ top: e.offsetY, left: e.offsetX, zIndex: 1, code: code, id: Date.parse(new Date()) }); this.dragItem = null; } Copy code
In this way, the components in our component list can be dragged to the canvas.
How can the components dragged to the canvas move flexibly by dragging? Similarly, we can add the mousedown event to the components in the canvas. In the event, we add the monitoring of the mousemove event. When the components in the canvas move, we update the metadata coordinates corresponding to the moved element in real time. The following is the implementation of the code.
mousedown(e, item) { this.moveItem = item; document.addEventListener("mousemove", this.mousemove); document.addEventListener("mouseup", this.mouseup); }, mousemove(e) { const _this = this; let { clientX, clientY } = e; const moveIdx = _.findIndex(this.list2, function(o) { return o.id === _this.moveItem.id; }); let newList2 = _.cloneDeep(this.list2); newList2[moveIdx].top = clientY; newList2[moveIdx].left = clientX; this.list2 = newList2; }, mouseup(e) { document.removeEventListener("mousemove", this.mousemove); document.removeEventListener("mouseup", this.mouseup); } Copy code
In this way, the components in the canvas can be moved.
Postscript
In this article, we implemented the simplest demo of free layout of page designer components, so that we can understand the implementation principle of free drag. As for the processing of some details, you can realize it yourself according to your own needs ~ let's pay attention to the small partners interested in this series of articles. We will continue to share and communicate with you.
Finally, let's look at the complete demo code.
<template> <div> <!-- List of components on the left --> <div class="left"> <div class="left-item" v-for="item in list1" :key="item.code" draggable @dragstart="e => dragstart(e, item)" @dragend="dragend" > {{ item.name }} </div> </div> <!-- Canvas area --> <div class="targetContent" ref="targetContent"> <div class="item" v-for="item in list2" :key="item.id" :ref="item.id" :style="{ top: `${item.top - 16}px`, left: `${item.left - 85}px`, 'z-index': `${item.zIndex}` }" @mousedown="e => mousedown(e, item)" > <template v-if="item.code === 'MyInput'"> <a-input></a-input> </template> </div> </div> </div> </template> <script> import _ from "lodash"; export default { data() { return { list1: [ { code: "MyInput", name: "Input box", props: {} } ], list2: [], dragItem: null, moveItem: null }; }, methods: { dragstart(e, item) { this.dragItem = item; // Set element placement behavior - move this.$refs.targetContent.addEventListener("dragenter", this.dragenter); // The default behavior must be blocked when the target element passes, otherwise drop cannot be triggered this.$refs.targetContent.addEventListener("dragover", this.dragover); // Set the placement behavior of the element when leaving the target element -- you can't drag and drop this.$refs.targetContent.addEventListener("dragleave", this.dragleave); // Drag the element to add the element to the canvas when the target element is released this.$refs.targetContent.addEventListener("drop", this.drop); }, dragend(e) { this.$refs.targetContent.removeEventListener("dragenter", this.dragenter); this.$refs.targetContent.removeEventListener("dragover", this.dragover); this.$refs.targetContent.removeEventListener("dragleave", this.dragleave); this.$refs.targetContent.removeEventListener("drop", this.drop); }, dragenter(e) { e.dataTransfer.dropEffect = "move"; }, dragover(e) { e.preventDefault(); }, dragleave(e) { e.dataTransfer.dropEffect = "none"; }, drop(e) { const { code } = this.dragItem; this.list2.push({ top: e.offsetY, left: e.offsetX, zIndex: 1, code: code, id: Date.parse(new Date()) }); this.dragItem = null; }, mousedown(e, item) { this.moveItem = item; document.addEventListener("mousemove", this.mousemove); document.addEventListener("mouseup", this.mouseup); }, mousemove(e) { const _this = this; let { clientX, clientY } = e; const moveIdx = _.findIndex(this.list2, function(o) { return o.id === _this.moveItem.id; }); let newList2 = _.cloneDeep(this.list2); newList2[moveIdx].top = clientY; newList2[moveIdx].left = clientX; this.list2 = newList2; }, mouseup(e) { document.removeEventListener("mousemove", this.mousemove); document.removeEventListener("mouseup", this.mouseup); } } }; </script> <style lang="less" scoped> .left { padding: 10px; position: absolute; width: 270px; background: rgb(247, 202, 202); top: 0; bottom: 0; left: 0; } .left-item { height: 100px; line-height: 100px; background: #fff; } .targetContent { background: rgb(173, 244, 247); height: 100vh; padding: 0 270px; } .item { position: absolute; } </style> Copy code
Author: fleshy siege lion
https://juejin.cn/post/7019820870537314334