Bpmn.js Chinese document (II)

Time:2021-9-18

Due to work needs (not very much), a BPMN process designer based on BPMN JS + Vue 2. X + elementui is open-source on the basis of the company’s project. The preview address is miyuefe blog. Welcome to fork and star.
The article was first published in the Chinese document (2) of nuggets bpmn.js. Please indicate the source for reprint.


aboutbpmn.jsFor the simple use of bpmn.js, I have made a brief description in the bpmn.js Chinese document (I), and explained the APIs of several core modules. This paper will continue tobpmn.jsThe function module API is briefly described.

4、 Modules

7. Basic modeling method of modeling

Diagram.jsBasic modeling factory providedBaseModeling, injectedEventBus, ElementFactory, CommandStackmodular.Bpmn.jsInheritedBaseModelingA new method is provided.

This module is often used in customizing node attributes

Mode of use

const Modeling = this.bpmnModeler.get("modeling");

ModelingWhen initializing, theCommandStackRegister the corresponding handler in the command stack to ensure that the operation can be resumed and cancelled.

ModelingThe method provided is mainly based onhandlersEach method will trigger the corresponding event

// BaseModeling (diagram.js)
BaseModeling.prototype.getHandlers = function () {
    var BaseModelingHandlers = {
        'shape. Append ': appendshapehandler, // the handler that the shape is reversibly added to the source shape
        'shape. Create ': createshapehandler, // the handler that creates and adds the shape reversibly to the process
        'shape. Delete ': deleteshapehandler, // handler for reversible shape removal
        'shape. Move ': moveshapehandler, // handler for reversible shape movement
        'shape. Resize ': resizeshapehandler, // handler for reversible shape transform size
        'shape. Replace ': replaceshapehandler, // replace the shape by adding a new shape and deleting the old shape. If possible, incoming and outgoing connections are maintained
        'shape. Togglecollapse ': toggleshapecollapsehandler, // switch the folding state of the element and the visibility of all its child elements
        'spacetool ': spacetoolhandler, // add or delete spaces by moving and adjusting shapes, sizes, and connecting anchors (cruise points)
        'label. Create': createlabelhandler, // create labels and attach them to specific model elements
        'connection. Create': createconnectionhandler, // create a connection and display it on the canvas
        'connection. Delete': deleteconnectionhandler, // remove the connection
        'connection. Move': moveconnectionhandler, // a handler that implements the reversible movement of a connection. This handler differs from the layout join handler in that it retains the join layout
        'connection. Layout': layoutconnectionhandler, // a handler that implements the reversible movement of shapes
        'connection. Updatewaypoints': updatewaypointshandler, // update anchor (cruise point)
        'connection. Reconnect': reconnectconnectionhandler, // reestablish the connection relationship
        'elements. Create': createelementhandler, // handler for reversible creation of elements
        'elements. Move': moveelementshandler, // handler for reversible movement of elements
        'elements. Delete': deleteelementhandler, // handler for reversible removal of elements
        'elements. Distribute': distributelementhandler, // handler for evenly distributing element layout
        'elements. Align': alignelementshandler, // align elements in some way
        'element. Updateattachment': updateattachmenthandler // a handler that implements reversible attachment / separation of shapes.
    }
    return BaseModelingHandlers;
}

// Modeling (bpmn.js)
var ModelingHandlers = BaseModeling.prototype.getHandlers.call(this);

ModelingHandlers['element.updateModdleProperties'] = UpdateModdlePropertiesHandler; //  Implements reversible modification of extended attributes on elements
ModelingHandlers['element.updateProperties'] = UpdatePropertiesHandler; //  Implements reversible modification of attributes on an element
ModelingHandlers['canvas.updateRoot'] = UpdateCanvasRootHandler; //  Reversible update canvas mount node
ModelingHandlers['lane.add'] = AddLaneHandler; //  Reversible channel addition
ModelingHandlers['lane.resize'] = ResizeLaneHandler; //  Channel reversible resize
ModelingHandlers['lane.split'] = SplitLaneHandler; //  Reversible separation of channels
ModelingHandlers['lane.updateRefs'] = UpdateFlowNodeRefsHandler; //  Reversible update channel reference
ModelingHandlers['id.updateClaim'] = IdClaimHandler;
ModelingHandlers['element.setColor'] = SetColorHandler; //  Reversible update element color
ModelingHandlers['element.updateLabel'] = UpdateLabelHandler; //  Reversible update element label

Provide method

const Modeling = this.bpmnModeler.get("modeling");


//Gets the currently owned handler
Modeling.getHandlers()

