LogicFlow中箭头的绘制,是通过SVG中的maker元素实现的,logicflow中的直线,折线,曲线目前都支持在起点和终点设置箭头。
- 关于marker:developer.mozilla.org/zh-CN/docs/…
- 关于logicflow:github.com/didi/LogicF…
- logicflow官网:logic-flow.org/
源码分析
Logicflow的底层架构是MVVM模式,箭头的绘制涉及到数据层Model和视图层View两部分。
Model
在baseEdgeModel里定义了arrowConfig属性,来存储起点终点箭头marker的Id。这个id需要跟view中定义marker的id一样,才能渲染出来。同时为了保证能够在流程图中多条连线markerId仍然能够标识正确,使用节点id作为其中一部分以保证唯一性。
LineEdgeModel(直线)、PolylineEdgeModel(折线)、BezierEdgeModel(曲线)继承自baseEdgeModel,因此其model中也是arrowConfig也标识箭头属性数据,arrowConfig有两个属性。
- markerEnd:连线终点箭头的markerId,默认是有值的,也就是连线默认展示终点箭头。
- markerStart:连线起点箭头的markerId,默认为空,也就是连线默认不展示起点箭头。如果想要展示起点箭头,需要在自定义连线时设置markerStart为
url(#marker-start-${this.id})
markerEnd,markerStart的取值需要跟View中的getArrow实现的markerId对应起来,上面取得值是默认定义的值,如果复写getArrow方法并修改其中marker的id规则,需要在model中一并修改赋值。
@observable arrowConfig = {
markerEnd: `url(#marker-end-${this.id})`,
markerStart: '',
}; // 箭头属性
View
在箭头的View实现中主要涉及到这几个方法:
- getArrow:获取箭头
- getArrowStyle:获取箭头样式
- getStartArrow:获取起点箭头
- getEndArrow:获取终点箭头
其中在连线视图中调用的是getArrow方法来绘制,getArrow调用getArrowStyle,getStartArrow,getEndArrow方法来进行细节实现,下面我们来分析下源码实现。
getArrow
getArrow中为每个连线定义了两个maker来实现起终点的箭头,从源代码中可以看到,两个maker的id与model中的id是一致的,如何让连线和这些maker对应上的呢?在LineEdge、PolylineEdge、BezierEdge中图形绘制中将model中的markerStart,markerEnd属性赋值到了图形绘制中,参见下边直线的绘制,这个实现是借助了svg标签支持的,详见marker。
同时也看到起点箭头已经绘制好了,如果想要展示出来,只要在model中进行赋值即可。
可以看到下面还有有refY refX 两个值,这两个值是用来标识箭头相对于起点和终点的偏移量,当你需要箭头不要紧紧贴着节点的时候可以使用这两个值进行调节。
getArrow(): h.JSX.Element | null {
const { model: { id } } = this.props;
const { refY = 0, refX = 2 } = this.getArrowStyle();
return (
<g>
<defs>
<marker id={`marker-start-${id}`} refX={-refX} refY={refY} overflow="visible" orient="auto" markerUnits="userSpaceOnUse">
{this.getStartArrow()}
</marker>
<marker id={`marker-end-${id}`} refX={refX} refY={refY} overflow="visible" orient="auto" markerUnits="userSpaceOnUse">
{this.getEndArrow()}
</marker>
</defs>
</g>
);
}
直线绘制箭头
<Line
{
...style
}
x1={startPoint.x}
y1={startPoint.y}
x2={endPoint.x}
y2={endPoint.y}
{...arrowConfig} // 箭头属性 markerEnd markerStart
{
...isAnimation ? {
strokeDasharray,
stroke,
style: {
strokeDashoffset,
animationName,
animationDuration,
animationIterationCount,
animationTimingFunction,
animationDirection,
},
} : {}
}
/>
getEndArrow
getEndArrow返回的是一个path,也就是箭头的路径,其中path的属性是从getArrowStyle中获得加以计算得到的。
分析箭头路径,d={M 0 0 L ${offset} -${verticalLength} L ${offset} ${verticalLength} Z
},从位置0,0出发到${offset} -${verticalLength}
然后到 ${offset} ${verticalLength}
闭合。这就是一个三角形,默认的offset和verticalLength分别是10, 5。如果想要更大的或者细长的箭头,可以通过主题设置修改这两个值实现。
如下面坐标的图所示,这个图形可以直接用于起点箭头(也就是这样实现的),终点的话需要翻转180度。箭头样式属性中还有stroke、fill、strokeWidth、transform来修饰。这些属性由getArrowStyle返回,下面会介绍这个函数返回值的来源。
如果你想要重新定义终点箭头,可以通过复写getEndArrow方法来实现,后面也会有如何定义的示例demo。
代码:
getEndArrow(): h.JSX.Element | null {
const { stroke, strokeWidth, offset, verticalLength } = this.getArrowStyle();
return <path stroke={stroke} fill={stroke} strokeWidth={strokeWidth} transform="rotate(180)" d={`M 0 0 L ${offset} -${verticalLength} L ${offset} ${verticalLength} Z`} />;
}
path绘制示意
getStartArrow
与终点箭头基本一致。
getArrowStyle
getArrowStyle方法通过整合model 主题等多个方面的数据,给出最终箭头的样式。如果你想要修改下箭头的颜色大小也可以通过复写这个方法实现。
源码:
getArrowStyle() {
const { model, graphModel } = this.props;
const edgeStyle = model.getEdgeStyle();
const edgeAnimationStyle = model.getEdgeAnimationStyle();
const { arrow } = graphModel.theme;
const stroke = model.isAnimation ? edgeAnimationStyle.stroke : edgeStyle.stroke;
return {
...edgeStyle,
fill: stroke,
stroke,
...arrow,
} as ArrowStyle;
}
复写:
class View extends PolylineEdge {
getArrowStyle() {
const style = super.getArrowStyle(); // 获取原来的返回值
return {
...style,
fill: 'green', // 只覆盖自己想要修改的,其他的保持不变,不用关心原来都有啥
stroke: 'green',
}
}
}
自定义箭头
上面介绍了箭头实现的源码,如果想要自定义箭头,可以通过主题设置,自定义连线复写等方式,自定义箭头demo如下图所示。
demo线上地址:xinxin93.github.io/logicflow_a…
demo解读文章:juejin.cn/post/716391…
demo代码地址:github.com/xinxin93/lo…