首页 > 其他分享 >低开开发笔记(四):实现编辑器内拖拽

低开开发笔记(四):实现编辑器内拖拽

时间:2024-04-22 17:13:58浏览次数:26  
标签:鼠标 低开 phoffsetTopbox dsl mouseupY 编辑器 拖拽 phoffsetHeightbox

好家伙,

本篇我们来说说,编辑器内如何实现拖拽

完整代码已开源

https://github.com/Fattiger4399/ph-questionnaire.git

 

 0.效果预览

 

1.思路

1.1.视图操作分析

这一块是这一章节最核心的部分

 用户进行了什么操作?

(1)点击编辑器中第一个组件

(2)松开

(3)在setter中修改第一个组件的数据

(4)按下第一个组件(不松开鼠标左键)

(5)拖拽(不松开鼠标左键)

(6)到达目标地点(松开鼠标左键)

 

上述操作中,只有第六个是需要我们进行分析的

 但其实也非常简单,

图片中画红框的区域如何确定呢?

目标区域的位置 小于 第四个组件的开始坐标 加上 一半的第四个组件的高度

并且

目标区域的位置 大于  第四个组件的开始坐标 减去 一半的第三个组件的高度   

换成公式大概长这个样子

mouseupY > phoffsetTopbox[i] + 0.5 * phoffsetHeightbox[i] && mouseupY < phoffsetTopbox[i + 1] + 0.5 * phoffsetHeightbox[i + 1]

放到四个后面。。。放到第五个后面。。。

后面的情况以此类推

 

好了

 

1.2.数据操作分析

编辑器本身就是按数据的顺序渲染的,所以,只要排好序,然后更新渲染器就可以了

 

 

2.开始操作

我们需要用到的一些坐标 

事件对象e的一些属性

  • isTrusted: 表示事件是否是由用户操作触发的,如果是由脚本创建的事件,则为 false,如果是由用户操作触发的则为 true
  • altKeyctrlKeymetaKeyshiftKey: 分别表示是否按下了 Alt、Ctrl、Meta、Shift 键。
  • bubbles: 表示该事件是否会冒泡。
  • button: 表示按下的是哪个鼠标按钮(左键为 0,中键为 1,右键为 2)。
  • clientXclientY: 表示鼠标指针在视口中的坐标。
  • pageXpageY: 表示鼠标指针相对于页面的坐标。
  • screenXscreenY: 表示鼠标指针相对于屏幕的坐标。
  • target: 表示事件的目标元素。
  • type: 表示事件的类型,这里是 "dragend"。
  • timeStamp: 表示事件发生的时间戳。
  • xy: 与 clientXclientY 相同,表示鼠标指针在视口中的坐标。
  • offsetX: 表示鼠标指针位置相对于触发事件的对象的 X 坐标。换句话说,它是鼠标指针距离事件目标元素的左侧边缘的像素距离。
  • offsetY: 表示鼠标指针位置相对于触发事件的对象的 Y 坐标。它是鼠标指针距离事件目标元素的顶部边缘的像素距离。

 

3.代码分析

(以下仅分析关键代码,完整代码请参考)

