Bpmn.js 中文文檔(二)
前言:
由于工作需要(其實不是很需要),在公司項目的基礎上開源了一個基于 bpmn-js + Vue 2.x + ElementUI 的一個流程編輯器 Bpmn Process Designer, 預覽位址 MiyueFE blog, 歡迎 fork 和 star。
四. Modules
7. Modeling 基本模組化方法
Diagram.js
提供的基礎模組化工廠
BaseModeling
,注入了
EventBus, ElementFactory, CommandStack
子產品。
Bpmn.js
繼承了
BaseModeling
并提供了新的方法。
該子產品在自定義節點屬性等方面經常使用
使用方式
Modeling
初始化時會向
CommandStack
指令堆棧中注冊對應的處理程式,以確定操作可恢複和取消。
Modeling
提供的方法主要是根據
handlers
來定義的,每個方法會觸發對應的事件
// BaseModeling (diagram.js)
BaseModeling.prototype.getHandlers = function () {
var BaseModelingHandlers = {
'shape.append': AppendShapeHandler, // 形狀可逆添加到源形狀的處理程式
'shape.create': CreateShapeHandler, // 形狀可逆建立、添加到流程中的處理程式
'shape.delete': DeleteShapeHandler, // 形狀可逆移除的處理程式
'shape.move': MoveShapeHandler, // 形狀可逆移動的處理程式
'shape.resize': ResizeShapeHandler, // 形狀可逆變換大小的處理程式
'shape.replace': ReplaceShapeHandler, // 通過添加新形狀并删除舊形狀來替換形狀。 如果可能,将保持傳入和傳出連接配接
'shape.toggleCollapse': ToggleShapeCollapseHandler, // 切換元素的折疊狀态及其所有子元素的可見性
'spaceTool': SpaceToolHandler, // 通過移動和調整形狀、大小、連線錨點(巡航點)來添加或者删除空間
'label.create': CreateLabelHandler, // 建立标簽并附加到特定的模型元素上
'connection.create': CreateConnectionHandler, // 建立連線,并顯示到畫布上
'connection.delete': DeleteConnectionHandler, // 移除連線
'connection.move': MoveConnectionHandler, // 實作連接配接的可逆移動的處理程式。 該處理程式與布局連接配接處理程式的不同之處在于它保留了連接配接布局
'connection.layout': LayoutConnectionHandler, // 實作形狀的可逆移動的處理程式
'connection.updateWaypoints': UpdateWaypointsHandler, // 更新錨點(巡航點)
'connection.reconnect': ReconnectConnectionHandler, // 重建立立連接配接關系
'elements.create': CreateElementsHandler, // 元素可逆建立的處理程式
'elements.move': MoveElementsHandler, // 元素可逆移動的處理程式
'elements.delete': DeleteElementsHandler, // 元素可逆移除的處理程式
'elements.distribute': DistributeElementsHandler, // 均勻配置設定元素布局的處理程式
'elements.align': AlignElementsHandler, // 以某種方式對齊元素
'element.updateAttachment': UpdateAttachmentHandler // 實作形狀的可逆附着/分離的處理程式。
}
return BaseModelingHandlers;
}
// Modeling (bpmn.js)
var ModelingHandlers = BaseModeling.prototype.getHandlers.call(this);
ModelingHandlers['element.updateModdleProperties'] = UpdateModdlePropertiesHandler; // 實作元素上的擴充屬性的可逆修改
ModelingHandlers['element.updateProperties'] = UpdatePropertiesHandler; // 實作元素上的屬性的可逆修改
ModelingHandlers['canvas.updateRoot'] = UpdateCanvasRootHandler; // 可逆更新畫布挂載節點
ModelingHandlers['lane.add'] = AddLaneHandler; // 可逆通道添加
ModelingHandlers['lane.resize'] = ResizeLaneHandler; // 通道可逆resize
ModelingHandlers['lane.split'] = SplitLaneHandler; // 通道可逆分隔
ModelingHandlers['lane.updateRefs'] = UpdateFlowNodeRefsHandler; // 可逆更新通道引用
ModelingHandlers['id.updateClaim'] = IdClaimHandler;
ModelingHandlers['element.setColor'] = SetColorHandler; // 可逆更新元素顔色
ModelingHandlers['element.updateLabel'] = UpdateLabelHandler; // 可逆更新元素label
提供方法
const Modeling = this.bpmnModeler.get("modeling");
// 擷取目前擁有的處理程式
Modeling.getHandlers()
/**
* 更新元素的label标簽,同時觸發 element.updateLabel 事件
* @param element: ModdleElement
* @param newLabel: ModdleElement 新的标簽元素
* @param newBounds: {x: number;y: number; width: number; height: number} 位置及大小
* @param hints?:{} 提示資訊
*/
Modeling.updateLabel(element, newLabel, newBounds, hints);
/**
* 建立新的連接配接線,觸發 connection.create 事件
* 會在内部調用 createConnection() 方法(Modeling.prototype.createConnection -- in diagram.js)
* @param source:ModdleElement 源元素
* @param target:ModdleElement 目标元素
* @param attrs?: {} 屬性,未傳時會根據規則替換成對應的對象,主要包含連線類型 type
* @param hints?: {}
* @return Connection 連線執行個體
*/
Modeling.connect(source, target, attrs, hints)
/**
* 更新元素擴充屬性,同時觸發 element.updateModdleProperties
* @param element 目标元素
* @param moddleElement 元素擴充屬性對應的執行個體
* @param properties 屬性
*/
Modeling.updateModdleProperties(element, moddleElement, properties)
/**
* 更新元素屬性,同時觸發 element.updateProperties
* @param element 目标元素
* @param properties 屬性
*/
Modeling.connect(element, properties)
/**
* 泳道(通道)事件,會觸發對應的事件 lane.resize
*/
Modeling.resizeLane(laneShape, newBounds, balanced)
/**
* 泳道(通道)事件,會觸發對應的事件 lane.add
*/
Modeling.addLane(targetLaneShape, location)
/**
* 泳道(通道)事件,會觸發對應的事件 lane.split
*/
Modeling.splitLane(targetLane, count)
/**
* 将目前圖轉換為協作圖
* @return Root
*/
Modeling.makeCollaboration()
/**
* 将目前圖轉換為一個過程
* @return Root
*/
Modeling.makeProcess()
/**
* 修改目标元素color,同時觸發 element.setColor 事件
* @param elements: ModdleElment || ModdleElement[] 目标元素
* @param colors:{[key: string]: string} svg對應的css顔色屬性對象
*/
Modeling.setColor(elements, colors)
BaseModeling
提供方法
BaseModeling
BaseModeling
為
diagram.js
提供的基礎方法,也可以直接調用未被
bpmn.js
覆寫的方法。
// 向指令堆棧注冊處理程式
Modeling.registerHandlers(commandStack)
// 移動 Shape 元素到新元素下, 觸發shape.move
Modeling.moveShape(shape, delta, newParent, newParentIndex, hints)
// 移動多個 Shape 元素到新元素下, 觸發 elements.move
Modeling.moveElements(shapes, delta, target, hints)
// 移動 Connection 元素到新元素下, 觸發 connection.move
Modeling.moveConnection(connection, delta, newParent, newParentIndex, hints)
// 移動 Connection 元素到新元素下, 觸發 connection.move
Modeling.layoutConnection(connection, hints)
/**
* 建立新的連線執行個體,觸發 connection.create
* @param source: ModdleElement
* @param target: ModdleElement
* @param parentIndex?: number
* @param connection: ModdleElement | Object 連線執行個體或者配置的屬性對象
* @param parent:ModdleElement 所在的元素的父元素 通常為 Root
* @param hints: {}
* @return Connection 新的連線執行個體
*/
Modeling.createConnection(source, target, parentIndex, connection, parent, hints)
/**
* 建立新的圖形執行個體,觸發 shape.create
* @param shape
* @param position
* @param target
* @param parentIndex
* @param hints
* @return Shape 新的圖形執行個體
*/
Modeling.createShape(shape, position, target, parentIndex, hints)
/**
* 建立多個元素執行個體,觸發 elements.create
* @param
* @param
* @return Elements 執行個體數組
*/
Modeling.createElements(elements, position, parent, parentIndex, hints)
/**
* 為元素建立 label 執行個體, 觸發 label.create
* @param labelTarget: ModdleElement 目标元素
* @param position: { x: number; y: number }
* @param label:ModdleElement label 執行個體
* @param parent: ModdleElement
* @return Label
*/
Modeling.createLabel(labelTarget, position, label, parent)
/**
* 将形狀附加到給定的源,在源和新建立的形狀之間繪制連接配接。觸發 shape.append
* @param source: ModdleElement
* @param shape: ModdleElement | Object
* @param position: { x: number; y: number }
* @param target: ModdleElement
* @param hints
* @return Shape 形狀執行個體
*/
Modeling.appendShape(source, shape, position, target, hints)
/**
* 移除元素,觸發 elements.delete
* @param elements: ModdleElement[]
*/
Modeling.removeElements(elements)
/**
* 不太了解
*/
Modeling.distributeElements(groups, axis, dimension)
/**
* 移除元素, 觸發 shape.delete
* @param shape: ModdleElement
* @param hints?: object
*/
Modeling.removeShape(shape, hints)
/**
* 移除連線, 觸發 connection.delete
* @param connection: ModdleElement
* @param hints?: object
*/
Modeling.removeConnection(connection, hints)
/**
* 更改元素類型(替換元素),觸發 shape.replace
* @param oldShape:ModdleElement
* @param newShape:ModdleElement
* @param hints?: object
* @return Shape 替換後的新元素執行個體
*/
Modeling.replaceShape(oldShape, newShape, hints)
/**
* 對其選中元素,觸發 shape.replace
* @param elements: ModdleElement[]
* @param alignment: Alignment
* @return
*/
Modeling.alignElements(elements, alignment)
/**
* 調整形狀元素大小,觸發 shape.resize
* @param shape: ModdleElement
* @param newBounds
* @param minBounds
* @param hints?: object
*/
Modeling.resizeShape(shape, newBounds, minBounds, hints)
/**
* 切換元素展開/收縮模式,觸發 shape.toggleCollapse
* @param shape?: ModdleElement
* @param hints?: object=
*/
Modeling.toggleCollapse(shape, hints)
// 連線調整的方法
Modeling.reconnect(connection, source, target, dockingOrPoints, hints)
Modeling.reconnectStart(connection, newSource, dockingOrPoints, hints)
Modeling.reconnectEnd(connection, newTarget, dockingOrPoints, hints)
Modeling.connect(source, target, attrs, hints)
8. Draw 繪制子產品
基礎的元素繪制方法,由
diagram.js
提供基礎子產品,源碼如下:
// diagram.js/lib/draw/index.js
import DefaultRenderer from './DefaultRenderer';
import Styles from './Styles';
export default {
__init__: [ 'defaultRenderer' ],
defaultRenderer: [ 'type', DefaultRenderer ],
styles: [ 'type', Styles ]
};
其中
DefaultRenderer
為預設元素繪制方法,繼承
BaseRenderer
,自身包含
CONNECTION_STYLE --連線預設樣式
,
FRAME_TYLE -- 架構預設樣式
和
SHAPE_STYLE -- 元素預設樣式
三個樣式屬性。
Styles
為樣式管理元件,包含
cls -- 根據屬性、樣式名等來定義樣式
,
style -- 根據屬性計算樣式
和
computeStyle -- 樣式計算方法
三個方法。
BaseRenderer
是一個抽象類,隻定義了方法和繪制時的觸發事件,沒有定義方法的具體實作。
Styles
樣式管理( diagram.js
)
Styles
diagram.js
根據源碼的思路,這個子產品隻推薦重寫,即修改預設的類名與樣式配置。
// diagram.js/lib/draw/Styles.js
import { isArray, assign, reduce } from 'min-dash';
/**
* A component that manages shape styles
*/
export default function Styles() {
var defaultTraits = {
'no-fill': {
fill: 'none'
},
'no-border': {
strokeOpacity: 0.0
},
'no-events': {
pointerEvents: 'none'
}
};
var self = this;
/**
* Builds a style definition from a className, a list of traits and an object of additional attributes.
*
* @param {string} className
* @param {Array<string>} traits
* @param {Object} additionalAttrs
*
* @return {Object} the style defintion
*/
this.cls = function(className, traits, additionalAttrs) {
var attrs = this.style(traits, additionalAttrs);
return assign(attrs, { 'class': className });
};
/**
* Builds a style definition from a list of traits and an object of additional attributes.
*
* @param {Array<string>} traits
* @param {Object} additionalAttrs
*
* @return {Object} the style defintion
*/
this.style = function(traits, additionalAttrs) {
if (!isArray(traits) && !additionalAttrs) {
additionalAttrs = traits;
traits = [];
}
var attrs = reduce(traits, function(attrs, t) {
return assign(attrs, defaultTraits[t] || {});
}, {});
return additionalAttrs ? assign(attrs, additionalAttrs) : attrs;
};
this.computeStyle = function(custom, traits, defaultStyles) {
if (!isArray(traits)) {
defaultStyles = traits;
traits = [];
}
return self.style(traits || [], assign({}, defaultStyles, custom || {}));
};
}
DefaultRenderer
預設繪制方法( diagram.js
)
DefaultRenderer
diagram.js
源碼位置: diagram-js/lib/draw/DefaultRenderer.js
繼承了
diagram.js/BaseRenderer
,注入
eventBus
styles
子產品,并且預設繪制方法的處理優先級最低,在有其他繪制方法的時候會被覆寫。
BaseRenderer
提供了一個抽象基類,并且提供了
canRender() , getShapePath(), getConnecttionPath(), drawShape(), DrawConnection()
五個抽象方法,定義了方法觸發時刻。
eventBus.on([ 'render.shape', 'render.connection' ], renderPriority, function(evt, context) {
var type = evt.type,
element = context.element,
visuals = context.gfx;
if (self.canRender(element)) {
if (type === 'render.shape') {
return self.drawShape(visuals, element);
} else {
return self.drawConnection(visuals, element);
}
}
});
eventBus.on([ 'render.getShapePath', 'render.getConnectionPath'], renderPriority, function(evt, element) {
if (self.canRender(element)) {
if (evt.type === 'render.getShapePath') {
return self.getShapePath(element);
} else {
return self.getConnectionPath(element);
}
}
});
DefaultRenderer
重寫了以上五個方法(
canRender()
直接傳回了
true
, 表示任何情況都可以繪制和渲染元素),實作預設元素和樣式的解析渲染。
方法說明:
-
: 判斷方法,傳回一個布爾值,為真時表示可以繼續解析元素屬性(位置、大小、形狀等)或者繼續渲染屬性。canRender()
-
: 元素(預設是方形元素)屬性解析方法。getShapePath(shape)
-
: 連線屬性解析方法。getConnectionPath(connection)
-
: 元素(預設是方形元素)繪制方法。drawShape(visuals, element)
-
: 連線繪制方法。drawConnection(visuals, connection)
--------------------------------- 分割線 -------------------------------------
繼承
bpmn.js
定義了一個
diagram.js/BaseRenderer
類,并針對
BpmnRender
流程需要的其他元素做了新的處理。
bpmn 2.0
bpmn.js
為了實作
bpmn 2.0
流程圖的支援,不僅重新定義了新的渲染方法類
BpmnRenderer, TextRender, PathMap
,以保證圖形元素的正常解析,以及
label
的便捷添加修改。
import BpmnRenderer from './BpmnRenderer';
import TextRenderer from './TextRenderer';
import PathMap from './PathMap';
export default {
__init__: [ 'bpmnRenderer' ],
bpmnRenderer: [ 'type', BpmnRenderer ],
textRenderer: [ 'type', TextRenderer ],
pathMap: [ 'type', PathMap ]
};
BpmnRenderer
流程元素繪制方法
BpmnRenderer
支援
bpmn 2.0
的流程元素的基礎繪制方法,繼承
BaseRender
,注入了
config, eventBus, styles, pathMap, canvas, textRenderer
子產品。源碼位于
bpmn-js/lib/draw/BpmnRenderer.js
,共1900+行(其中1200+行都在定義繪制各種元素的方法)。
BpmnRenderer
隻實作了基類的4個抽象方法(
getConnectionPath()
方法沒有使用,由此可見其實
bpmn-js
内部的連線元素也是當做了
shape
類型來進行處理的,畢竟有個箭頭,也可能存在折線的情況),并且沒有新增方法。但是在
canRender()
方法裡判斷了需要渲染的元素是否屬于
bpmn:BaseElement
類型。
BpmnRenderer.prototype.canRender = function(element) {
return is(element, 'bpmn:BaseElement'); // 從解析檔案 bpmn.json 其實可以發現,所有需要渲染的元素最終都繼承了 Bpmn:BaseElement
};
在
getShapePath()
方法中,對屬于
bpmnEvent(事件類節點,例如開始和結束等事件,顯示為圓形)
,
bpmn:Activity(任務類節點,包含子流程類型的節點,顯示為圓角矩形)
,
bpmn:Gateway(網關類型,顯示為菱形)
三個大類型的節點定義的對應的路徑擷取方法,其他類型則沿用與
diagram.js/DefaultRenderer.js
裡面使用的
getRectPath()
方法。
drawShape()
與
drawConnection()
方法則是判斷了需要渲染的元素類型,調用對應的
handler()
方法也處理(也就是上面說的那1200+行代碼),通過
handlers
對象(所有
handler()
方法的集合,以各類型的類型名作為
key
),可以發現可顯示的元素一共有60種:
0: "bpmn:Event"
1: "bpmn:StartEvent"
2: "bpmn:MessageEventDefinition"
3: "bpmn:TimerEventDefinition"
4: "bpmn:EscalationEventDefinition"
5: "bpmn:ConditionalEventDefinition"
6: "bpmn:LinkEventDefinition"
7: "bpmn:ErrorEventDefinition"
8: "bpmn:CancelEventDefinition"
9: "bpmn:CompensateEventDefinition"
10: "bpmn:SignalEventDefinition"
11: "bpmn:MultipleEventDefinition"
12: "bpmn:ParallelMultipleEventDefinition"
13: "bpmn:EndEvent"
14: "bpmn:TerminateEventDefinition"
15: "bpmn:IntermediateEvent"
16: "bpmn:IntermediateCatchEvent"
17: "bpmn:IntermediateThrowEvent"
18: "bpmn:Activity"
19: "bpmn:Task"
20: "bpmn:ServiceTask"
21: "bpmn:UserTask"
22: "bpmn:ManualTask"
23: "bpmn:SendTask"
24: "bpmn:ReceiveTask"
25: "bpmn:ScriptTask"
26: "bpmn:BusinessRuleTask"
27: "bpmn:SubProcess"
28: "bpmn:AdHocSubProcess"
29: "bpmn:Transaction"
30: "bpmn:CallActivity"
31: "bpmn:Participant"
32: "bpmn:Lane"
33: "bpmn:InclusiveGateway"
34: "bpmn:ExclusiveGateway"
35: "bpmn:ComplexGateway"
36: "bpmn:ParallelGateway"
37: "bpmn:EventBasedGateway"
38: "bpmn:Gateway"
39: "bpmn:SequenceFlow"
40: "bpmn:Association"
41: "bpmn:DataInputAssociation"
42: "bpmn:DataOutputAssociation"
43: "bpmn:MessageFlow"
44: "bpmn:DataObject"
45: "bpmn:DataObjectReference"
46: "bpmn:DataInput"
47: "bpmn:DataOutput"
48: "bpmn:DataStoreReference"
49: "bpmn:BoundaryEvent"
50: "bpmn:Group"
51: "label"
52: "bpmn:TextAnnotation"
53: "ParticipantMultiplicityMarker"
54: "SubProcessMarker"
55: "ParallelMarker"
56: "SequentialMarker"
57: "CompensationMarker"
58: "LoopMarker"
59: "AdhocMarker"
具體的實作方法,有興趣的童鞋可以自行檢視。
要實作自定義,本質也是定義一個自己的渲染函數類,繼承
renderer
,然後修改原型鍊上的方法,使生成的渲染方法執行個體每次調用
BaseRenderer
或者
drawShape()
等方法的時候都調用自定義的繪制方法(這裡必須重新實作基類
drawConnection()
的幾個方法,否則不生效)。
BaseRenderer
TextRenderer
文本元素繪制方法
TextRenderer
源碼位于
bpmn-js/lib/draw/TextRenderer.js
,主要實作了文字元素(即
Label
标簽)的渲染與顯示,通過擷取綁定節點的位置和大小,在對應的位置生成一個
text
标簽來顯示文本。可通過重寫該函數類來實作自定義的文本位置控制。
PathMap
SVG元素路徑對象
PathMap
包含
BpmnRenderer
所需的SVG路徑的函數,内部有一個
pathMap
對象,儲存了所有的元素的 svg 路徑、預設大小。
9. AlignElements 元素對齊
diagram.js
子產品,注入子產品
Modeling
。主要用作元素對齊。
會按照元素對齊方向的邊界對齊。
使用:
const AlignElements = this.bpmnModeler.get("alignElements");
/**
* Executes the alignment of a selection of elements
* 執行元素選擇的對齊
*
* @param {Array} elements 通常為節點元素
* @param {string} type 可用:left|right|center|top|bottom|middle
*/
AlignElements.trigger(Elements, type);
改寫:
// index.js
import CustomElements from './CustomElements';
export default {
__init__: [ 'customElements' ],
customElements: [ 'type', CustomElements ]
};
// CustomElements.js
import inherits from 'inherits';
import AlignElements from 'diagrem-js/lib/features/align-elements/AlignElements';
export default function CustomElements(modeling) {
this._modeling = modeling;
}
inherits(CustomElements, AlignElements);
CustomElements.$inject = [ 'modeling' ];
CustomElements.prototype.trigger = function(elements, type) {
// 對齊邏輯
}
10. AttachSupport 依附支援
diagram.js
子產品,注入子產品
injector, eventBus, canvas, rules, modeling
,依賴規則子產品
rulesModule
。主要用作元素移動期間的綁定關系和預覽。
基礎邏輯子產品,不推薦更改,也不提供直接使用的方法。
11. AutoPlace 元素自動放置
将元素自動放置到合适位置(預設在後方,正後方存在元素時向右下偏移)并調整連接配接的方法。通常在點選
contentPad
建立新元素的時候觸發。
注入基礎子產品
eventBus
與
modeling
。
預設會初始化一個放置後選中元素的方法。
使用:
const AutoPlace = this.bpmnModeler.get("autoPlace");
/**
* Append shape to source at appropriate position.
* 将形狀添加的源對應的合适位置
* 會觸發 autoPlace.start autoPlace autoPlace.end 三個事件
*
* @param {djs.model.Shape} source ModdleElement
* @param {djs.model.Shape} shape ModdleElement
*
* @return {djs.model.Shape} appended shape
*/
AutoPlace.append(source, shape, hints);
12. AutoResize 元素大小調整
一個自動調整大小的元件子產品,用于在建立或移動子元素接近父邊緣的情況下擴充父元素。
注入子產品
eventBus, elementRegistry, modeling, rules
暫時沒找到怎麼直接調用
重寫/禁用:
// 重寫
// index.js
import AutoResize from './AutoResize';
export default {
__init__: [ 'autoResize' ],
autoResize: [ 'type', AutoResize ]
// 禁用直接設定 autoResize: [ 'type', "" ] 或者 autoResize: [ 'type', () => false ]
};
// AutoResize.js
export default function AutoResize(eventBus, elementRegistry, modeling, rules) {
// ...
}
AutoResize.$inject = [
'eventBus',
'elementRegistry',
'modeling',
'rules'
];
inherits(AutoResize, CommandInterceptor); // CommandInterceptor 向commandStack中插入指令的原型。
13. AutoScroll 畫布滾動
畫布自動擴充滾動的方法,如果目前光标點靠近邊框,則開始畫布滾動。 當目前光标點移回到滾動邊框内時取消或手動取消。
依賴于
DraggingModule
,注入子產品
eventBus, canvas
函數原型上傳入了,但列印為
config
undefined
使用與方法:
const AutoScroll = this.modeler.get("autoScroll");
/**
* Starts scrolling loop.
* 開始滾動
* Point is given in global scale in canvas container box plane.
*
* @param {Object} point { x: X, y: Y }
*/
AutoScroll.startScroll(point);
// 停止滾動
AutoScroll.stopScroll();
/**
* 覆寫預設配置
* @param {Object} options
* options.scrollThresholdIn: [ 20, 20, 20, 20 ],
* options.scrollThresholdOut: [ 0, 0, 0, 0 ],
* options.scrollRepeatTimeout: 15,
* options.scrollStep: 10
*/
AutoScroll.setOptions(options);
本文首發于“掘金”
作者:MiyueFE
連結:https://juejin.cn/post/6925304166638174216
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。