/**
 *Update the label label of the element and trigger the element.updatelabel event at the same time
 * @param element: ModdleElement
 *@ param newlabel: moddleelement new label element
 *@ param newboundaries: {X: number; Y: number; width: number; height: number} location and size
 * @param hints?: {} prompt information
 */
Modeling.updateLabel(element, newLabel, newBounds, hints);

/**
 *Create a new connection line and trigger the connection.create event
 *The createconnection () method (modeling. Prototype. Createconnection -- in diagram. JS) will be called internally
 *@ param source: moddleelement source element
 *@ param target: moddleelement target element
 * @param attrs?:  The {} attribute will be replaced with the corresponding object according to the rules when it is not transmitted, mainly including the connection type
 * @param hints?: {} 
 *@ return connection connection instance
 */
Modeling.connect(source, target, attrs, hints)

/**
 *Update the element extension properties and trigger element.updatemoddelproperties at the same time
 *@ param elementtarget element
 *The @ param moddleelement element element extends the instance corresponding to the attribute
 *@ param properties property
 */
Modeling.updateModdleProperties(element, moddleElement, properties)

/**
 *Update element properties and trigger element.updateproperties at the same time
 *@ param elementtarget element
 *@ param properties property
 */
Modeling.connect(element, properties)

/**
 *Lane (channel) event will trigger the corresponding event lane.resize
 */
Modeling.resizeLane(laneShape, newBounds, balanced)

/**
 *Lane (channel) event will trigger the corresponding event lane.add
 */
Modeling.addLane(targetLaneShape, location)

/**
 *Lane (channel) event will trigger the corresponding event lane.split
 */
Modeling.splitLane(targetLane, count)

/**
 *Convert current graph to collaboration graph
 * @return Root
 */
Modeling.makeCollaboration()

/**
 *Convert the current graph to a process
 * @return Root
 */
Modeling.makeProcess()

/**
 *Modify the target element color and trigger the element.setcolor event at the same time
 *@ param elements: moddlelement | moddleelement [] target element
 *@ param colors: {[key: String]: String} CSS color attribute object corresponding to svg
 */
Modeling.setColor(elements, colors)

BaseModelingProvide method

BaseModelingbydiagram.jsThe provided basic method can also be called directly without being usedbpmn.jsMethod of coverage.

//Registers a handler with the command stack
Modeling.registerHandlers(commandStack)

//Move the shape element to the new element and trigger shape.move
Modeling.moveShape(shape, delta, newParent, newParentIndex, hints)

//Move multiple shape elements under the new element and trigger elements.move
Modeling.moveElements(shapes, delta, target, hints)

//Move the connection element to the new element and trigger connection.move
Modeling.moveConnection(connection, delta, newParent, newParentIndex, hints)

//Move the connection element to the new element and trigger connection.move
Modeling.layoutConnection(connection, hints)


/**
 *Create a new connection instance and trigger connection.create
 * @param source: ModdleElement
 * @param target: ModdleElement
 * @param parentIndex?: number
 *@ param connection: moddlelement | object connection instance or configured property object
 *@ param parent: the parent element of the element where moddleelement is located is usually root
 * @param hints: {}
 *@ return connection new connection instance
 */
Modeling.createConnection(source, target, parentIndex, connection, parent, hints)

/**
 *Create a new graphic instance and trigger shape.create
 * @param shape
 * @param position
 * @param target
 * @param parentIndex
 * @param hints
 *@ return shape new graphic instance
 */
Modeling.createShape(shape, position, target, parentIndex, hints)

/**
 *Create multiple element instances and trigger elements.create
 * @param
 * @param
 *@ return elements instance array
 */
Modeling.createElements(elements, position, parent, parentIndex, hints)

/**
 *Create a label instance for the element and trigger label.create
 *@ param labeltarget: moddleelement target element
 * @param position: { x: number; y: number }
 *@ param label: moddleelement label instance
 * @param parent: ModdleElement
 * @return Label
 */
Modeling.createLabel(labelTarget, position, label, parent)

/**
 *Attach a shape to a given source and draw a connection between the source and the newly created shape. Trigger shape.append
 * @param source: ModdleElement
 * @param shape: ModdleElement | Object
 * @param position: { x: number; y: number }
 * @param target: ModdleElement
 * @param hints
 *@ return shape instance
 */
Modeling.appendShape(source, shape, position, target, hints)

/**
 *Remove the element and trigger elements.delete
 * @param elements: ModdleElement[]
 */
Modeling.removeElements(elements)

/**
 *I don't know much
 */
Modeling.distributeElements(groups, axis, dimension)

/**
 *Remove the element and trigger shape.delete
 * @param shape: ModdleElement
 * @param hints?: object
 */
Modeling.removeShape(shape, hints)