select(config) {
            // 去除所有选中样式
            this.dsl = removeChildrenBorder(this.dsl);
            // 添加选中样式
            config.dsl = addChildrenBorder(config.dsl);
            this.model.selected = config.dsl;

            // 组件拖拽处理
            const editorElement = this.$refs.editor.$el;
            const node = `div.${this.model.selected.component}`;
            const allElements = editorElement.querySelectorAll('div');
            const childElements = editorElement.querySelectorAll(node);

            let sameid = this.model.selected.wid - 1;
            // 筛选出以 ph- 开头的 div 元素
            const phElements = Array.from(allElements).filter(div => {
                // 获取元素的类名数组
                const classList = div.classList;
                // 检查是否有任何类名以 'ph-' 开头
                return Array.from(classList).some(className => className.startsWith('ph-'));
            });
            let phoffsetTopbox = []
            let phoffsetHeightbox = []
            // 打印出所有匹配的元素
            phElements.forEach((item) => {
                phoffsetTopbox.push(item.offsetTop)
                phoffsetHeightbox.push(item.offsetHeight)
            })

            // 定义事件处理函数,并保存引用
            this.dragStartHandler = (e) => {
            };

            this.dragEndHandler = (e) => {
                // 注意:这里不需要移除事件监听器,因为它们会在select方法开始时被移除
                // console.log(e);
                // console.log("mouseupY轴   " + e.offsetY);
                //进行位置交换操作
                let newdsl;
                if (e.offsetY) {
                    const arraylength = this.dsl.children.length;
                    console.log(arraylength, phoffsetTopbox)
                    //将一号位组件拖拽到三号位上方
                    //拖拽大约为范围为150-250px
                    let i, j;
                    let overdragid;
                    let using_id = this.model.selected.wid;
                    //phoffsetTopbox
                    //phoffsetHeightbox
                    const mouseupY = phoffsetTopbox[using_id - 1] + e.offsetY;
                    const childlength = this.dsl.children.length
                    console.log(mouseupY)
                    //向下
                    if (e.offsetY > 0) {
                        for (i = using_id; i <= this.dsl.children.length;) {
                            // console.log(i)
                            // console.log(phoffsetTopbox[i], phoffsetHeightbox[i])
                            //向下
                            if (mouseupY > phoffsetTopbox[i] + 0.5 * phoffsetHeightbox[i] && mouseupY < phoffsetTopbox[i + 1] + 0.5 * phoffsetHeightbox[i + 1]) {
                                i++;
                                break;
                            } else if (mouseupY > 0 && mouseupY < phoffsetTopbox[i] + 0.5 * phoffsetHeightbox[i]) {
                                break;

                            } else if (mouseupY > phoffsetTopbox[childlength - 1] + 0.5 * phoffsetHeightbox[childlength - 1]) {
                                i = childlength;
                                break;
                            }
                            else {
                                i++;
                            }
                        }
                    } else {
                        //向上
                        for (i = using_id; i > 0;) {
                            // console.log(i)
                            console.log(mouseupY)
                            if (mouseupY > phoffsetTopbox[i - 2] - 0.5 * phoffsetHeightbox[i - 2] && mouseupY < phoffsetTopbox[i - 1] - 0.5 * phoffsetHeightbox[i - 1]) {
                                i = i - 2;
                                break;
                            }
                            //上半部分
                            else if (mouseupY < phoffsetTopbox[i] && mouseupY > phoffsetTopbox[i - 2] + 0.5 * phoffsetHeightbox[i - 2]) {
                                break;
                            } else if (mouseupY < phoffsetTopbox[0] + 0.5 * phoffsetHeightbox[0]) {
                                i = 0;
                                break;
                            } else {
                                i--;
                            }
                        }
                    }
                    console.log(this.dsl.children)
                    if (this.model.selected.wid != i) {
                        //复制
                        let copied_element = this.dsl.children[this.model.selected.wid - 1]
                        // //插入
                        this.dsl.children.splice(i, 0, copied_element)
                        //删除
                        this.dsl.children.splice(using_id - 1, 1)
                    }
                    console.log(i)
                }
            };
            // 在添加新的监听器之前,先移除旧的监听器
            if (childElements[sameid]) {
                childElements[sameid].removeEventListener('dragstart', this.dragStartHandler);
                childElements[sameid].removeEventListener('dragend', this.dragEndHandler);
            }
            // 添加新的监听器
            childElements[sameid].setAttribute("draggable", "true");
            childElements[sameid].addEventListener('dragstart', this.dragStartHandler);
            childElements[sameid].addEventListener('dragend', this.dragEndHandler);

            //更新视图
            this.$refs.editor.$forceUpdate();
        },

 

  1. 首先,根据传入的 config 对象,更新选中的样式,将选中的组件标记为 config.dsl

  2. 然后,通过获取编辑器元素和选中组件的类名,找到所有符合条件的子元素并保存在 childElements 中。

  3. 根据选中组件的 wid 属性计算出 sameid,用于定位对应的子元素。

  4. 通过筛选出以 ph- 开头的 div 元素,计算出这些元素的 offsetTop 和 offsetHeight,并保存在 phoffsetTopbox 和 phoffsetHeightbox 数组中。

  5. 定义了两个事件处理函数 dragStartHandler 和 dragEndHandler,分别处理拖拽开始和拖拽结束的逻辑。

  6. 在拖拽结束时,根据鼠标在 Y 轴上的位置,判断拖拽的方向(向上或向下),并根据计算得到的位置信息,将选中的组件插入到新的位置上。

  7. 移除旧的事件监听器,添加新的事件监听器,并设置元素为可拖拽状态。

 

