从零实现一套低代码(保姆级教程) --- 【3】实现Button组件和画布区的拖拽

news2024/12/25 9:55:42

摘要

目前是每天更新一篇, 因为我不止要写文章,这些代码也是我正在敲的。可能速度没有那么快,但是这个频率感觉还是可以的。

本篇是这个系列的第三篇,如果你是第一次看到这个文章,那你应该会对低代码有那么一丢丢兴趣或者很有兴趣。从标题来看,也知道我这个系列就是实现一个低代码的项目。

那如果你想知道,我实现的项目的样子是什么样的,可以访问下面的链接:
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
XinBuilder 点击跳转

如果你有兴趣,跟着实现这么一套代码,那么你可以从第一章节开始阅读:
从零实现一套低代码(保姆级教程) — 【1】初始化项目,实现左侧组件列表

因为上一篇文章,没有额外的提交,所有的代码都写在了文章里,所以这里就直接继续上一篇开始说。

在上一篇结束后,我们实现了左侧组件拖拽到画布区的效果!!!

但是因为我们组件里面只有一个文本,所以今天我们主要来实现组件,以及画布区中组件的拖拽。
在这里插入图片描述

1.组件的实现

在上一节中,我们在Button组件中,从props里面接收了style,从而实现组件在画布区的定位效果。

但是之所以显示文本,是因为我们写的就是文本,现在我们给它改成antD里面的Button组件。

import { Button as AntButton } from 'antd'

export default function Button(props: any) {
  const {style} = props
  return (
    <div style={style}>
      <AntButton>按钮</AntButton>
    </div>
  )
}

这里注意一下,因为我的组件就叫Button,如果引入antD的Button时,没有改名会引起报错

你可以有两种解决办法,一种是像我这样,另一种就是修改自己这个组件的名,例如不叫Button,叫XButton即可

当你修改完后,再拖拽组件,那么在画布区显示的就不是一段文本了!!!!

在这里插入图片描述

2.实现画布区的拖拽

现在呢,我们拖拽到画布区的组件,就定死在那里了,不能拖拽,现在我们要给画布区的组件添加拖拽的效果。

现在请读者打开mainPart下的index.jsx文件,看一下最终的return。

我们在遍历comList的时候,返回的Com外层应该包一层div用来实现拖拽的效果。
在这里插入图片描述

同时也要给这个外层的div一个拖拽的方法,onDragStart,由于在画布区拖拽组件,也只能拖拽到画布区,所以我们不需要像上次一样,做一些禁止默认行为的处理了。

  // 画布区的组件拖拽方法
  const onDragStart = (com: ComJson) => {
    return () => {

    }
  }


  return (
    <div onDrop={onDrop} onDragOver={onDragOver} onDragEnter={onDragEnter} className='mainCom'>
      {
        comList.map(com => {
          const Com = components[com.comType as keyof typeof components];
          // 在外面包一层控制拖拽的div
          return <div draggable onDragStart={onDragStart(com)}>
            <Com style={com.style} />
          </div>
        })
      }
    </div>
  )

OK。现在请打开浏览器看一下实现的效果吧,是不是发现有点问题,每次你在画布区拖拽后,都会生成一个新的组件,而不是组件的移动。这是为什么呢?

在这里插入图片描述
答案在我们写的onDrop方法里,因为每次拖拽完都会触发onDrop方法,每次都会push一个新的组件

所以我们应该是从左侧组件列表拖拽的时候,进行push。如果是在画布区拖拽,我们只需要更新style属性即可。

