从零实现一套低代码(保姆级教程) --- 【2】实现画布区并展示组件

2023-12-21 20:46:19

摘要

这一篇是这个系列里的第二篇文章,如果没有看过第一章节,请移步到第一章节。
从零实现一套低代码(保姆级教程) — 【1】初始化项目,实现左侧组件列表

毕竟要看一下实现出来什么东西,再决定是不是要学习呢。
那整体来说,本系列主要就是实现出一套低代码的项目,而如果你已经看完了第一节。希望在后面的章节里,你依旧能一边参考线上例子,一边自己实现。

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
XinBuilder 点击跳转

在上一节课里,我们实现了左侧组件列表的展开。注意一点,在文章后面,我做了一点样式的优化,在github的提交里可以看到。
目前的效果是这样:

在这里插入图片描述
而这一篇文章,主要是为了实现画布区(当然是简单先实现出来效果),并且能够往里面拖入组件。

如果你对上一节内容已经完成了的话,那么就开始吧。
在这里插入图片描述

1.组件该如何渲染

首先我们回顾一下第一节的内容,我们左侧组件列表里的组件是哪里来的?
是在components文件夹下的index.js中,通过export导出的。

那我们导出的是什么,就是组件本身。所以在renderComponent方法中,我们可以拿到组件的类型,然后在画布区通过<Com / >的方式展现。

在这里插入图片描述
OK,如果这里没有问题的,每次我拖拽组件的时候,我要保留当前拖拽组件的name。
现在我们补充renderComponent方法:

  const renderComponent = () => {
    return <div className="componetGroup">
      {
        Object.keys(components).map(name => {
          const Icon = componentIconMap[name]
          const text = componentTextMap[name]
          return <div key={name} className='componentItem'>
          	// 新加的onDragStart方法,传入组件方法和组件类型
            <div onDragStart={onDragStart(name)} draggable style={{display: 'inline-block'}}><Icon style={{marginRight:'10px'}} /><span>{text}</span></div>
          </div>
        })
      }
    </div>
  }

那在onDragStart方法里我们应该做什么呢,要把这个数据传递给画布区。然后在画布区里插入Com就行了,是吧。

这里我们先不考虑怎么传递,直接先挂载window上。后面会有其他的方法,先实现逻辑就行。

  const onDragStart = (name: string) => {
    return () => {
      window.nowCom = name
    }
  }

经过这部分修改,我们每次拖拽组件的时候,在window上就会挂载一个组件类型。

读者也可自己打个log来看一下,是不是正确的数据。

2.实现画布区的可拖拽

现在我们拖拽左侧组件,不管拖拽到哪里,鼠标上肯定都有一个禁用符号。
那是因为浏览器的默认行为是,所有的组件都不允许防止拖拽元素。

为了解决这个问题,我们可以禁止onDragEnter和onDragOver的默认行为,同时给画布区一个onDrop事件。

onDrop事件,是当我把组件,拖入到画布区后,触发的事件,所以画布区的渲染逻辑。我们要写在这个方法里。

import './index.css'

export default function MainCom() {

  const onDrop = () => {
    
  }

  const onDragEnter = (e: any) => {
    e.preventDefault()
  }

  const onDragOver = (e: any) => {
    e.preventDefault()
  }


  return (
    <div onDrop={onDrop} onDragOver={onDragOver} onDragEnter={onDragEnter} className='mainCom'>

    </div>
  )
}

我之前说过,对于画布区,我的设计思路是,整个屏幕都是画布区。只不过左侧栏,右侧栏和顶部栏会遮挡画布区,不过我可以通过折叠他们来扩大画布区。

所以对于画布区来说,我设置的样式是整个屏幕,当然如果你不希望这么做,也可以自己调整对应的样式。

.mainCom {
  width: 100%;
  height: 100vh;
}

OK。实现完上面的步骤,我们就可以实现画布区的渲染了。

3. 实现画布区添加组件