/**
 *Remove the connection and trigger connection.delete
 * @param connection: ModdleElement
 * @param hints?: object
 */
Modeling.removeConnection(connection, hints)

/**
 *Change the element type (replace element) and trigger shape.replace
 * @param oldShape:ModdleElement
 * @param newShape:ModdleElement
 * @param hints?: object
 *@ return shape replaced new element instance
 */
Modeling.replaceShape(oldShape, newShape, hints)

/**
 *Trigger shape.replace for the selected element
 * @param elements: ModdleElement[]
 * @param alignment: Alignment
 * @return
 */
Modeling.alignElements(elements, alignment)

/**
 *Adjust the size of the shape element and trigger shape.resize
 * @param shape: ModdleElement
 * @param newBounds
 * @param minBounds
 * @param hints?: object
 */
Modeling.resizeShape(shape, newBounds, minBounds, hints)

/**
 *Switch the element expansion / contraction mode and trigger shape.togglecollapse
 * @param shape?: ModdleElement
 * @param hints?: object=
 */
Modeling.toggleCollapse(shape, hints)

//Connection adjustment method
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 drawing module

Basic element drawing method, bydiagram.jsProvide basic modules, and the source code is as follows:

// diagram.js/lib/draw/index.js
import DefaultRenderer from './DefaultRenderer';
import Styles from './Styles';

export default {
  __init__: [ 'defaultRenderer' ],
  defaultRenderer: [ 'type', DefaultRenderer ],
  styles: [ 'type', Styles ]
};

amongDefaultRendererDraw methods for default elements, inheritingBaseRenderer, self containedCONNECTION_ Style -- line default style, FRAME_ Type -- frame default styleandSHAPE_ Style -- element default styleThree style properties.

StylesFor style management components, includeCLS -- defines styles based on attributes, style names, and so on, Style -- calculate styles based on attributesandComputestyle -- style calculation methodThree methods.

BaseRendererIs an abstract class, which only defines the method and the trigger event during drawing, but does not define the specific implementation of the method.

StylesStyle management(diagram.js

According to the idea of the source code, this module only recommends rewriting, that is, modifying the default class name and style configuration.

// 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 || {}));
  };
}

DefaultRendererDefault drawing method(diagram.js

Source location:diagram-js/lib/draw/DefaultRenderer.js

Inheriteddiagram.js/BaseRenderer, injectioneventBus stylesModule, and the default rendering method has the lowest processing priority, which will be overwritten when there are other rendering methods.

BaseRendererProvides an abstract base class, and providescanRender() , getShapePath(), getConnecttionPath(), drawShape(), DrawConnection()Five abstract methods define the method trigger time.

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);
        }
    }
});

DefaultRendererThe above five methods have been rewritten(canRender()Straight backtrue, which means that elements can be drawn and rendered in any case, so as to realize the analytical rendering of default elements and styles.

Method description:

  1. canRender(): judgment method, which returns a Boolean value. If true, it means that you can continue to parse element attributes (position, size, shape, etc.) or render attributes.
  2. getShapePath(shape): element (square element by default) attribute resolution method.
  3. getConnectionPath(connection): connection attribute resolution method.
  4. drawShape(visuals, element): drawing method of element (square element by default).
  5. drawConnection(visuals, connection): line drawing method.

———————————Split line————————————-

bpmn.jsinheritdiagram.js/BaseRendererDefines aBpmnRenderClass, and forbpmn 2.0Other elements required by the process have been newly processed.

bpmn.jsIn order to achievebpmn 2.0Flowchart support not only redefines the new rendering method classBpmnRenderer, TextRender, PathMapTo ensure the normal parsing of graphic elements, andlabelEasy to add and modify.

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 ]
};

BpmnRendererProcess element drawing method

supportbpmn 2.0The basic drawing method of process elements, inheritanceBaseRender, injectedconfig, eventBus, styles, pathMap, canvas, textRenderermodular. The source code is located inbpmn-js/lib/draw/BpmnRenderer.js, a total of 1900 + lines (of which 1200 + lines define the method of drawing various elements).

BpmnRendererOnly four abstract methods of the base class are implemented(getConnectionPath()Method is not used, so it can be seen thatbpmn-jsThe internal wiring element is also used asshapeType. After all, there is an arrow (or a polyline), and there is no new method. But incanRender()Method to determine whether the element to be rendered belongs tobpmn:BaseElementType.

BpmnRenderer.prototype.canRender = function(element) {
      return is(element, 'bpmn:BaseElement'); //  From the parsing file bpmn.json, it can be found that all elements to be rendered eventually inherit BPMN: baseelement
};

staygetShapePath()Method, which belongs toBpmnevent (event class nodes, such as start and end events, are displayed as circles)BPMN: activity (task class node, including sub process type nodes, displayed as rounded rectangle)BPMN: gateway (Gateway type, displayed as diamond)The corresponding path acquisition methods defined by the three major types of nodes, and the other types follow the same path asdiagram.js/DefaultRenderer.jsUsed insidegetRectPath()method.

drawShape()AnddrawConnection()Method is to judge the element type to be rendered and call the correspondinghandler()Method (that is, the 1200 + lines of code mentioned above), throughhandlersObject (all)handler()Method, with the type name of each type as thekey), you can find a total of 60 elements that can be displayed:

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"

