低代码BI大屏 之 拖拽器实现原理

3,236 阅读2分钟
1. 拖拽器作用

将左侧小组件拖拽到中心区域画布中

屏幕录制2023-04-19 下午4.09.28.gif

2.页面布局及各区域作用

image.png

工具

放置一些常用工具,如:撤销,删除,重置等;

左侧小组件列表

遍历渲染全部小组件,用来支持中心画布区域使用;

中心画布区域

承载左侧小组件组装成一个大屏页面,有标尺、标线等,小组件在画布中可随意拖动并支持8点放大缩小,支持鼠标放置小组件上后鼠标右键菜单,功能有撤销上一步、删除该小组件等;

右侧配置项

主要有三个部分,小组件数据源配置,小组件外观属性配置,小组件事件配置;

3.拖拽原理

html5中的支持拖拽的属性「draggable」,draggable设置为"true"时,表示该元素可拖拽;当元素为图片、链接、文本时,draggable属性默认为"true",无需手动设置,除非将上述三种元素禁用拖拽时,需手动设置draggable="false";其它元素需要手动设置draggable="true"来支持该元素的可拖拽;

主要步骤如下:
3.1 将需要拖拽的小组件设置draggable="true"
<component :is="comp.comp" draggable="true" />

// 全部小组件集合 
const widgetList = ref>([])

/** 初始化小组件 */ 
function initWidget() { 
    widgetList.value.push({ 
        index:1, 
        name: 'BarChart', 
        comp: BarChart 
      }, { 
        index:2, 
        name: 'LineChart', 
        comp: BarChart 
      }) 
 } 
 
 // 初始化小组件列表 
 initWidget()
3.2 拖拽区域设置拖拽开始事件@dragstart
        <!--
         在 dragstart 事件处理程序中,我们得到了用户拖动元素的引用。
         @dragstart="e => onWidgetDragStart(e, comp)"
        -->
        <!------------html部分------------->
        <div class="w-220px bg-blue-100 pl-40px">
            左侧小组件列表
            <div
                v-for="comp in widgetList"
                :key="comp.index"
                @dragstart="e => onWidgetDragStart(e, comp)">
                {{ comp.name }}
                <component :is="comp.comp" draggable="true" />
            </div>
        </div>
        
        <!------------js部分------------->
        // 当前拖拽的小组件 
        let currentDragWidget = {} as widget 
        
        // 当前拖拽小组件的xy坐标 
        let currentDragWidgetAxis = {} as {x:number, y:number} 
        
        /** 开始拖拽小组件 */ 
        function onWidgetDragStart(e: DragEvent, comp: widget) { 
            currentDragWidget = comp 
            currentDragWidgetAxis = {x: e.offsetX, y: e.offsetY} 
        }
3.3 画布区域设置「拖拽结束」@dragover及「放置」事件@drop
        <! --

            在目标容器的 dragover 事件处理程序中,我们调用 event.preventDefault(),这使它能够接收 drop 事件,否则drop时间不生效。

            @dragover="e => e.preventDefault()"

            在放置区域的 drop 事件处理程序中,我们将可拖动的元素从原始区域移动到可放置区域。

            @drop="onWidgetDrop"

        -->
        <!------------html部分------------->
        <div
            class="flex-1 bg-blue-200 flex flex-col"
            @dragover="e => e.preventDefault()"
            @drop="onWidgetDrop">
            中心画布区域
            <div
                v-for="item in currentCanvasComp"
                :key="item.index">
                {{ item.name }}
                <component :is="item.comp" />
            </div>
        </div>
        
        <!------------js部分------------->
        // 全部小组件集合
        const widgetList = ref<Array<widget>>([])

        // 当前画布中组件集合
        const currentCanvasComp = ref<Array<widgetDrag>>([])
        
        /** 放置拖拽的小组件 */
        function onWidgetDrop(e: DragEvent) {
            console.log('e=>', e)
            currentCanvasComp.value.push({
                ...currentDragWidget,
                x: e.offsetX-currentDragWidgetAxis.x,
                y: e.offsetY-currentDragWidgetAxis.y,
                active: false,
                w: 100,
                h: 100
            })
        }
3.4 画布内拖拽及8点放大缩小

使用vue3-draggable-resizable插件来支持画布内的拖动和缩放。

image.png

总结

至此,一个简易的拖拽器核心功能就实现了,主要依赖于html5支持的draggable属性及其事件;