经过前面 palette
模块的自定义,左侧工具栏的样式已经出来了,但是 render
和 contextPad
这两个部分还是原来的样式。同样的,这两部分也需要重写对应的方法来实现自定义。
同样在plugins
文件目录下新建render
文件夹。创建一个index.js
入口文件和render.js
自定义render文件(用来覆盖默认render),再创建一个util.js
文件,用来存放render
的自定义配置(元素大小和自定义元素图片路径)。
render.js
import inherits from 'inherits'
import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer'
import {
isObject,
assign,
forEach
} from 'min-dash';
import {
append as svgAppend,
create as svgCreate,
classes as svgClasses
} from 'tiny-svg'
import {
customElements,
customConfig,
hasLabelElements
} from './util'
export default function CustomRenderer(eventBus, styles, textRenderer) {
//删除双击事件
delete eventBus._listeners['element.dblclick']
BaseRenderer.call(this, eventBus, 2000)
var computeStyle = styles.computeStyle
function renderLabel(parentGfx, label, options) {
options = assign({
size: {
x: 0,
y: 20,
width: 40,
}
}, options);
var text = textRenderer.createText(label || '', options);
svgClasses(text).add('djs-label');
svgAppend(parentGfx, text);
return text;
}
//计算文字宽度
String.prototype.pxWidth = function (font) {
// re-use canvas object for better performance
var canvas = String.prototype.pxWidth.canvas || (String.prototype.pxWidth.canvas = document.createElement("canvas")),
context = canvas.getContext("2d");
font && (context.font = font);
var metrics = context.measureText(this);
return metrics.width;
}
//绘制自定义元素
this.drawCustomElements = function (parentNode, element) {
const {
type,
} = element // 获取到类型
if (type !== 'label') {
if (customElements.includes(type)) { // or customConfig[type]
const {
url,
attr,
defaultName
} = customConfig[type]
//可以设置节点默认值
// if (!element.businessObject.name && defaultName) {
// element.businessObject.name = defaultName
// }
const customIcon = svgCreate('image', {
...attr,
href: url
})
element['width'] = attr.width // 直接修改了元素的宽高
element['height'] = attr.height
svgAppend(parentNode, customIcon)
// 判断是否有name属性来决定是否要渲染出label
// if (!hasLabelElements.includes(type) && element.businessObject.name) {
//当类型为UserTask时,自定义绘制label标签(标签会在shape里面)
if (type === 'bpmn:UserTask' && element.businessObject.name) {
const textWidth = element.businessObject.name.pxWidth('normal 13px')
const text = svgCreate('text', {
x: attr.x + (attr.width / 2 - textWidth / 1.6),
y: attr.y + attr.height + 15,
fontSize: "13px",
fill: "#000"
})
text.innerHTML = element.businessObject.name
svgAppend(parentNode, text)
}
// renderLabel(parentNode, element.label)
return customIcon
}
const shape = this.bpmnRenderer.drawShape(parentNode, element)
return shape
}
}
}
inherits(CustomRenderer, BaseRenderer)
CustomRenderer.$inject = ['eventBus', 'styles', 'textRenderer']
CustomRenderer.prototype.canRender = function (element) {
// ignore labels
return true
// return !element.labelTarget;
}
CustomRenderer.prototype.drawShape = function (p, element) {
const {
type,
} = element
if (customElements.includes(type)) {
return this.drawCustomElements(p, element)
}
}
CustomRenderer.prototype.getShapePath = function (shape) {
}
上面代码做的事情:
重写 renderer类,同时覆盖了其原型上的 drawShape方法
通过drawShape来绘制各种类型的元素,先判断哪些元素类型需要自定义绘制,然后通过drawCustomElements方法绘制自定义元素。
util.js
// 数组的作用就是用来放哪些类型是需要我们自定义的, 从而在渲染的时候就可以与不需要自定义的元素作区分.
const startNode = require('/public/bpmn_imgs/startNode.png')
const endNode = require('/public/bpmn_imgs/endNode.png')
const taskNode = require('/public/bpmn_imgs/taskNode.png')
const gatewayNode = require('/public/bpmn_imgs/gatewayNode.png')
const customElements = ['bpmn:StartEvent', 'bpmn:ExclusiveGateway', 'bpmn:UserTask', 'bpmn:EndEvent'] //需要自定义的元素类型
const hasLabelElements = ['bpmn:StartEvent', 'bpmn:ExclusiveGateway', 'bpmn:UserTask', 'bpmn:EndEvent'] // 一开始就有label标签的元素类型
const customConfig = { // 自定义元素的配置
'bpmn:StartEvent': {
url: startNode,
defaultName: '开始',
attr: {
x: 0,
y: 0,
width: 40,
height: 40
}
},
'bpmn:EndEvent': {
url: endNode,
defaultName: '结束',
attr: {
x: 0,
y: 0,
width: 40,
height: 40
}
},
'bpmn:UserTask': {
url: taskNode,
attr: {
x: 0,
y: 0,
width: 40,
height: 40
}
},
'bpmn:ExclusiveGateway': {
url: gatewayNode,
attr: {
x: 0,
y: 0,
width: 40,
height: 40
}
},
}
export {
customElements,
hasLabelElements,
customConfig
}
util.js
导出了一些自定义元素的配置 customConfig 中配置了需要自定义的元素的图片url和宽、高、坐标。
index.js
import CustomRenderer from './render'
export default {
__init__: ["CustomRenderer"],
CustomRenderer: ["type", CustomRenderer]
};
render
这个模块的类重写完过后,应该看到绘制在画板上的元素变成自定义的了
但是contextPad
这个部分还是原生的样式,这个模块同样也需要重写。到这里我们应该清楚了我们就是通过重写bpmn
的几个方法来达到自定义的效果,所以搞懂方法里的参数和作用也很重要,有兴趣的自己打印出来里面的参数研究一下,会更加清晰代码到底做了什么。