那怎么判断是画布区拖拽的组件还是左侧栏拖拽的组件呢?我们可以在这里的onDragStart里,将window.nowCom设定为一个特殊的值。在onDrop根据这个值去判断走哪条分支。

  // 用于保存当前画布区拖拽的节点
  const [dragCom, setDragCom] = useState<ComJson | null>(null)

  const onDrop = (e: any) => {
    const endLeft = e.clientX;
    const endTop = e.clientY;
    const style = {
      position: 'absolute',
      left: endLeft + 'px',
      top: endTop + 'px',
      zIndex:100
    }
    // 判断当前拖拽的节点是从画布区拖拽,还是左侧组件栏拖拽
    if(window.nowCom === 'renderCom' && dragCom) {
      dragCom.style = style
    }else{
      comList.push({
        comType: window.nowCom,
        style
      })
    }
    setComList([...comList])
  }
  
  const onDragStart = (com: ComJson) => {
    return () => {
      // 设置拖拽的节点和nowCom的固定值
      window.nowCom = 'renderCom';
      setDragCom(com)
    }
  }

现在请再看一下效果,在画布区的拖拽就已经解决了。
但是呢,是不是位置不太准确呢

3.计算组件拖拽后的位置

好像在画布区拖拽完组件,新的位置总是差了那么一点,这是为什么呢?
先给一点提示,如果你拖拽的时候,鼠标位置在组件的左上角,那就没有问题了。

答案是,你给组件的left和top的定位,是基于鼠标位置的,也就是说,你把鼠标距离顶部和左部的距离,给了组件,那如果组件有自己的宽高,那么这个定位就是不准的。

所以为了算出正确的位置,我们应该计算出鼠标向左移动的距离和向上移动的距离。
然后加上组件本身的left和top值。

组件位置.left = 组件旧位置.left + 鼠标向左移动的距离
组件位置.top= 组件旧位置.top+ 鼠标向上移动的距离

那怎么计算鼠标移动的距离呢?我们可以在dragStart中,保存鼠标的位置。drop事件里,再保存一下鼠标的位置,诶?两者的差值不就是鼠标移动的位置嘛?
简单画一下流程图:

在这里插入图片描述

  // 用来保存鼠标的开始位置和结束位置
  const distance = useRef<Distance>({
    startLeft: void 0,
    startTop: void 0,
    endLeft: void 0,
    endTop: void 0
  })
  
  const onDragStart = (com: ComJson) => {
    return (e: any) => {
      window.nowCom = 'renderCom';
      setDragCom(com);
      // 开始位置
      distance.current.startLeft = e.clientX;
      distance.current.startTop = e.clientY;
    }
  }

    const onDrop = (e: any) => {
    // 鼠标的结束位置
    distance.current.endLeft = e.clientX;
    distance.current.endTop = e.clientY;
    let style: any;
    if(window.nowCom === 'renderCom' && dragCom && dragCom.style) {
      // 根据鼠标位置的差值计算组件位置
      dragCom.style = {
        ...dragCom.style,
        left: parseInt(dragCom.style.left) + (e.clientX - (distance.current.startLeft || 0)) + 'px',
        top: parseInt(dragCom.style.top) + (e.clientY - (distance.current.startTop || 0)) + 'px'
      }
    }else{
      style = {
        position: 'absolute',
        left: distance.current.endLeft + 'px',
        top: distance.current.endTop + 'px',
        zIndex:100
      }
      comList.push({
        comType: window.nowCom,
        style
      })
    }
    setComList([...comList])
  }

基于上面的实现,我们就完成了组件在画布区的拖拽了。

本章内容会提交在github上:
https://github.com/TeacherXin/XinBuilder2
commit: 第三节:实现Button组件和画布区的拖拽

博主补充

那如果现在你已经完成了所有的过程,你可以自己把其他组件的代码也补充一下。
我会在github上提交一段代码,用来补充文本框组件的代码,内容可以在github上查看

https://github.com/TeacherXin/XinBuilder2
commit: 第三节:实现Input组件代码

第二个问题,为什么鼠标的位置我们通过useRef来进行存储。因为这四个变量不需要更新去渲染组件,所以没必要通过useState去管理。但是呢,每次更新组件的时候还需要拿到之前的值,所以我们使用useRef进行管理。

