组件:
- ReactFabric
- Rect
- Path
- BackgroundImage
- Group
- Text
pnpm add react-fabric2
- 受控模式
import { ReactFabric, Rect, Path, BackgroundImage, Objects } from 'react-fabric2'
<ReactFabric defaultCentered defaultSelection={false}>
<BackgroundImage scaleToFit src={imgBaseURL + currentSrc} />
<Rect width={100} height={100} left={100} top={100} />
<Path path="M 0 0 L 100 100" />
<Objects objects={[]} />
</ReactFabric>
组件:
-
ReactFabric
-
defaultSelection:修改fabric默认值; 接受 Fabric Canvas 类的所有 props, 但建议使用非受控模式,目前仅支持一个非受控属性
defaultSelection={false}
,受控用法暂未测试 -
defaultCentered: 背景图是否居中显示
-
BackgroundImage scaleToFit 缩放背景图以适应屏幕 scaleToCover 缩放背景图以适应屏幕,另一张模式 其他 FabricImage 的 props
-
Rect 矩形
- 接受 Fabric Rect 类的所有 props, 提供工具函数
getRectProps
生成矩形属性
- 接受 Fabric Rect 类的所有 props, 提供工具函数
-
Path 路径
- 接受 Fabric Path 类的所有 props
-
Group 组
- 接受 Fabric Group 类的所有 props, 用于整体拖动场景
-
Text 文本
- 接受 Fabric Text 类的所有 props
-
Objects objects 数组类型,用于从数据动态生成所有图形, 需要 type 字段
-
Control
-
dom 控件,用于实现tooltip效果
- closeOnOutsideClick 是否在点击外部时关闭,默认true
- Content 内容
- placement 位置 参考 floating-ui 文档
- open 是否打开
- onOpenChange 打开状态改变时触发 支持 Control 嵌套,理论上无限嵌套
事件:
- 所有组件均支持所有内部事件,规则为前面加on, 小驼峰, 比如 canvas
mouse:down
->onMouseDown
, 有类型提示
ReactFabricProvider
+ useReactFabric
const Toolbar = () => {
const { setMinManualZoom, setMaxManualZoom, reset, setDefaultSelection, canvas } = useReactFabric()
return <div>Toolbar</div>
}
const Main = () => {
return (
<ReactFabricProvider>
<ReactFabric>
<BackgroundImage scaleToFit src={imgBaseURL + currentSrc} />
</ReactFabric>
<Toolbar />
</ReactFabricProvider>
)
}
- 受控模式 支持所有属性和事件, 因此天然支持了受控模式
const [rect, setRect] = useState({
points: '100,100,200,200,300,300,400,400',
})
<ReactFabric>
<Rect
left={getRectProps(rect?.points, 5)?.left}
top={getRectProps(rect?.points, 5)?.top}
width={getRectProps(rect?.points, 5)?.width}
height={getRectProps(rect?.points, 5)?.height}
onModified={({ target }) => {
const rect = target as FabricRect
setRect({
points: rect.getCoords().reduce<number[]>((acc, { x, y }) => [...acc, Math.round(x), Math.round(y)], [])
.join(','),
})
}}
/>
<Control
className="z-10"
closeOnOutsideClick={false}
open={open}
onOpenChange={(nextOpen)=>setOpen(nextOpen)}
Content={
<button>删除</button>
}
placement="bottom"
>
</ReactFabric>
- 非受控模式
图形在 Group 中渲染时,由于 Group 矩阵会导致内部图形身上的坐标系发生变化, 因此受控模式无法很好的支持到各种矩阵计算 场景: 结合 Group 使用, 注意事项 在Group身上使用onModified等方式修改坐标, 而不是修改内部基础图形
<ReactFabric>
<Group key={groupKey} onModified={({ target }) => {
const group = target as FabricGroup
const objects = group.getObjects()
const nextShapes = objects.map(object=> object
.getCoords()
.reduce<number[]>((acc, { x, y }) => [...acc, Math.round(x), Math.round(y)], [])
.join(','))
}}>
<Rect
defaultLeft={getRectProps(rect?.points, 5)?.left}
defaultTop={getRectProps(rect?.points, 5)?.top}
defaultWidth={getRectProps(rect?.points, 5)?.width}
defaultHeight={getRectProps(rect?.points, 5)?.height}
/>
</Group>
</ReactFabric>
// 受控模式在 通过 api 更新画布
const { canvas } = useReactFabric()
// 向上-向下+偏移
const onTranslateY = (px: number) => {
if (!canvas) {
console.warn('canvas is null')
return
}
const group = canvas.item(0) as FabricGroup
group.top = group.top + px
group.setCoords()
canvas.requestRenderAll()
// 不会触发onModified,因此需要手动set
const objects = group.getObjects()
// 不会触发onModified,因此需要手动set
const nextShapes = formList[sideType - 1]?.shapes?.map(shape => {
const fabricObject = objects.find((obj: any) => obj.id === shape.id)
if (!fabricObject) return shape
// Convert fabric object coordinates to points string
const points = fabricObject
.getCoords()
.reduce<number[]>((acc, { x, y }) => [...acc, Math.round(x), Math.round(y)], [])
.join(',')
return {
...shape,
points,
}
})
setPageStore({
formList: produce(formList, draft => {
set(draft, [sideType - 1, 'shapes'], nextShapes)
}),
})
}
- getRectProps 根据points 生成矩形属性
const result = {
left: Number(x1),
top: Number(y1),
width: Math.abs(x2 - x1) - strokeWidth,
height: Math.abs(y3 - y1) - strokeWidth,
strokeWidth,
}
绘制矩形
支持rect的所有props
<PluginFreeRect
strokeWidth={5}
onComplete={async (nextRect, { canvas }) => {
let pointsArray = nextRect.pointsArray
console.log(pointsArray)
}}
/>
网格线
<PluginGrid></PluginGrid>