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等
项目难点:
- 实现输入时候,其他用户同步显示
- 光标特性反射,以及当你在输入e时候跳转到特效选择框bug解决
- canvas绘制图形,和一系列监听事件
- 创建和修改元素在其他用户页面动态进行显示
- 通过comments实现留言,并保存
- 铅笔自由绘画和鼠标拖动动态绘制图形
- 通过输入框动态改变元素属性,并实现双向数据绑定。
- 展示历史记录,实现撤销,重置方法
最后还是希望前端能发展的更好,而不是被后端随便代替,当一个资深的前端,从初级前端的大军中脱颖而出。