Specific implementation methods, interested children’s shoes can view by themselves.

To implement customizationrendererIn essence, it is also to define its own rendering function class and inheritBaseRendererThen modify the methods on the prototype chain so that the generated rendering method instance is called every timedrawShape()perhapsdrawConnection()Call the custom drawing method when waiting for the method (the base class must be re implemented here)BaseRendererSeveral methods, otherwise it will not take effect).

TextRendererText element drawing method

The source code is located inbpmn-js/lib/draw/TextRenderer.js, mainly implements the text element (i.eLabelBy obtaining the position and size of the bound node, a new node is generated at the corresponding positiontextLabel to display text. You can override this function class to implement custom text position control.

PathMapSvg element path object

containBpmnRendererThe required SVG path function has an internalpathMapObject, which saves the SVG path and default size of all elements.

9. Align elements

diagram.jsInjection moduleModeling。 Mainly used for element alignment.

Aligns according to the boundary of the element alignment direction.

use:

const AlignElements = this.bpmnModeler.get("alignElements");

/**
 * Executes the alignment of a selection of elements
 *Perform alignment of element selection
 *
 *@ param {array} elements are usually node elements
 *@ param {string} type available: left | right | center | top | bottom | middle
 */
AlignElements.trigger(Elements, type);

Rewrite:

// 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) {
    //Alignment logic
}

10. Attachsupport attachment support

diagram.jsInjection moduleinjector, eventBus, canvas, rules, modeling, dependency rule modulerulesModule。 It is mainly used as binding relationship and preview during element movement.

The basic logic module is not recommended to be changed, nor does it provide a method for direct use.

11. Autoplace elements are placed automatically

Automatically place the element in the appropriate position (the default is in the rear, and the right and down offset when there is an element directly behind) and adjust the connection method. Usually ClickcontentPadTriggered when a new element is created.

Injection base moduleeventBusAndmodeling

By default, a method for placing the selected element is initialized.

use:

const AutoPlace = this.bpmnModeler.get("autoPlace");

/**
 * Append shape to source at appropriate position.
 *Add the shape to the appropriate location corresponding to the source
 *Three events autoplace.start autoplace autoplace.end will be triggered
 *
 * @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 element sizing

An auto resizing component module used to extend a parent element when a child element is created or moved close to the parent edge.

Injection moduleeventBus, elementRegistry, modeling, rules

I haven’t found how to call directly yet

Override / disable:

//Rewrite
// index.js
import AutoResize from './AutoResize';

export default {
  __init__: [ 'autoResize' ],
  autoResize: [ 'type', AutoResize ]
  //Disable direct setting autoresize: ['type ',' '] or autoresize: ['type', () = > false]
};

// AutoResize.js
export default function AutoResize(eventBus, elementRegistry, modeling, rules) {
    // ...
}

AutoResize.$inject = [
  'eventBus',
  'elementRegistry',
  'modeling',
  'rules'
];

inherits(AutoResize, CommandInterceptor); //  The commandinterceptor inserts the prototype of the command into the commandstack.

13. Autoscroll canvas scrolling

The canvas automatically extends the scrolling method. If the current cursor point is close to the border, the canvas scrolling will start. Cancel or manually Cancel when the current cursor point moves back into the scroll frame.

Depend onDraggingModule, injection moduleeventBus, canvas

Function prototype Uploadedconfig, but print asundefined

Usage and method:

const AutoScroll = this.modeler.get("autoScroll");

/**
 * Starts scrolling loop.
 *Start scrolling
 * Point is given in global scale in canvas container box plane.
 *
 * @param  {Object} point { x: X, y: Y }
 */
AutoScroll.startScroll(point);

//Stop scrolling
AutoScroll.stopScroll();

/**
 *Override default configuration
 * @param {Object} options 
 * options.scrollThresholdIn: [ 20, 20, 20, 20 ],
 * options.scrollThresholdOut: [ 0, 0, 0, 0 ],
 * options.scrollRepeatTimeout: 15,
 * options.scrollStep: 10
 */
AutoScroll.setOptions(options);