React项目-figma-clone完-羊的日记(4.30)

165 阅读4分钟

4.28(figma-clone)

序言:看代码一直是学习前端技术的好方法,所有比较喜欢根据代码一句一句注释,并查看handle方法是如何实现的,各个数据 的类型是怎么定义的,然后看到各种没有见过css样式,积累并通过注释,撤销的方式查看它实现了什么效果。

这个项目是一个多用户同屏互动设置ui的单页面项目,主要使用liveblock这个显示服务器来保存数据并实现数据共享,并使用tailcss编写css样式,这个确实比普通编写css更快,但是需要不断积累,然后就是使用react和nextjs来实现代码框架,写完之后会觉得其实项目难度中等偏下,不过新鲜的技术是比较多的,主要还是canvas的操作和liveblocks的功能,下面是该项目的剩下1/2.

1.storeage

获取在liveblock中的存储数据,当你使用canvas在房间中添加dom元素,或者添加信息,这些信息则会保存在room中。你可以通过它获取数据


const canvasObjects = useStorage((root)=>root.canvasObjects)
//获取仓库数据
const syncShapeInStorage = useMutation(({storage},object)=>{
    if(!object) return
    const {objectId} = object
    const shapeData = object.toJSON()
    shapeData.objectId =objectId
    const canvasObjects = storage.get('canvasObjects')
    canvasObjects.set(objectId,shapeData)
​
  },[])
//异步获取图形数据

2.canvas

首先你需要创建画布和获取画布操作方式,然后需要根据监听事件来依次完成各种操作。


useEffect(() => {
    // console.log(fabricRef);
    // 初始化
    const canvas = initializeFabric({
      canvasRef,
      fabricRef,
    });
    // 画布绑定点击事件
    canvas.on("mouse:down", (options) => {
      handleCanvasMouseDown({
        options,
        canvas,
        selectedShapeRef,
        isDrawing,
        shapeRef,
      });
    });
    // 鼠标按住移动
    canvas.on("mouse:move", (options) => {
      handleCanvaseMouseMove({
        options,
        canvas,
        selectedShapeRef,
        isDrawing,
        shapeRef,
        syncShapeInStorage
      });
    });
    // 离开
    canvas.on("mouse:up", (options) => {
      handleCanvasMouseUp({
        canvas,
        selectedShapeRef,
        isDrawing,
        shapeRef,
        syncShapeInStorage,
        setActiveElement,
        activeObjectRef
      });
    });
    // 监听移动
    canvas.on('object:modified',(options)=>{
      handleCanvasObjectModified({
        options,
        syncShapeInStorage
      })
    })
    // 选择创建
    canvas.on('selection:created',(options:any)=>{
      handleCanvasSelectionCreated({
        options,
        isEditingRef,
        setElementAttributes,
      })
    })
    // 缩放改变数据
    canvas.on('object:scaling',(options)=>{
      handleCanvasObjectScaling({
        options,setElementAttributes
      })
    })
    // 监听自由绘画
    canvas.on('path:created',(options)=>{
      handlePathCreated({
        options,syncShapeInStorage
      })
    })
​
    // 监听窗口是否改变大小
    window.addEventListener("resize", () => {
      handleResize({
        canvas: fabricRef.current,
      });
    });
    window.addEventListener('keydown',(e:any)=>{
      handleKeyDown({
        e,
        canvas:fabricRef.current,
        undo,
        redo,
        syncShapeInStorage,
        deleteShapeFromStorage
      })
    })
    return ()=>{
      // 监听所有删除canvas元素和事件
      canvas.dispose()
    }
  }, [])

3.fabric

fabric是一个控制canvas的方法类库,类似three.js的封装,通过这个库可以对画布元素进行操作,多用来创建canvas元素,拖动,伸缩等。


// 获取图形元素
  const shapeRef = useRef<fabric.Object | null>(null);
//创建图形
shapeRef.current = createSpecificShape(
      selectedShapeRef.current,
      pointer as any
    );

4.liveblocks/react-comments

liveblocks/react-comments是一个多人协同聊天功能,你可以通过这个功能将你想说的话或者留言,保存在画布上。根据liveblocks文档来一般不会出错,你也可以对一些代码做自己的操作,比如固定位置,条件渲染等。

5.右键菜单

使用shaden/ui的Context Menu可以实现,其中包含四大部分,首先是ContextMenu作为承载整个页面的容器,其中含有两个子元素,ContextMenuTrigger用来包含你本来页面的所有元素,ContextMenuContent用来包含你的菜单元素,它的子元素ContextMenuItem用来包含各个菜单选项,也可以使用嵌套方式来实现。


<ContextMenu>
  <ContextMenuTrigger> 
    ...
    </ContextMenuTrigger>
  <ContextMenuContent>
    <ContextMenuItem>Profile</ContextMenuItem>
    <ContextMenuItem>Billing</ContextMenuItem>
    <ContextMenuItem>Team</ContextMenuItem>
    <ContextMenuItem>Subscription</ContextMenuItem>
  </ContextMenuContent>
</ContextMenu>

6.导航栏

左侧导航栏主要包含你创建的元素,每创建一个元素就会新建一行,


const canvasObjects = useStorage((root)=>root.canvasObjects)
//从仓库中去出所有图形元素
<LeftSidebar allShapes={Array.from(canvasObjects)}/>
//将所有图形的类数组转换为数组传递过去,然后再进行遍历

右侧导航栏,通过pages传递一些列修改元素的方法,让子元素调用,然后间接修改元素的大小、颜色,字体、字号等,并通过elementAttributes来动态渲染输入框中数据,保证页面的响应式。


<RightBar 
          elementAttributes={elementAttributes}//元素属性
          setElementAttributes={setElementAttributes}//修改元素属性方法
          fabricRef ={fabricRef}//操作canvas元素方法
          isEditingRef={isEditingRef}
          activeObjectRef={activeObjectRef}//当前选择元素
          syncShapeInStorage={syncShapeInStorage}//仓库
        />

现在总结一下该项目

技术栈:react,nextjs,shadcn/ui,tailwindcss,liveblocks,fabric,jspdf等

项目难点:

  1. 实现输入时候,其他用户同步显示
  2. 光标特性反射,以及当你在输入e时候跳转到特效选择框bug解决
  3. canvas绘制图形,和一系列监听事件
  4. 创建和修改元素在其他用户页面动态进行显示
  5. 通过comments实现留言,并保存
  6. 铅笔自由绘画和鼠标拖动动态绘制图形
  7. 通过输入框动态改变元素属性,并实现双向数据绑定。
  8. 展示历史记录,实现撤销,重置方法

最后还是希望前端能发展的更好,而不是被后端随便代替,当一个资深的前端,从初级前端的大军中脱颖而出。