自由绘制线条
图布自由手绘与SVG路径节点
功能概述
此示例演示了一个强大的创意功能:直接在图画布上绘制自由草图的能力,这些草图会自动转换为图节点。用户可以切换"自由绘图"模式,用鼠标绘制任意形状,系统会将这些绘图转换为 SVG 路径节点,成为图的一部分。这弥合了手动注释和结构化图数据之间的差距,实现了图表绘制、白板协作和创意草图等用例。
核心特性实现
自由手绘的自定义画布叠加层
SmoothCanvas 组件提供了一个用于捕获鼠标移动的叠加层:
<SmoothCanvas
lineColor='red'
lineWidth={3}
onUserPathCreate={onUserPathCreate}
onClose={() => { setFreelyDrawMode(false) }}
/>
此组件跟踪鼠标/触摸移动并生成代表用户绘图的 SVG 路径数据。
路径到节点转换算法
核心创新是将原始绘图坐标转换为图节点:
const onUserPathCreate = (path: string, color: string, width: number) => {
// 解析路径中的所有点以计算边界框
const coords = path.split(/[ML]/).filter(s => s.trim() !== "").map(pair => {
const [x, y] = pair.trim().split(/\s+/).map(Number);
return { x, y };
});
// 计算视口坐标系中的边界
let minX = coords[0].x, maxX = coords[0].x;
let minY = coords[0].y, maxY = coords[0].y;
coords.forEach(p => {
if (p.x < minX) minX = p.x;
if (p.x < maxX) maxX = p.x;
if (p.y < minY) minY = p.y;
if (p.y > maxY) maxY = p.y;
});
// 坐标转换:视口到画布坐标
const canvasPos = graphInstance.getCanvasXyByViewXy({ x: minX, y: minY });
const zoom = graphInstance.getOptions().canvasZoom / 100;
const nodeW = Math.max(viewWidth / zoom, 20);
const nodeH = Math.max(viewHeight / zoom, 20);
// 将路径数据归一化为相对于节点左上角的坐标
const relativePath = coords.map((p, i) => {
const relX = (p.x - minX) / zoom;
const relY = (p.y - minY) / zoom;
return `${i === 0 ? 'M' : 'L'}${relX.toFixed(1)} ${relY.toFixed(1)}`;
}).join(' ');
};
该算法:
- 解析 SVG 路径字符串以提取坐标点
- 计算绘图的边界框
- 使用当前缩放级别将视口坐标转换为画布坐标
- 将路径坐标归一化为相对于节点位置的坐标
创建 SVG 路径节点
转换后的绘图成为具有自定义 SVG 渲染的节点:
const newNode = {
id: newNodeId,
type: 'svg-path',
x: canvasPos.x,
y: canvasPos.y,
width: nodeW,
height: nodeH,
text: '',
color: 'transparent',
borderColor: 'transparent',
data: {
path: relativePath,
originalViewBox: `0 0 ${nodeW} ${nodeH}`,
strokeColor: color,
strokeWidth: width
}
};
graphInstance.addNodes([newNode]);
节点在其数据中存储归一化的 SVG 路径,允许稍后渲染。
SVG 路径的自定义节点插槽渲染
RGSlotOnNode 组件以不同方式有条件地渲染 SVG 路径:
<RGSlotOnNode>
{({ node }: RGNodeSlotProps) => {
if (node.type === 'svg-path') {
return (
<div style={{ width: '100%', height: '100%', overflow: 'visible', pointerEvents: 'none' }}>
<svg width="100%" height="100%" style={{ display: 'block' }}>
<path
d={node.data?.path}
fill="none"
stroke={node.data?.strokeColor || '#FF0000'}
strokeWidth={node.data?.strokeWidth || 4}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</div>
);
}
return <div className="rg-node-text"><span>{node.text}</span></div>;
}}
</RGSlotOnNode>
具有 type: 'svg-path' 的节点渲染为内联 SVG 图形,而常规节点渲染文本。
与编辑工具集成
绘图功能与图编辑功能无缝集成:
<RGSlotOnView>
<RGEditingNodeController>
<RGEditingResize />
<MyNodeToolbar onRemoveNode={onRemoveNode} />
</RGEditingNodeController>
<RGEditingConnectController />
</RGSlotOnView>
用户可以像常规节点一样选择、调整大小、移动和删除绘制的节点。
创意使用场景
协作白板和图表绘制
创建混合白板工具,用户可以在结构化图表元素旁边自由手绘注释。例如,在建筑或工程中,用户可以绘制自动捕获并与正式图表元素集成的粗略草图。
教育笔记记录
构建教育工具,学生可以用自由手绘图突出显示或注释概念图。研究食物网的生物学学生可以圈出一组生物或绘制箭头以指示超出正式链接的额外关系。
分镜脚本和视觉规划
用于分镜脚本应用程序,用户可以在其中快速草图场景构思或角色位置。自由手绘成为一流元素,可以像其他分镜脚本组件一样重新排列、连接和注释。
UX/UI 设计工作流程
实施 UX 设计师可以直接在交互图上草图用户流注释或可用性问题的设计工具。自由手绘图可以指向特定的 UI 元素,并被视为设计文档的一部分。
数据注释和标记
应用于数据分析师需要标记集群、异常值或感兴趣区域的数据分析工作流程。自由手绘圆圈或高亮可以将相关节点分组以进行演示或进一步分析。
带有草图的心智映射
创建允许在文本节点旁边自由手绘草图的心智映射工具。用户可以绘制代表概念的图标、符号或快速插图,使心智地图更具表现力和记忆性。
艺术和创意可视化
用于创建添加有机、人类元素到结构化数据的自由手绘图的艺术可视化。这可用于生成艺术或创建更具吸引力的数据演示。