簡單的低開編輯器(二):實現元件拖拽

2023-11-21 21:01:01

好傢伙,

 

0.程式碼已開源

Fattiger4399/lowcode-demo: 一個簡單的低程式碼編輯器 技術棧:Vue3 element-plus jsx (github.com)

該章實現的效果:元件從物料區到畫布的拖拽

 

1.分析

 

 

先來分析,滑鼠點選物料區的某個元件,再將其拖拽到畫布這個過程

我們如何實現元件的拖拽??

  1. 滑鼠按下元件時獲取當前選中元件的資訊
  2. 繫結滑鼠的移動和鬆開事件,鬆開時在畫布上渲染當前塊。
  3. 將拖拽的元件渲染在畫布上。

 

<div>
            <div class="editor-left">
                {/* 根據註冊列表 渲染對應的內容 可以實現h5的拖拽*/}
                {config.componentList.map(component => (
                    <div class="editor-left-item"
                        draggable
                        onDragstart={e => dragstart(e, component)}
                        onDragend={dragend}
                    >
                        <span>{component.label}</span>
                        <div>{component.preview()}</div>

                    </div>
                ))}
            </div>

            <div class="editor-top">選單欄</div>
            <div class="editor-right">屬性控制欄目</div>
            <div class="editor-container">
                <div class="editor-container-canvas">
                    {/* 產生內容 */}
                    <div class="editor-container-canvas__content"
                        style={containerStyles.value}
                        ref={containerRef}
                        onMousedown={containerMousedown}>
                        {
                            (data.value.blocks.map(block => (
                                <EditorBlock
                                    class={block.focus ? 'editor-block-focus' : ''}
                                    block={block}
                                    onMousedown={(e) => blockMousedown(e, block)}
                                ></EditorBlock>
                            )))
                        }
                    </div>
                </div>
            </div>

        </div>

 

2.處理物料區   

import { registerConfig as config } from './utils/editor-config'

config為我們的元件登入檔資訊

 
  const { dragstart, dragend } = useMenuDragger(containerRef, data)

{config.componentList.map(component => ( <div class="editor-left-item" draggable onDragstart={e => dragstart(e, component)} onDragend={dragend} > <span>{component.label}</span> <div>{component.preview()}</div> </div> ))}

 

此處為物料元件新增onDragstart和onDragend事件

 

useMenuDragger.js

export function useMenuDragger(containerRef,data){
    let currentComponent = null;
        const dragenter = (e) => {
            e.dataTransfer.dropEffect = 'move'; //h5的拖動圖示
        }

        const dragover = (e) => {
            e.preventDefault();
        }

        const dragleave = (e) => {
            e.dataTransfer.dropEffect = 'none';
        }

        const drop = (e) => {
            //先留在這
            console.log(e.offsetY)
            console.log(e.offsetX)

            let blocks = data.value.blocks;//內部已渲染的元件
            data.value = {
                ...data.value, blocks: [
                    ...blocks,
                    {
                        top: e.offsetY,
                        left: e.offsetX,
                        zIndex: 1,
                        key: currentComponent.key,
                        alignCenter: true //鬆手時劇中 
                    }
                ]
            }
            currentComponent = null
        }
        const dragstart = (e, component) => {
            //dragenter進入元素中
            containerRef.value.addEventListener('dragenter', dragenter)
            containerRef.value.addEventListener('dragover', dragover)
            containerRef.value.addEventListener('dragleave', dragleave)
            containerRef.value.addEventListener('drop', drop)
            currentComponent = component
        }
        const dragend = (e) => {
            containerRef.value.removeEventListener('dragenter', dragenter)
            containerRef.value.removeEventListener('dragover', dragover)
            containerRef.value.removeEventListener('dragleave', dragleave)
            containerRef.value.removeEventListener('drop', drop)
        }
        return{
            dragstart,
            dragend
        }
}

 

 

3.處理畫布區域  

<div class="editor-container-canvas__content"
                        style={containerStyles.value}
                        ref={containerRef}
                        onMousedown={containerMousedown}>
                        {
                            (data.value.blocks.map(block => (
                                <EditorBlock
                                    class={block.focus ? 'editor-block-focus' : ''}
                                    block={block}
                                    onMousedown={(e) => blockMousedown(e, block)}
                                ></EditorBlock>
                            )))
                        }
                    </div>

元件傳值

當元件被拉倒畫布後

const drop = (e) => {
            //先留在這
            console.log(e.offsetY)
            console.log(e.offsetX)

            let blocks = data.value.blocks;//內部已渲染的元件
            data.value = {
                ...data.value, blocks: [
                    ...blocks,
                    {
                        top: e.offsetY,
                        left: e.offsetX,
                        zIndex: 1,
                        key: currentComponent.key,
                        alignCenter: true //鬆手時劇中 
                    }
                ]
            }
            currentComponent = null
        }

將新元件(拖拽元件)的相關值新增到data.value.blocks中,最後由渲染器進行渲染

(data.value.blocks.map(block => (
                                <EditorBlock
                                    class={block.focus ? 'editor-block-focus' : ''}
                                    block={block}
                                    onMousedown={(e) => blockMousedown(e, block)}
                                ></EditorBlock>
                            )))