标签:鼠标,低开,phoffsetTopbox,dsl,mouseupY,编辑器,拖拽,phoffsetHeightbox
From: https://www.cnblogs.com/FatTiger4399/p/18146367

相关文章

  • 拖拽式工作流有哪几个优势?
    在信息技术迅猛发展的今天,如何助力中小型企业在数字化转型的过程中平稳过渡?又是如何让中小型企业摆脱数据孤岛、成本投入高等各种瓶颈和难题?低代码技术平台是近些年较为理想的平台产品,其中拖拽式工作流优势特点突出,可以助力企业实现快速、高效、低成本的流程化办公。要了解拖拽式......
  • 3dmax材质编辑器崩溃怎么解决?3dmax渲染崩溃解决方法
    许多用户在更新版本后遭遇了一个共同的问题:一旦打开3dsMax的材质编辑器,程序就会崩溃。对此,众多用户感到困扰,不知道如何解决。下面我们以起来看看解决方法吧。3dmax材质编辑器崩溃解决方法1、登录到3dmax中,先开启渲染设置。2、选择【公用】【指定渲染器】,点击产品级,选择【V-R......
  • SpreadsheetControl组件修改,拖拽、新建、打开文件在独立浮窗打开
    一、修改文件拖拽功能,使其能够在另外一个独立窗体打开,需要配合documentManager控件实现。实现后效果:将11.xlsx文件拖拽到工作区 1.创建XExceluserControl用户窗体,代码如下:publicpartialclassXExcelUserControl:DevExpress.XtraEditors.XtraUserControl{publi......
  • 2-81. 创建交易窗口 UI 并实现拖拽交易打开交易窗口
    关闭窗口修改EventHandler修改InventoryUI修改NPCFunction打开窗口人物无法移动修改Enums修改EventHandler修改NPCFunction修改Player打开对话框人物还可以移动修改DialogController打开商店的时候同时打开背包调整背包和商店锚点位置修改Inven......
  • Canvas图形编辑器-数据结构与History(undo/redo)
    Canvas图形编辑器-数据结构与History(undo/redo)这是作为社区老给我推Canvas,于是我也学习Canvas做了个简历编辑器的后续内容,主要是介绍了对数据结构的设计以及History能力的实现。在线编辑:https://windrunnermax.github.io/CanvasEditor开源地址:https://github.com/Wind......
  • 基于Canvas实现的简历编辑器
    基于Canvas实现的简历编辑器大概一个月前,我发现社区老是给我推荐Canvas相关的内容,比如很多小游戏、流程图编辑器、图片编辑器等等各种各样的项目,不知道是不是因为我某一天点击了相关内容触发了推荐机制,还是因为现在Canvas比较火大家都在卷,本着我可以用不上但是不能不会的原则,我......
  • Markdown编辑器
    Markdown编辑器一、简介1.说明由于有些语法无法在博客园展示,推荐使用Typora解锁全套,下载地址:https://www.typora.io/推荐使用jupyter,使用方法:https://www.cnblogs.com/nickchen121/p/10722733.htmlmarkdown数学公式大全,https://www.cnblogs.com/nickchen121/p/11746655.html......
  • Linux-vim文本编辑器-三种模式-vim里的替换
    1.vi和vim命令是linux中强大的文本编辑器,由于Linux系统一切皆文件,而配置一个服务就是在修改其配置文件的参数。vim编辑器是运维工程师必须掌握的一个工具,没有它很多工作都无法完成。vim其实是vi的升级版 2.vim三种工作模式Vim编辑器中设置了三种模式:命令模......
  • 2款Notepad++平替工具(实用、跨平台的文本编辑器)
    前言今天大姚给大家分享2款Notepad++平替工具,实用、跨平台(支持Window/MacOS/Linux操作系统平台)的文本编辑器。NotepadNextNotepadNext是一个跨平台的Notepad++的重新实现。开发是使用QtCreator和MicrosftVisualC++(msvc)编译器完成的。目前支持Window/MacOS/Linux操作......
  • 界面控件DevExpress WinForms/WPF v23.2 - 富文本编辑器支持内容控件
    众所周知内容控件是交互式UI元素(文本字段、下拉列表、日期选择器),用于在屏幕上输入和管理信息。内容控件通常在模板/表单中使用,以标准化文档格式和简化数据输入。DevExpress文字处理产品库(WordProcessingDocumentAPI、WinForm和WPF富文本编辑器)附带了内容控制支持(v23.2+)。具......