第三个问题,现在我们有comList保存所有的组件数据,dragCom保存在画布区拖拽的组件。这个在后面,我们会采用redux进行管理,会有所更改,目前的话我们先使用这种模式来把整个流程串通。

最后,博主的TS可能没有那么熟练,如果有建议的话,博主也会积极采纳的!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1327887.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ros2 学习11-通信中动作的定义及自定义action 交互示例

机器人是一个复杂的智能系统&#xff0c;并不仅仅是键盘遥控运动、识别某个目标这么简单&#xff0c;我们需要实现的是送餐、送货、分拣等满足具体场景需求的机器人。 在这些应用功能的实现中&#xff0c;另外一种ROS通信机制也会被常常用到——那就是动作。从这个名字上就可以…

【krita】实时绘画 入门到精通 海报+电商+装修+人物

安装插件 首先打开comfyUI&#xff0c;再打开krita&#xff0c;出现问题提示&#xff0c; 打开 cd custom_nodes 输入命令 安装控件 git clone https://github.com/Acly/comfyui-tooling-nodes.git krita基础设置 设置模型 设置lora &#xff08;可设置lora强度 增加更多…

解锁高效工作!5款优秀工时管理软件推荐

工时管理&#xff0c;一直是让许多企业和团队头疼的问题。传统的纸质工时表、复杂的电子表格&#xff0c;不仅操作繁琐&#xff0c;还容易出错。幸好&#xff0c;随着科技的进步&#xff0c;我们迎来了工时管理软件的春天。今天&#xff0c;就让我们一起走进这个新时代&#xf…

蛮力法之背包问题

问题: 有 n 个重量分别是 w1,w2....,wn 的物品&#xff08;物品编号为 1-n&#xff09;它们的价值分别为 v1,v2,...,vn 给定一个容量为 W 的背包。设计从这些物品中选取一部分放入该背包的方案。 每个物品要么选中要么不选中【其实每个物品只有 1 件】&#xff0c;要求选中…

智慧互联网银行引领金融变革,开源网安VulHunter护航数字化发展

某银行作为国内知名的互联网银行&#xff0c;以构建“智慧型互联行”为总体战略目标&#xff0c;始终坚持科技赋能金融的理念。通过AI、大数据、云计算等数字技术与金融业务的探索融合&#xff0c;实现以更低的成本为客户提供便捷、高效和优质体验的互联网金融服务。 架构升级助…

基于ssm+vue的在线听书网站论文

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;书籍信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广大…

如何利用烛龙和谷歌插件优化CLS(累积布局偏移) | 京东云技术团队

简介 CLS 衡量的是页面的整个生命周期内发生的每次意外布局偏移的最大突发性_布局偏移分数_。布局变化的发生是因为浏览器倾向于异步加载页面元素。更重要的是&#xff0c;您的页面上可能存在一些初始尺寸未知的媒体元素。这种组合意味着浏览器在加载完成之前无法确定单个元素…

HarmonyOS快速入门之开发准备

开发准备 本文档适用于HarmonyOS应用开发的初学者。通过构建一个简单的具有页面跳转/返回功能的应用&#xff08;如下图所示&#xff09;&#xff0c;快速了解工程目录的主要文件&#xff0c;熟悉HarmonyOS应用开发流程。 在开始之前&#xff0c;您需要了解有关HarmonyOS应用的…

EOCR-i3M420/iFM420施耐德智能通讯保护继电器产品简介

EOCR-i3M420/iFM420是施耐德EOCR的新一代电子式电动机保护器产品&#xff0c;具有过电流、欠电流、缺相、逆相、堵转、失速、三相不平衡等保护功能&#xff0c;并具有4-20mA电流输出功能。EOCR-i3M420/iFM420是通讯型产品&#xff0c;提供Modbus RTU通讯协议&#xff0c;RS485接…

nodejs+vue+微信小程序+python+PHP兴趣趣班预约管理系统设计与实现-计算机毕业设计推荐

