经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
低开开发笔记(四):实现编辑器内拖拽
来源:cnblogs  作者:养肥胖虎  时间:2024/4/23 10:06:35  对本文有异议

好家伙,

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

完整代码已开源

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

 

 0.效果预览

 

1.思路

1.1.视图操作分析

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

 用户进行了什么操作?

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

(2)松开

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

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

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

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

 

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

 但其实也非常简单,

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

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

并且

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

换成公式大概长这个样子

  1. 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.代码分析

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

  1. select(config) {
  2. // 去除所有选中样式
  3. this.dsl = removeChildrenBorder(this.dsl);
  4. // 添加选中样式
  5. config.dsl = addChildrenBorder(config.dsl);
  6. this.model.selected = config.dsl;
  7. // 组件拖拽处理
  8. const editorElement = this.$refs.editor.$el;
  9. const node = `div.${this.model.selected.component}`;
  10. const allElements = editorElement.querySelectorAll('div');
  11. const childElements = editorElement.querySelectorAll(node);
  12. let sameid = this.model.selected.wid - 1;
  13. // 筛选出以 ph- 开头的 div 元素
  14. const phElements = Array.from(allElements).filter(div => {
  15. // 获取元素的类名数组
  16. const classList = div.classList;
  17. // 检查是否有任何类名以 'ph-' 开头
  18. return Array.from(classList).some(className => className.startsWith('ph-'));
  19. });
  20. let phoffsetTopbox = []
  21. let phoffsetHeightbox = []
  22. // 打印出所有匹配的元素
  23. phElements.forEach((item) => {
  24. phoffsetTopbox.push(item.offsetTop)
  25. phoffsetHeightbox.push(item.offsetHeight)
  26. })
  27. // 定义事件处理函数,并保存引用
  28. this.dragStartHandler = (e) => {
  29. };
  30. this.dragEndHandler = (e) => {
  31. // 注意:这里不需要移除事件监听器,因为它们会在select方法开始时被移除
  32. // console.log(e);
  33. // console.log("mouseupY轴 " + e.offsetY);
  34. //进行位置交换操作
  35. let newdsl;
  36. if (e.offsetY) {
  37. const arraylength = this.dsl.children.length;
  38. console.log(arraylength, phoffsetTopbox)
  39. //将一号位组件拖拽到三号位上方
  40. //拖拽大约为范围为150-250px
  41. let i, j;
  42. let overdragid;
  43. let using_id = this.model.selected.wid;
  44. //phoffsetTopbox
  45. //phoffsetHeightbox
  46. const mouseupY = phoffsetTopbox[using_id - 1] + e.offsetY;
  47. const childlength = this.dsl.children.length
  48. console.log(mouseupY)
  49. //向下
  50. if (e.offsetY > 0) {
  51. for (i = using_id; i <= this.dsl.children.length;) {
  52. // console.log(i)
  53. // console.log(phoffsetTopbox[i], phoffsetHeightbox[i])
  54. //向下
  55. if (mouseupY > phoffsetTopbox[i] + 0.5 * phoffsetHeightbox[i] && mouseupY < phoffsetTopbox[i + 1] + 0.5 * phoffsetHeightbox[i + 1]) {
  56. i++;
  57. break;
  58. } else if (mouseupY > 0 && mouseupY < phoffsetTopbox[i] + 0.5 * phoffsetHeightbox[i]) {
  59. break;
  60. } else if (mouseupY > phoffsetTopbox[childlength - 1] + 0.5 * phoffsetHeightbox[childlength - 1]) {
  61. i = childlength;
  62. break;
  63. }
  64. else {
  65. i++;
  66. }
  67. }
  68. } else {
  69. //向上
  70. for (i = using_id; i > 0;) {
  71. // console.log(i)
  72. console.log(mouseupY)
  73. if (mouseupY > phoffsetTopbox[i - 2] - 0.5 * phoffsetHeightbox[i - 2] && mouseupY < phoffsetTopbox[i - 1] - 0.5 * phoffsetHeightbox[i - 1]) {
  74. i = i - 2;
  75. break;
  76. }
  77. //上半部分
  78. else if (mouseupY < phoffsetTopbox[i] && mouseupY > phoffsetTopbox[i - 2] + 0.5 * phoffsetHeightbox[i - 2]) {
  79. break;
  80. } else if (mouseupY < phoffsetTopbox[0] + 0.5 * phoffsetHeightbox[0]) {
  81. i = 0;
  82. break;
  83. } else {
  84. i--;
  85. }
  86. }
  87. }
  88. console.log(this.dsl.children)
  89. if (this.model.selected.wid != i) {
  90. //复制
  91. let copied_element = this.dsl.children[this.model.selected.wid - 1]
  92. // //插入
  93. this.dsl.children.splice(i, 0, copied_element)
  94. //删除
  95. this.dsl.children.splice(using_id - 1, 1)
  96. }
  97. console.log(i)
  98. }
  99. };
  100. // 在添加新的监听器之前,先移除旧的监听器
  101. if (childElements[sameid]) {
  102. childElements[sameid].removeEventListener('dragstart', this.dragStartHandler);
  103. childElements[sameid].removeEventListener('dragend', this.dragEndHandler);
  104. }
  105. // 添加新的监听器
  106. childElements[sameid].setAttribute("draggable", "true");
  107. childElements[sameid].addEventListener('dragstart', this.dragStartHandler);
  108. childElements[sameid].addEventListener('dragend', this.dragEndHandler);
  109. //更新视图
  110. this.$refs.editor.$forceUpdate();
  111. },

 

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

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

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

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

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

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

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

 

原文链接:https://www.cnblogs.com/FatTiger4399/p/18146367

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号