触发Drop事件的时候,window上已经有一个nowCom了,我们就可以通过这个组件类型,获取到对应的组件。

但是我们会拖入很多组件,所以我们先用一个List(后面会采用redux来进行存储),来存储所有的组件类型。

  const [comList, setComList] = useState<string []>([])

  const onDrop = () => {
    comList.push(window.nowCom)
    setComList([...comList])
  }

这样每次拖拽到画布区后,comList中就会存储一个组件类型。
在render函数中,我们再遍历comList,返回对应的组件。

  return (
    <div onDrop={onDrop} onDragOver={onDragOver} onDragEnter={onDragEnter} className='mainCom'>
      {
        comList.map(name => {
          const Com = components[name as keyof typeof components];
          return <Com />
        })
      }
    </div>
  )

OK。现在我们就可以看一下页面效果,是不是发现拖拽完之后并没有效果。。。。
那是因为被顶部栏和左侧栏挡住了,因为我们没有设置组件的位置,默认就会在左上角。

但是看页面结构,你会发现组件已经添加到画布区了。

在这里插入图片描述

4.实现组件的位置定位

目前我们已经把组件添加到画布区了,但是我们肯定希望的是,我鼠标在哪,我拖拽的组件的位置就在哪里。

那我们实现一下这部分功能,那我们可以通过e.clientX和e.clientY来确定鼠标的位置。最终怎么映射到组件上呢?组件需要接受一个style属性。

所以,我们的comList里,就不能单纯的是一个stirng类型的name了。我们还需要样式,所以我们修改为成一个对象类型的结构。

interface ComJson {
  comType: string,
  style?: any
}

// 修改存储类型
 const [comList, setComList] = useState<ComJson []>([])

 const onDrop = () => {
   comList.push({
     comType: window.nowCom
   })
   setComList([...comList])
 }

  return (
    <div onDrop={onDrop} onDragOver={onDragOver} onDragEnter={onDragEnter} className='mainCom'>
      {
        comList.map(com => {
          const Com = components[com.comType as keyof typeof components];
          return <Com />
        })
      }
    </div>
  )

现在我们再onDrop中,给组件加上style。

  const onDrop = (e: any) => {
  // 新加的代码。给对应的组件加上style
    const endLeft = e.clientX;
    const endTop = e.clientY;
    const style = {
      position: 'absolute',
      left: endLeft + 'px',
      top: endTop + 'px',
      zIndex:100
    }
    comList.push({
      comType: window.nowCom,
      style
    })
    setComList([...comList])
  }

再把这个style再传递给组件:

  return (
    <div onDrop={onDrop} onDragOver={onDragOver} onDragEnter={onDragEnter} className='mainCom'>
      {
        comList.map(com => {
          const Com = components[com.comType as keyof typeof components];
          // 新加的代码,将style传递给组件
          return <Com style={com.style} />
        })
      }
    </div>
  )

OK,现在我们离成功只有一步了,这个style传给哪个组件了?
就是componets文件夹下的组件,现在我们给button组件的props里,接收一个style!!!!

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

现在再来看一下效果吧!
在这里插入图片描述
因为我们的Button里面只有一个文本,所以展示的也是文本。

OK,到现在画布区的渲染也算是简单实现了。

博主补充

可能有细心的读者会发现,最后循环渲染的组件,没有key。是因为在后面的章节,我们要给组件生成一个唯一的ID,然后当做key。这里就先空着了。

对于其他的组件,如果在你的代码里有,可以把style补充进去。当然这里博主给一个思路,既然style可以传过去,那其他属性是不是也可以传。如果你的button组件,就是antD的button组件,那你可以传的属性是不是很多呢?
当然这些内容后面都会实现的。

本章内容会提交在github上:
https://github.com/TeacherXin/XinBuilder2
commit: 第二节:实现画布区并展示组件

文章来源:https://blog.csdn.net/weixin_46726346/article/details/135132760
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。