当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规范化管理。 软件信息技术能够覆盖社会各行业领域是时代的发展要求&…

Shiro的认证与授权过程的401自定义信息返回格式

spring boot与shiro整合的很多这个不用多赘述&#xff0c;主要分享下过滤器这块 shiro中的部分过滤器 anon过滤器&#xff1a;用于处理匿名访问&#xff0c;即不需要身份验证的URL。 authc过滤器&#xff1a;用于进行身份验证&#xff0c;如果身份验证失败&#xff0c;将会返…

ModuleNotFoundError: No module named ‘tensorflow‘

直接运行pip install tensorflow安装成功之后&#xff0c;发现版本是tensorflow2.15.0 python的版本是3.9版本 导入包&#xff1a;import tensorflow 打包xxx.exe,调用之后提示错误 ModuleNotFoundError: No module named tensorflow 最后发现特定的python的版本对应特定的t…

Qt 6.3 学习笔记

文章目录 Qt的安装和配置创建一个Qt项目信号和槽布局和控件绘图和动画数据库和网络 Qt是一个跨平台的C图形用户界面应用程序开发框架。它提供了创建GUI应用程序的工具和库。Qt 6.3是Qt的最新版本&#xff0c;引入了许多新特性和改进。在这个章节中&#xff0c;我们将详细介绍如…

DRF从入门到精通二(Request源码分析、DRF之序列化、反序列化、反序列化校验、序列化器常用字段及参数、source、定制字段、保存数据)

文章目录 一、Request对象源码分析区分原生request和新生request新的request还能像原来的reqeust一样使用吗源码片段分析总结&#xff1a; 二、DRF之序列化组件序列化介绍序列化步骤序列化组件的基本使用反序列化基本使用反序列化的新增反序列化的新增删除单条 反序列化的校验序…

109基于MATLAB 中的设施布局设计和位置分配

基于MATLAB 中的设施布局设计和位置分配&#xff0c;通过PSO算法进行最佳位置匹配。程序已调通&#xff0c;可直接运行。 109设施布局设计和位置分配通 (xiaohongshu.com)

2023光伏“洗牌”,鼎捷数智方案如何助力企业抓住时代契机?

2023年的光伏行业正进入重新“洗牌”的阶段&#xff01; 一方面&#xff0c;在“双碳政策”和全球能源危机的双重驱动下&#xff0c;我国光伏全产业链迎来高速增长&#xff0c;成为外贸出口“新三样”之一。光伏发电正加速取代传统化石能源&#xff0c;在电网中发挥着日益重要的…

vue3+Ts

安装 命令含义可参考typescript文章中的自动编译部分 npm create vitelatest vuets-project -- --template vue-tsvs code插件 Vue Language Features (Volar)对.vue文件进行实时的类型错误反馈TypeScript Vue Plugin (Volar) 用于支持在TS中import*.vue文件&#xff08;mai…

【2023年网络安全优秀创新成果大赛专刊】银行数据安全解决方案(天空卫士)

在2023年网络安全优秀创新成果大赛&#xff0c;成都分站中&#xff0c;天空卫士银行数据安全方案获得优秀解决方案奖。与此同时&#xff0c;天空卫士受信息安全杂志邀请&#xff0c;编写《银行数据安全解决方案》。12月6日&#xff0c;天空卫士编写的《银行数据安全解决方案》做…

Debezium发布历史20

原文地址&#xff1a; https://debezium.io/blog/2017/09/25/streaming-to-another-database/ 欢迎关注留言&#xff0c;我是收集整理小能手&#xff0c;工具翻译&#xff0c;仅供参考&#xff0c;笔芯笔芯. 将数据流式传输到下游数据库 九月 25, 2017 作者&#xff1a; Jiri…

期货交易策略模拟测试-基于CLBISO01策略-2023.12.22

采取与昨天同样的策略进行盘中模拟测试&#xff0c;今天行情还可以&#xff0c;挺“顺溜”。