feat: update shape style

This commit is contained in:
zhanning.bzn 2019-09-26 21:05:57 +08:00
parent 2222dd8680
commit 2930ba7080
13 changed files with 1213 additions and 907 deletions

View File

@ -434,11 +434,14 @@
}
}
}
},
getStateStyle(cfg) {
}
}, 'triangle')
G6.registerNode('triangle2', {
labelPosition: 'center',
getCustomConfig() {
getCustomConfig(cfg) {
return {
default: {
direction: 'down',

View File

@ -0,0 +1,135 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="mountNode"></div>
<script src="../build/g6.js"></script>
<script>
const data = {
nodes: [
{
id: 'triangle',
label: '主题 ModelRect Title',
description: '这里是一顿描述信息,不知道长短',
x: 150,
y: 100
}
]
}
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 600,
defaultNode: {
shape: 'modelRect',
style: {
fill: 'red'
},
icon: {
show: true,
width: 30,
height: 30
},
linkPoints: {
top: false,
fill: '#fff',
size: 5
}
},
nodeStateStyles: {
select: {
fill: 'red'
},
hover: {
fill: 'blue'
}
}
})
// 这里优先级大于graph中的nodeStyle
graph.node(node => {
let fill = 'red'
let shape = 'diamond'
if (node.inDegree > 200) {
fill = '#36cfc9'
shape = 'diamond'
}
return {
shape,
icon: {
show: true,
width: 25,
height: 25
},
linkPoints: {
top: true,
bottom: true
},
style: {
fill,
stroke: '#eaff8f',
radius: 15
},
stateStyles: {
hover: {
fill: '#bae637',
stroke: '#87e8de',
width: 130,
height: 200,
r: 100
},
select: {
fill: '#ccc'
}
}
}
})
graph.data(data)
graph.render()
console.log(graph.save())
graph.on('node:click', evt => {
const { item } = evt
// graph.setItemState(item, 'select', true)
graph.updateItem(item, {
size: [ 160, 130],
style: {
fill: '#b5f5ec'
},
preRect: {
fill: '#ff4d4f'
},
stateIcon: {
img: 'https://gw.alipayobjects.com/zos/basement_prod/c781088a-c635-452a-940c-0173663456d4.svg'
}
})
})
graph.on('node:mouseenter', evt => {
const { item } = evt
// graph.setItemState(item, 'hover', true)
// graph.updateItem(item, {
// style: {
// r: 45,
// icon: {
// img: 'https://gw.alipayobjects.com/zos/basement_prod/c781088a-c635-452a-940c-0173663456d4.svg'
// }
// }
// })
})
graph.on('node:mouseleave', evt => {
const { item } = evt
// graph.setItemState(item, 'hover', false)
})
</script>
</body>
</html>

View File

@ -5,6 +5,7 @@ const NODE = 'node';
const EDGE = 'edge';
const CFG_PREFIX = 'default';
const MAPPER_SUFFIX = 'Mapper';
const STATE_SUFFIX = 'stateStyles';
const hasOwnProperty = Object.hasOwnProperty;
class ItemController {
@ -16,14 +17,14 @@ class ItemController {
const parent = graph.get(type + 'Group') || graph.get('group');
const upperType = Util.upperFirst(type);
let item;
let styles = graph.get(type + 'Style') || {};
let styles = graph.get(type + Util.upperFirst(STATE_SUFFIX)) || {};
const defaultModel = graph.get(CFG_PREFIX + upperType);
const mapper = graph.get(type + MAPPER_SUFFIX);
if (mapper) {
const mappedModel = mapper(model);
if (mappedModel.styles) {
styles = mappedModel.styles;
delete mappedModel.styles;
if (mappedModel[STATE_SUFFIX]) {
styles = mappedModel[STATE_SUFFIX];
delete mappedModel[STATE_SUFFIX];
}
Util.each(mappedModel, (val, cfg) => {
model[cfg] = val;

View File

@ -401,16 +401,16 @@ class Graph extends EventEmitter {
* 若是自定义节点切在各种状态下
* graph.node(node => {
* return {
* default: {
* fill: 'red',
* opacity: 1
* },
* selected: {
* style: {
* fill: 'blue',
* opacity: 0.2
* }
* }
* {
shape: 'rect',
label: node.id,
style: { fill: '#666' },
styles: {
default: { fill: 'red' },
selected: { fill: 'blue' },
custom: { fill: 'green' }
}
}
* }
* });
* @param {function} nodeFn 指定每个节点样式

View File

@ -30,7 +30,8 @@ const singleNodeDefinition = Util.mix({}, SingleShapeMixin, {
* @return {Array} 宽高
*/
getSize(cfg) {
let size = cfg.size || Global.defaultNode.size;
const customOptions = this.getCustomConfig(cfg) || {};
let size = cfg.size || customOptions.size || this.options.size || Global.defaultNode.size;
if (!Util.isArray(size)) {
size = [ size, size ];
}

View File

@ -5,64 +5,70 @@ const deepMix = require('@antv/util/lib/deep-mix');
Shape.registerNode('circle', {
// 自定义节点时的配置
options: {
// 默认配置
default: {
r: 20,
stroke: '#69c0ff',
fill: '#e6f7ff',
lineWidth: 1,
size: 30,
style: {
x: 0,
y: 0,
// 文本样式配置
labelCfg: {
style: {
fill: '#595959'
}
stroke: '#87e8de',
fill: '#36cfc9',
lineWidth: 1
},
labelCfg: {
style: {
fill: '#595959'
},
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A'
offset: 15
},
stateStyles: {
// 鼠标hover状态下的配置
hover: {
lineWidth: 3
},
// 节点中icon配置
icon: {
// 是否显示icon值为 false 则不渲染icon
show: false,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 16,
height: 16
// 选中节点状态下的配置
select: {
lineWidth: 5
}
},
// 鼠标hover状态下的配置
hover: {
lineWidth: 3
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A'
},
// 选中节点状态下的配置
select: {
lineWidth: 5
// 节点中icon配置
icon: {
// 是否显示icon值为 false 则不渲染icon
show: false,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 16,
height: 16
}
},
shapeType: 'circle',
// 文本位置
labelPosition: 'bottom',
drawShape(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { icon, linkPoints, ...circleStyle } = style;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, icon: defaultIcon } = this.options;
const { style: customStyle, icon: customIcon } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const icon = deepMix({}, defaultIcon, customIcon, cfg.icon);
const size = this.getSize(cfg);
const r = size[0];
const keyShape = group.addShape('circle', {
attrs: circleStyle
attrs: {
...style,
r
}
});
const { r } = circleStyle;
const { width, height, show } = icon;
if (show) {
const image = group.addShape('image', {
@ -77,20 +83,35 @@ Shape.registerNode('circle', {
image.set('capture', false);
}
const { top, left, right, bottom, size,
fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.drawLinkPoints(cfg, group);
return keyShape;
},
/**
* 绘制节点上的LinkPoints
* @param {Object} cfg data数据配置项
* @param {Group} group Group实例
*/
drawLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const { top, left, right, bottom, size: markSize,
...markStyle } = linkPoints;
const size = this.getSize(cfg);
const r = size[0];
if (left) {
// left circle
group.addShape('circle', {
attrs: {
...markStyle,
x: -r,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'circle-anchor-left'
className: 'circle-mark-left'
});
}
@ -98,14 +119,12 @@ Shape.registerNode('circle', {
// right circle
group.addShape('circle', {
attrs: {
...markStyle,
x: r,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'circle-anchor-right'
className: 'circle-mark-right'
});
}
@ -113,14 +132,12 @@ Shape.registerNode('circle', {
// top circle
group.addShape('circle', {
attrs: {
...markStyle,
x: 0,
y: -r,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'circle-anchor-top'
className: 'circle-mark-top'
});
}
@ -128,46 +145,33 @@ Shape.registerNode('circle', {
// bottom circle
group.addShape('circle', {
attrs: {
...markStyle,
x: 0,
y: r,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'circle-anchor-bottom'
className: 'circle-mark-bottom'
});
}
return keyShape;
},
/**
* 获取节点宽高
* @internal 返回节点的大小 [width, height] 的方式维护
* @param {Object} cfg 节点的配置项
* @return {Array} 宽高
*/
getSize(cfg) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { r } = style;
return [ 2 * r, 2 * r ];
},
update(cfg, item) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default || {};
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { icon, linkPoints, labelCfg: defaultLabelCfg, ...circleStyle } = style;
const { r } = circleStyle;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, icon: defaultIcon, labelCfg: defaultLabelCfg } = this.options;
const { style: customStyle, icon: customIcon, labelCfg: customLabelCfg } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const icon = deepMix({}, defaultIcon, customIcon, cfg.icon);
const size = this.getSize(cfg);
const r = size[0];
const group = item.getContainer();
const keyShape = item.get('keyShape');
keyShape.attr(circleStyle);
keyShape.attr({
...style,
r
});
const labelCfg = deepMix({}, defaultLabelCfg, cfg.labelCfg);
const labelCfg = deepMix({}, defaultLabelCfg, customLabelCfg, cfg.labelCfg);
const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
const text = group.findByClassName('node-label');
@ -187,52 +191,68 @@ Shape.registerNode('circle', {
});
}
const { size, fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.updateLinkPoints(cfg, group);
},
/**
* 更新linkPoints
* @param {Object} cfg 节点数据配置项
* @param {Group} group Item所在的group
*/
updateLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const anchorLeft = group.findByClassName('circle-anchor-left');
if (anchorLeft) {
anchorLeft.attr({
const { size: markSize, fill: markFill, stroke: markStroke, lineWidth: borderWidth } = linkPoints;
const size = this.getSize(cfg);
const r = size[0];
const markLeft = group.findByClassName('circle-mark-left');
if (markLeft) {
markLeft.attr({
x: -r,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorRight = group.findByClassName('circle-anchor-right');
if (anchorRight) {
anchorRight.attr({
const markRight = group.findByClassName('circle-mark-right');
if (markRight) {
markRight.attr({
x: r,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorTop = group.findByClassName('circle-anchor-top');
if (anchorTop) {
anchorTop.attr({
const markTop = group.findByClassName('circle-mark-top');
if (markTop) {
markTop.attr({
x: 0,
y: -r,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorBottom = group.findByClassName('circle-anchor-bottom');
if (anchorBottom) {
anchorBottom.attr({
const markBottom = group.findByClassName('circle-mark-bottom');
if (markBottom) {
markBottom.attr({
x: 0,
y: r,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}

View File

@ -5,63 +5,65 @@ const deepMix = require('@antv/util/lib/deep-mix');
Shape.registerNode('diamond', {
// 自定义节点时的配置
options: {
// 默认配置
default: {
width: 100,
height: 100,
stroke: '#69c0ff',
fill: '#e6f7ff',
lineWidth: 1,
// 文本样式配置
labelCfg: {
style: {
fill: '#595959'
}
},
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A'
},
// 节点中icon配置
icon: {
// 是否显示icon值为 false 则不渲染icon
show: false,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 16,
height: 16
size: [ 100, 100 ],
style: {
stroke: '#87e8de',
fill: '#36cfc9',
lineWidth: 1
},
// 文本样式配置
labelCfg: {
style: {
fill: '#595959'
}
},
// 鼠标hover状态下的配置
hover: {
lineWidth: 3
stateStyles: {
// 鼠标hover状态下的配置
hover: {
lineWidth: 3
},
// 选中节点状态下的配置
select: {
lineWidth: 5
}
},
// 选中节点状态下的配置
select: {
lineWidth: 5
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A'
},
// 节点中icon配置
icon: {
// 是否显示icon值为 false 则不渲染icon
show: false,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 16,
height: 16
}
},
shapeType: 'circle',
// 文本位置
labelPosition: 'center',
drawShape(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { icon, linkPoints, width, height, ...diamondStyle } = style;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, icon: defaultIcon } = this.options;
const { style: customStyle, icon: customIcon } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const icon = deepMix({}, defaultIcon, customIcon, cfg.icon);
const path = this.getPath(cfg);
const keyShape = group.addShape('path', {
attrs: {
path,
...diamondStyle
...style
}
});
@ -79,20 +81,36 @@ Shape.registerNode('diamond', {
image.set('capture', false);
}
const { top, left, right, bottom, size,
fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.drawLinkPoints(cfg, group);
return keyShape;
},
/**
* 绘制节点上的LinkPoints
* @param {Object} cfg data数据配置项
* @param {Group} group Group实例
*/
drawLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const { top, left, right, bottom, size: markSize,
...markStyle } = linkPoints;
const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
if (left) {
// left circle
group.addShape('circle', {
attrs: {
...markStyle,
x: -width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'diamond-anchor-left'
className: 'diamond-mark-left'
});
}
@ -100,14 +118,12 @@ Shape.registerNode('diamond', {
// right circle
group.addShape('circle', {
attrs: {
...markStyle,
x: width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'diamond-anchor-right'
className: 'diamond-mark-right'
});
}
@ -115,14 +131,12 @@ Shape.registerNode('diamond', {
// top circle
group.addShape('circle', {
attrs: {
...markStyle,
x: 0,
y: -height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'diamond-anchor-top'
className: 'diamond-mark-top'
});
}
@ -130,27 +144,19 @@ Shape.registerNode('diamond', {
// bottom circle
group.addShape('circle', {
attrs: {
...markStyle,
x: 0,
y: height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'diamond-anchor-bottom'
className: 'diamond-mark-bottom'
});
}
return keyShape;
},
shouldUpdate() {
return false;
},
getPath(cfg) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { width, height } = style;
const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
const path = [
[ 'M', 0, -height / 2 ], // 上部顶点
[ 'L', width / 2, 0 ], // 右侧点
@ -160,35 +166,22 @@ Shape.registerNode('diamond', {
];
return path;
},
/**
* 获取节点宽高
* @internal 返回节点的大小 [width, height] 的方式维护
* @param {Object} cfg 节点的配置项
* @return {Array} 宽高
*/
getSize(cfg) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { width, height } = style;
return [ width, height ];
},
update(cfg, item) {
const group = item.getContainer();
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default || {};
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, icon: defaultIcon, labelCfg: defaultLabelCfg } = this.options;
const { style: customStyle, icon: customIcon, labelCfg: customLabelCfg } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const icon = deepMix({}, defaultIcon, customIcon, cfg.icon);
const { width, height, icon, linkPoints, labelCfg: defaultLabelCfg, ...diamondStyle } = style;
const keyShape = item.get('keyShape');
const path = this.getPath(cfg);
keyShape.attr({
path,
...diamondStyle
...style
});
const labelCfg = deepMix({}, defaultLabelCfg, cfg.labelCfg);
const labelCfg = deepMix({}, defaultLabelCfg, customLabelCfg, cfg.labelCfg);
const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
const text = group.findByClassName('node-label');
@ -208,52 +201,69 @@ Shape.registerNode('diamond', {
});
}
const { size, fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.updateLinkPoints(cfg, group);
},
/**
* 更新linkPoints
* @param {Object} cfg 节点数据配置项
* @param {Group} group Item所在的group
*/
updateLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const anchorLeft = group.findByClassName('diamond-anchor-left');
if (anchorLeft) {
anchorLeft.attr({
const { size: markSize, fill: markFill, stroke: markStroke, lineWidth: borderWidth } = linkPoints;
const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
const markLeft = group.findByClassName('diamond-mark-left');
if (markLeft) {
markLeft.attr({
x: -width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorRight = group.findByClassName('diamond-anchor-right');
if (anchorRight) {
anchorRight.attr({
const markRight = group.findByClassName('diamond-mark-right');
if (markRight) {
markRight.attr({
x: width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorTop = group.findByClassName('diamond-anchor-top');
if (anchorTop) {
anchorTop.attr({
const markTop = group.findByClassName('diamond-mark-top');
if (markTop) {
markTop.attr({
x: 0,
y: -height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorBottom = group.findByClassName('diamond-anchor-bottom');
if (anchorBottom) {
anchorBottom.attr({
const markBottom = group.findByClassName('diamond-mark-bottom');
if (markBottom) {
markBottom.attr({
x: 0,
y: height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}

View File

@ -7,68 +7,78 @@ const deepMix = require('@antv/util/lib/deep-mix');
Shape.registerNode('ellipse', {
// 自定义节点时的配置
options: {
// 默认配置
default: {
rx: 60,
ry: 30,
stroke: '#69c0ff',
fill: '#e6f7ff',
lineWidth: 1,
size: [ 60, 30 ],
style: {
x: 0,
y: 0,
// 文本样式配置
labelCfg: {
style: {
fill: '#595959'
}
},
// 节点中icon配置
icon: {
// 是否显示icon值为 false 则不渲染icon
show: false,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 36,
height: 36
},
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A'
},
// 连接点,默认为左右
anchorPoints: [[ 0, 0.5 ], [ 1, 0.5 ]]
stroke: '#87e8de',
fill: '#36cfc9',
lineWidth: 1
},
// 鼠标hover状态下的配置
hover: {
lineWidth: 3
// 文本样式配置
labelCfg: {
style: {
fill: '#595959'
}
},
// 选中节点状态下的配置
select: {
lineWidth: 5
}
stateStyles: {
// 鼠标hover状态下的配置
hover: {
lineWidth: 3
},
// 选中节点状态下的配置
select: {
lineWidth: 5
}
},
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A'
},
// 节点中icon配置
icon: {
// 是否显示icon值为 false 则不渲染icon
show: false,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 36,
height: 36
},
// 连接点,默认为左右
anchorPoints: [[ 0, 0.5 ], [ 1, 0.5 ]]
},
shapeType: 'ellipse',
// 文本位置
labelPosition: 'center',
drawShape(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
// const customStyle = this.getCustomConfig(cfg) || {};
// const defaultConfig = customStyle.default;
// const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
// 使用用户配置的size大小
const { linkPoints, icon, ...ellipseStyle } = style;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, icon: defaultIcon } = this.options;
const { style: customStyle, icon: customIcon } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const icon = deepMix({}, defaultIcon, customIcon, cfg.icon);
const size = this.getSize(cfg);
const rx = size[0];
const ry = size[1];
const { rx, ry } = ellipseStyle;
const keyShape = group.addShape('ellipse', {
attrs: ellipseStyle
attrs: {
...style,
rx,
ry
}
});
const { width, height, show } = icon;
@ -85,20 +95,37 @@ Shape.registerNode('ellipse', {
image.set('capture', false);
}
const { top, left, right, bottom, size,
fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.drawLinkPoints(cfg, group);
return keyShape;
},
/**
* 绘制节点上的LinkPoints
* @param {Object} cfg data数据配置项
* @param {Group} group Group实例
*/
drawLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const { top, left, right, bottom, size: markSize,
...markStyle } = linkPoints;
const size = this.getSize(cfg);
const rx = size[0];
const ry = size[1];
if (left) {
// left circle
group.addShape('circle', {
attrs: {
...markStyle,
x: -rx,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'ellipse-anchor-left'
className: 'ellipse-mark-left'
});
}
@ -106,14 +133,12 @@ Shape.registerNode('ellipse', {
// right circle
group.addShape('circle', {
attrs: {
...markStyle,
x: rx,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'ellipse-anchor-right'
className: 'ellipse-mark-right'
});
}
@ -121,14 +146,12 @@ Shape.registerNode('ellipse', {
// top circle
group.addShape('circle', {
attrs: {
...markStyle,
x: 0,
y: -ry,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'ellipse-anchor-top'
className: 'ellipse-mark-top'
});
}
@ -136,47 +159,44 @@ Shape.registerNode('ellipse', {
// bottom circle
group.addShape('circle', {
attrs: {
...markStyle,
x: 0,
y: ry,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'ellipse-anchor-bottom'
className: 'ellipse-mark-bottom'
});
}
return keyShape;
},
/**
* 获取节点宽高
* @internal 返回节点的大小 [width, height] 的方式维护
* @param {Object} cfg 节点的配置项
* @return {Array} 宽高
*/
getSize(cfg) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { rx, ry } = style;
return [ 2 * rx, 2 * ry ];
},
update(cfg, item) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default || {};
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { icon, linkPoints, labelCfg: defaultLabelCfg, ...ellipseStyle } = style;
// const customStyle = this.getCustomConfig(cfg) || {};
// const defaultConfig = customStyle.default || {};
// const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
// const { icon, linkPoints, labelCfg: defaultLabelCfg, ...ellipseStyle } = style;
// const { rx, ry } = ellipseStyle;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, icon: defaultIcon, labelCfg: defaultLabelCfg } = this.options;
const { style: customStyle, icon: customIcon, labelCfg: customLabelCfg } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const icon = deepMix({}, defaultIcon, customIcon, cfg.icon);
const size = this.getSize(cfg);
const rx = size[0];
const ry = size[1];
const { rx, ry } = ellipseStyle;
const keyShape = item.get('keyShape');
keyShape.attr(ellipseStyle);
keyShape.attr({
...style,
rx,
ry
});
const group = item.getContainer();
const labelCfg = deepMix({}, defaultLabelCfg, cfg.labelCfg);
const labelCfg = deepMix({}, defaultLabelCfg, customLabelCfg, cfg.labelCfg);
const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
const text = group.findByClassName('node-label');
if (text) {
@ -195,53 +215,62 @@ Shape.registerNode('ellipse', {
});
}
const { size, fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.updateLinkPoints(cfg, group);
},
/**
* 更新linkPoints
* @param {Object} cfg 节点数据配置项
* @param {Group} group Item所在的group
*/
updateLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const anchorLeft = group.findByClassName('ellipse-anchor-left');
if (anchorLeft) {
anchorLeft.attr({
const { size: markSize, ...markStyles } = linkPoints;
const size = this.getSize(cfg);
const rx = size[0];
const ry = size[1];
const markLeft = group.findByClassName('ellipse-mark-left');
if (markLeft) {
markLeft.attr({
...markStyles,
x: -rx,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
const anchorRight = group.findByClassName('ellipse-anchor-right');
if (anchorRight) {
anchorRight.attr({
const markRight = group.findByClassName('ellipse-mark-right');
if (markRight) {
markRight.attr({
...markStyles,
x: rx,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
const anchorTop = group.findByClassName('ellipse-anchor-top');
if (anchorTop) {
anchorTop.attr({
const markTop = group.findByClassName('ellipse-mark-top');
if (markTop) {
markTop.attr({
...markStyles,
x: 0,
y: -ry,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
const anchorBottom = group.findByClassName('ellipse-anchor-bottom');
if (anchorBottom) {
anchorBottom.attr({
const markBottom = group.findByClassName('ellipse-mark-bottom');
if (markBottom) {
markBottom.attr({
...markStyles,
x: 0,
y: ry,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
}

View File

@ -6,95 +6,98 @@ Shape.registerNode('modelRect', {
// labelPosition: 'center',
// 自定义节点时的配置
options: {
// 默认配置
default: {
width: 185,
height: 70,
size: [ 185, 70 ],
style: {
radius: 5,
stroke: '#69c0ff',
fill: '#ffffff',
lineWidth: 1,
fillOpacity: 1,
// 文本样式配置
labelCfg: {
style: {
fill: '#595959',
fontSize: 14
},
offset: 30
},
preRect: {
show: true,
width: 4,
fill: '#40a9ff',
radius: 2
},
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A'
},
// 节点中icon配置
logoIcon: {
// 是否显示icon值为 false 则不渲染icon
show: true,
x: 0,
y: 0,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/4f81893c-1806-4de4-aff3-9a6b266bc8a2.svg',
width: 16,
height: 16
},
// 节点中表示状态的icon配置
stateIcon: {
// 是否显示icon值为 false 则不渲染icon
show: true,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/300a2523-67e0-4cbf-9d4a-67c077b40395.svg',
width: 16,
height: 16
},
// 连接点,默认为左右
anchorPoints: [[ 0, 0.5 ], [ 1, 0.5 ]]
fillOpacity: 1
},
// hover状态下的配置
hover: {
lineWidth: 2,
stroke: '#1890ff',
fill: '#e6f7ff'
// 文本样式配置
labelCfg: {
style: {
fill: '#595959',
fontSize: 14
},
offset: 30
},
// 节点选中状态下的配置
select: {
lineWidth: 3,
stroke: '#1890ff',
fill: '#e6f7ff'
}
stateStyles: {
// hover状态下的配置
hover: {
lineWidth: 2,
stroke: '#1890ff',
fill: '#e6f7ff'
},
// 节点选中状态下的配置
select: {
lineWidth: 3,
stroke: '#1890ff',
fill: '#e6f7ff'
}
},
preRect: {
show: true,
width: 4,
fill: '#40a9ff',
radius: 2
},
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A'
},
// 节点中icon配置
logoIcon: {
// 是否显示icon值为 false 则不渲染icon
show: true,
x: 0,
y: 0,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/4f81893c-1806-4de4-aff3-9a6b266bc8a2.svg',
width: 16,
height: 16
},
// 节点中表示状态的icon配置
stateIcon: {
// 是否显示icon值为 false 则不渲染icon
show: true,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/300a2523-67e0-4cbf-9d4a-67c077b40395.svg',
width: 16,
height: 16
},
// 连接点,默认为左右
anchorPoints: [[ 0, 0.5 ], [ 1, 0.5 ]]
},
shapeType: 'modelRect',
drawShape(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { width, height, linkPoints, preRect,
logoIcon, stateIcon, ...rectStyle } = style;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, preRect: defaultPreRect } = this.options;
const { style: customStyle, preRect: customPreRect } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
const keyShape = group.addShape('rect', {
attrs: {
...style,
x: -width / 2,
y: -height / 2,
width,
height,
...rectStyle
height
}
});
const preRect = deepMix({}, defaultPreRect, customPreRect, cfg.preRect);
const { show: preRectShow, ...preRectStyle } = preRect;
if (preRectShow) {
group.addShape('rect', {
@ -108,6 +111,26 @@ Shape.registerNode('modelRect', {
});
}
this.drawLogoIcon(cfg, group);
this.drawStateIcon(cfg, group);
this.drawLinkPoints(cfg, group);
return keyShape;
},
/**
* 绘制模型矩形左边的logo图标
* @param {Object} cfg 数据配置项
* @param {Group} group Group实例
*/
drawLogoIcon(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { logoIcon: defaultLogoIcon } = this.options;
const { logoIcon: customLogoIcon } = customOptions;
const logoIcon = deepMix({}, defaultLogoIcon, customLogoIcon, cfg.logoIcon);
const size = this.getSize(cfg);
const width = size[0];
if (logoIcon.show) {
const { width: w, height: h, x, y, ...logoIconStyle } = logoIcon;
const image = group.addShape('image', {
@ -115,14 +138,25 @@ Shape.registerNode('modelRect', {
...logoIconStyle,
x: x || -width / 2 + w,
y: y || -h / 2
// width: w,
// height: h
},
className: 'rect-logo-icon'
});
image.set('capture', false);
}
},
/**
* 绘制模型矩形右边的状态图标
* @param {Object} cfg 数据配置项
* @param {Group} group Group实例
*/
drawStateIcon(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { stateIcon: defaultStateIcon } = this.options;
const { stateIcon: customStateIcon } = customOptions;
const stateIcon = deepMix({}, defaultStateIcon, customStateIcon, cfg.stateIcon);
const size = this.getSize(cfg);
const width = size[0];
if (stateIcon.show) {
const { width: w, height: h, x, y } = stateIcon;
@ -137,21 +171,34 @@ Shape.registerNode('modelRect', {
image.set('capture', false);
}
},
/**
* 绘制节点上的LinkPoints
* @param {Object} cfg data数据配置项
* @param {Group} group Group实例
*/
drawLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const { top, left, right, bottom, size: markSize,
...markStyle } = linkPoints;
const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
const { top, left, right, bottom, size,
fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
if (left) {
// left circle
group.addShape('circle', {
attrs: {
...markStyle,
x: -width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'rect-anchor-left'
className: 'rect-mark-left'
});
}
@ -159,14 +206,12 @@ Shape.registerNode('modelRect', {
// right circle
group.addShape('circle', {
attrs: {
...markStyle,
x: width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'rect-anchor-right'
className: 'rect-mark-right'
});
}
@ -174,14 +219,12 @@ Shape.registerNode('modelRect', {
// top circle
group.addShape('circle', {
attrs: {
...markStyle,
x: 0,
y: -height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'rect-anchor-top'
className: 'rect-mark-top'
});
}
@ -189,26 +232,29 @@ Shape.registerNode('modelRect', {
// bottom circle
group.addShape('circle', {
attrs: {
...markStyle,
x: 0,
y: height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'rect-anchor-bottom'
className: 'rect-mark-bottom'
});
}
return keyShape;
},
drawLabel(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default || {};
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const labelCfg = deepMix({}, style.labelCfg, cfg.labelCfg);
const customOptions = this.getCustomConfig(cfg) || {};
const { labelCfg: defaultLabelCfg, logoIcon: defaultLogoIcon } = this.options;
const { labelCfg: customLabelCfg, logoIcon: customLogoIcon } = customOptions;
const logoIcon = deepMix({}, defaultLogoIcon, customLogoIcon, cfg.logoIcon);
const labelCfg = deepMix({}, defaultLabelCfg, customLabelCfg, cfg.labelCfg);
const size = this.getSize(cfg);
const width = size[0];
let label = null;
const { logoIcon, width } = style;
const { show, width: w } = logoIcon;
let offsetX = -width / 2 + labelCfg.offset;
@ -223,13 +269,13 @@ Shape.registerNode('modelRect', {
...fontStyle,
y: -5,
x: offsetX,
text: Util.fittingString(cfg.label, 70, 14)
text: Util.fittingString(cfg.label, 100, 14)
}
});
group.addShape('text', {
attrs: {
text: Util.fittingString(cfg.description, 80, 12),
text: Util.fittingString(cfg.description, 75, 12),
fontSize: 12,
x: offsetX,
y: 17,
@ -249,40 +295,39 @@ Shape.registerNode('modelRect', {
}
return label;
},
getAnchorPoints() {
const defaultOptions = this.options;
return defaultOptions.anchorPoints;
},
getSize(cfg) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { width, height } = style;
return [ width, height ];
getAnchorPoints(cfg) {
const customOptions = this.getCustomConfig(cfg) || {};
const { anchorPoints: defaultAnchorPoints } = this.options;
const { anchorPoints: customAnchorPoints } = customOptions;
const anchorPoints = deepMix({}, defaultAnchorPoints, customAnchorPoints);
return anchorPoints;
},
update(cfg, item) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { width, height, linkPoints, logoIcon, stateIcon,
labelCfg: defaultLabelCfg, ...rectStyle } = style;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, labelCfg: defaultLabelCfg, preRect: defaultPreRect,
logoIcon: defaultLogoIcon, stateIcon: defaultStateIcon } = this.options;
const { style: customStyle, labelCfg: customLabelCfg, preRect: customPreRect,
logoIcon: customLogoIcon, stateIcon: customStateIcon } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
const keyShape = item.get('keyShape');
keyShape.attr({
...style,
x: -width / 2,
y: -height / 2,
width,
height,
...rectStyle
height
});
const group = item.getContainer();
const labelCfg = deepMix({}, defaultLabelCfg, cfg.labelCfg);
// const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
const labelCfg = deepMix({}, defaultLabelCfg, customLabelCfg, cfg.labelCfg);
const text = group.findByClassName('node-label');
const logoIcon = deepMix({}, defaultLogoIcon, customLogoIcon, cfg.logoIcon);
const { show, width: w } = logoIcon;
const { offset, style: fontStyle } = labelCfg;
@ -318,7 +363,9 @@ Shape.registerNode('modelRect', {
const preRectShape = group.findByClassName('pre-rect');
if (preRectShape) {
const preRect = deepMix({}, defaultPreRect, customPreRect, cfg.preRect);
preRectShape.attr({
...preRect,
x: -width / 2,
y: -height / 2,
height
@ -327,8 +374,9 @@ Shape.registerNode('modelRect', {
const logoIconShape = group.findByClassName('rect-logo-icon');
if (logoIconShape) {
const { width: w, height: h, x, y } = logoIcon;
const { width: w, height: h, x, y, ...logoIconStyle } = logoIcon;
logoIconShape.attr({
...logoIconStyle,
x: x || -width / 2 + w,
y: y || -h / 2,
width: w,
@ -338,8 +386,10 @@ Shape.registerNode('modelRect', {
const stateIconShape = group.findByClassName('rect-state-icon');
if (stateIconShape) {
const { width: w, height: h, x, y } = stateIcon;
const stateIcon = deepMix({}, defaultStateIcon, customStateIcon, cfg.stateIcon);
const { width: w, height: h, x, y, ...stateIconStyle } = stateIcon;
stateIconShape.attr({
...stateIconStyle,
x: x || width / 2 - w * 2 + 8,
y: y || -h / 2,
width: w,
@ -347,52 +397,69 @@ Shape.registerNode('modelRect', {
});
}
const { size, fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.updateLinkPoints(cfg, group);
},
/**
* 更新linkPoints
* @param {Object} cfg 节点数据配置项
* @param {Group} group Item所在的group
*/
updateLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const anchorLeft = group.findByClassName('rect-anchor-left');
if (anchorLeft) {
anchorLeft.attr({
const { size: markSize, fill: markFill, stroke: markStroke, lineWidth: borderWidth } = linkPoints;
const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
const markLeft = group.findByClassName('rect-mark-left');
if (markLeft) {
markLeft.attr({
x: -width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorRight = group.findByClassName('rect-anchor-right');
if (anchorRight) {
anchorRight.attr({
const markRight = group.findByClassName('rect-mark-right');
if (markRight) {
markRight.attr({
x: width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorTop = group.findByClassName('rect-anchor-top');
if (anchorTop) {
anchorTop.attr({
const markTop = group.findByClassName('rect-mark-top');
if (markTop) {
markTop.attr({
x: 0,
y: -height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorBottom = group.findByClassName('rect-anchor-bottom');
if (anchorBottom) {
anchorBottom.attr({
const markBottom = group.findByClassName('rect-mark-bottom');
if (markBottom) {
markBottom.attr({
x: 0,
y: height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}

View File

@ -4,81 +4,100 @@ const deepMix = require('@antv/util/lib/deep-mix');
Shape.registerNode('rect', {
// 自定义节点时的配置
options: {
// 默认配置
default: {
width: 100,
height: 30,
size: [ 100, 30 ],
style: {
radius: 0,
stroke: '#69c0ff',
fill: '#e6f7ff',
stroke: '#87e8de',
fill: '#36cfc9',
lineWidth: 1,
fillOpacity: 1,
// 文本样式配置
labelCfg: {
style: {
fill: '#595959',
fontSize: 12
}
},
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A'
},
// 连接点,默认为左右
anchorPoints: [[ 0, 0.5 ], [ 1, 0.5 ]]
fillOpacity: 1
},
// hover状态下的配置
hover: {
lineWidth: 2,
stroke: '#1890ff'
// 文本样式配置
labelCfg: {
style: {
fill: '#595959',
fontSize: 12
}
},
// 节点选中状态下的配置
select: {
lineWidth: 3,
stroke: '#1890ff',
fill: '#91d5ff'
}
stateStyles: {
// hover状态下的配置
hover: {
lineWidth: 2,
stroke: '#1890ff'
},
// 节点选中状态下的配置
select: {
lineWidth: 3,
stroke: '#1890ff',
fill: '#91d5ff'
}
},
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#72CC4A',
stroke: '#72CC4A'
},
// 连接点,默认为左右
markPoints: [[ 0, 0.5 ], [ 1, 0.5 ]]
},
shapeType: 'rect',
drawShape(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { width, height, linkPoints, ...rectStyle } = style;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle } = this.options;
const { style: customStyle } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
const keyShape = group.addShape('rect', {
attrs: {
...style,
x: -width / 2,
y: -height / 2,
width,
height,
...rectStyle
height
},
className: 'rect-keyShape'
});
const { top, left, right, bottom, size,
fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.drawLinkPoints(cfg, group);
return keyShape;
},
/**
* 绘制节点上的LinkPoints
* @param {Object} cfg data数据配置项
* @param {Group} group Group实例
*/
drawLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const { top, left, right, bottom, size: markSize,
...markStyle } = linkPoints;
const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
if (left) {
// left circle
group.addShape('circle', {
attrs: {
...markStyle,
x: -width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'rect-anchor-left'
className: 'rect-mark-left'
});
}
@ -86,14 +105,12 @@ Shape.registerNode('rect', {
// right circle
group.addShape('circle', {
attrs: {
...markStyle,
x: width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'rect-anchor-right'
className: 'rect-mark-right'
});
}
@ -101,14 +118,12 @@ Shape.registerNode('rect', {
// top circle
group.addShape('circle', {
attrs: {
...markStyle,
x: 0,
y: -height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'rect-anchor-top'
className: 'rect-mark-top'
});
}
@ -116,35 +131,30 @@ Shape.registerNode('rect', {
// bottom circle
group.addShape('circle', {
attrs: {
...markStyle,
x: 0,
y: height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'rect-anchor-bottom'
className: 'rect-mark-bottom'
});
}
return keyShape;
},
getAnchorPoints() {
const defaultOptions = this.options;
return defaultOptions.anchorPoints;
},
getSize(cfg) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { width, height } = style;
return [ width, height ];
getAnchorPoints(cfg) {
const customOptions = this.getCustomConfig(cfg) || {};
const { anchorPoints: defaultAnchorPoints } = this.options;
const { anchorPoints: customAnchorPoints } = customOptions;
const anchorPoints = deepMix({}, defaultAnchorPoints, customAnchorPoints);
return anchorPoints;
},
update(cfg, item) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { width, height, linkPoints, labelCfg: defaultLabelCfg, ...rectStyle } = style;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, labelCfg: defaultLabelCfg } = this.options;
const { style: customStyle, labelCfg: customLabelCfg } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
const keyShape = item.get('keyShape');
keyShape.attr({
@ -152,12 +162,12 @@ Shape.registerNode('rect', {
y: -height / 2,
width,
height,
...rectStyle
...style
});
const group = item.getContainer();
const labelCfg = deepMix({}, defaultLabelCfg, cfg.labelCfg);
const labelCfg = deepMix({}, defaultLabelCfg, customLabelCfg, cfg.labelCfg);
const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
const text = group.findByClassName('node-label');
if (text) {
@ -165,53 +175,69 @@ Shape.registerNode('rect', {
...labelStyle
});
}
this.updateLinkPoints(cfg, group);
},
/**
* 更新linkPoints
* @param {Object} cfg 节点数据配置项
* @param {Group} group Item所在的group
*/
updateLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const { size, fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
const { size: markSize, fill: markFill, stroke: markStroke, lineWidth: borderWidth } = linkPoints;
const anchorLeft = group.findByClassName('rect-anchor-left');
if (anchorLeft) {
anchorLeft.attr({
const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
const markLeft = group.findByClassName('rect-mark-left');
if (markLeft) {
markLeft.attr({
x: -width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorRight = group.findByClassName('rect-anchor-right');
if (anchorRight) {
anchorRight.attr({
const markRight = group.findByClassName('rect-mark-right');
if (markRight) {
markRight.attr({
x: width / 2,
y: 0,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorTop = group.findByClassName('rect-anchor-top');
if (anchorTop) {
anchorTop.attr({
const markTop = group.findByClassName('rect-mark-top');
if (markTop) {
markTop.attr({
x: 0,
y: -height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}
const anchorBottom = group.findByClassName('rect-anchor-bottom');
if (anchorBottom) {
anchorBottom.attr({
const markBottom = group.findByClassName('rect-mark-bottom');
if (markBottom) {
markBottom.attr({
x: 0,
y: height / 2,
r: size,
fill: anchorFill,
stroke: anchorStroke,
r: markSize,
fill: markFill,
stroke: markStroke,
lineWidth: borderWidth
});
}

View File

@ -1,69 +1,71 @@
const Shape = require('../shape');
const deepMix = require('@antv/util/lib/deep-mix');
// 菱形shape
// 五角星shape
Shape.registerNode('star', {
// 自定义节点时的配置
options: {
// 默认配置
default: {
outerR: 60,
innerR: 20,
stroke: '#69c0ff',
fill: '#e6f7ff',
lineWidth: 1,
// 文本样式配置
labelCfg: {
style: {
fill: '#595959'
},
offset: 0
size: [ 20, 60 ],
style: {
stroke: '#87e8de',
fill: '#36cfc9',
lineWidth: 1
},
// 文本样式配置
labelCfg: {
style: {
fill: '#595959'
},
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
left: false,
leftBottom: false,
rightBottom: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#fff',
stroke: '#72CC4A'
offset: 0
},
stateStyles: {
// 鼠标hover状态下的配置
hover: {
lineWidth: 3
},
// 节点中icon配置
icon: {
// 是否显示icon值为 false 则不渲染icon
show: false,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 16,
height: 16
// 选中节点状态下的配置
select: {
lineWidth: 5
}
},
// 鼠标hover状态下的配置
hover: {
lineWidth: 3
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
left: false,
leftBottom: false,
rightBottom: false,
// circle的大小
size: 3,
lineWidth: 1,
fill: '#fff',
stroke: '#72CC4A'
},
// 选中节点状态下的配置
select: {
lineWidth: 5
// 节点中icon配置
icon: {
// 是否显示icon值为 false 则不渲染icon
show: false,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 16,
height: 16
}
},
shapeType: 'star',
// 文本位置
labelPosition: 'center',
drawShape(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { icon, linkPoints, outerR, ...starStyle } = style;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, icon: defaultIcon } = this.options;
const { style: customStyle, icon: customIcon } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const icon = deepMix({}, defaultIcon, customIcon, cfg.icon);
const path = this.getPath(cfg);
const keyShape = group.addShape('path', {
attrs: {
path,
...starStyle
...style
}
});
@ -81,8 +83,25 @@ Shape.registerNode('star', {
image.set('capture', false);
}
const { top, left, right, leftBottom, rightBottom, size,
fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.drawLinkPoints(cfg, group);
return keyShape;
},
/**
* 绘制节点上的LinkPoints
* @param {Object} cfg data数据配置项
* @param {Group} group Group实例
*/
drawLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const { top, left, right, leftBottom, rightBottom, size: markSize,
...markStyle } = linkPoints;
const size = this.getSize(cfg);
const outerR = size[1];
if (right) {
// right circle
@ -92,14 +111,12 @@ Shape.registerNode('star', {
group.addShape('circle', {
attrs: {
...markStyle,
x: x1,
y: -y1,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'star-anchor-right'
className: 'star-mark-right'
});
}
@ -111,14 +128,12 @@ Shape.registerNode('star', {
// top circle
group.addShape('circle', {
attrs: {
...markStyle,
x: x1,
y: -y1,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'star-anchor-top'
className: 'star-mark-top'
});
}
@ -130,14 +145,12 @@ Shape.registerNode('star', {
// left circle
group.addShape('circle', {
attrs: {
...markStyle,
x: x1,
y: -y1,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'star-anchor-left'
className: 'star-mark-left'
});
}
@ -149,14 +162,12 @@ Shape.registerNode('star', {
// left bottom circle
group.addShape('circle', {
attrs: {
...markStyle,
x: x1,
y: -y1,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'star-anchor-left-bottom'
className: 'star-mark-left-bottom'
});
}
@ -168,27 +179,19 @@ Shape.registerNode('star', {
// left bottom circle
group.addShape('circle', {
attrs: {
...markStyle,
x: x1,
y: -y1,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'star-anchor-right-bottom'
className: 'star-mark-right-bottom'
});
}
return keyShape;
},
shouldUpdate() {
return false;
},
getPath(cfg) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { outerR, innerR } = style;
const size = this.getSize(cfg);
const innerR = size[0];
const outerR = size[1];
const path = [];
for (let i = 0; i < 5; i++) {
const x1 = Math.cos((18 + 72 * i) / 180 * Math.PI) * outerR;
@ -216,36 +219,22 @@ Shape.registerNode('star', {
return path;
},
/**
* 获取节点宽高
* @internal 返回节点的大小 [width, height] 的方式维护
* @param {Object} cfg 节点的配置项
* @return {Array} 宽高
*/
getSize(cfg) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { outerR } = style;
return [ outerR, outerR ];
},
update(cfg, item) {
const group = item.getContainer();
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default || {};
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, icon: defaultIcon, labelCfg: defaultLabelCfg } = this.options;
const { style: customStyle, icon: customIcon, labelCfg: customLabelCfg } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const icon = deepMix({}, defaultIcon, customIcon, cfg.icon);
const { icon, linkPoints, outerR,
labelCfg: defaultLabelCfg, ...starStyle } = style;
const keyShape = item.get('keyShape');
const path = this.getPath(cfg);
keyShape.attr({
path,
...starStyle
...style
});
const labelCfg = deepMix({}, defaultLabelCfg, cfg.labelCfg);
const labelCfg = deepMix({}, defaultLabelCfg, customLabelCfg, cfg.labelCfg);
const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
const text = group.findByClassName('node-label');
@ -265,84 +254,90 @@ Shape.registerNode('star', {
});
}
const { size, fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.updateLinkPoints(cfg, group);
},
/**
* 更新linkPoints
* @param {Object} cfg 节点数据配置项
* @param {Group} group Item所在的group
*/
updateLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints } = this.options;
const { linkPoints: customLinkPoints } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const anchorRight = group.findByClassName('star-anchor-right');
if (anchorRight) {
const { size: markSize, ...markStyle } = linkPoints;
const size = this.getSize(cfg);
const outerR = size[1];
const markRight = group.findByClassName('star-mark-right');
if (markRight) {
const x = Math.cos((18 + 72 * 0) / 180 * Math.PI) * outerR;
const y = Math.sin((18 + 72 * 0) / 180 * Math.PI) * outerR;
anchorRight.attr({
markRight.attr({
...markStyle,
x,
y: -y,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
const anchorTop = group.findByClassName('star-anchor-top');
if (anchorTop) {
const markTop = group.findByClassName('star-mark-top');
if (markTop) {
const x = Math.cos((18 + 72 * 1) / 180 * Math.PI) * outerR;
const y = Math.sin((18 + 72 * 1) / 180 * Math.PI) * outerR;
// top circle
anchorTop.attr({
markTop.attr({
...markStyle,
x,
y: -y,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
const anchorLeft = group.findByClassName('star-anchor-left');
if (anchorLeft) {
const markLeft = group.findByClassName('star-mark-left');
if (markLeft) {
const x = Math.cos((18 + 72 * 2) / 180 * Math.PI) * outerR;
const y = Math.sin((18 + 72 * 2) / 180 * Math.PI) * outerR;
// left circle
anchorLeft.attr({
markLeft.attr({
...markStyle,
x,
y: -y,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
const anchorLeftBottom = group.findByClassName('star-anchor-left-bottom');
if (anchorLeftBottom) {
const markLeftBottom = group.findByClassName('star-mark-left-bottom');
if (markLeftBottom) {
const x = Math.cos((18 + 72 * 3) / 180 * Math.PI) * outerR;
const y = Math.sin((18 + 72 * 3) / 180 * Math.PI) * outerR;
// bottom circle
anchorLeftBottom.attr({
markLeftBottom.attr({
...markStyle,
x,
y: -y,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
const anchorRightBottom = group.findByClassName('star-anchor-right-bottom');
if (anchorRightBottom) {
const markRightBottom = group.findByClassName('star-mark-right-bottom');
if (markRightBottom) {
const x = Math.cos((18 + 72 * 4) / 180 * Math.PI) * outerR;
const y = Math.sin((18 + 72 * 4) / 180 * Math.PI) * outerR;
// bottom circle
anchorRightBottom.attr({
markRightBottom.attr({
...markStyle,
x,
y: -y,
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
}

View File

@ -5,66 +5,69 @@ const deepMix = require('@antv/util/lib/deep-mix');
Shape.registerNode('triangle', {
// 自定义节点时的配置
options: {
// 默认配置
default: {
len: 40,
direction: 'up',
stroke: '#69c0ff',
fill: '#e6f7ff',
lineWidth: 1,
// 文本样式配置
labelCfg: {
style: {
fill: '#595959'
},
offset: 15
size: 40,
direction: 'up',
style: {
stroke: '#87e8de',
fill: '#36cfc9',
lineWidth: 1
},
// 文本样式配置
labelCfg: {
style: {
fill: '#595959'
},
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 5,
lineWidth: 1,
fill: '#fff',
stroke: '#72CC4A'
offset: 15
},
stateStyles: {
// 鼠标hover状态下的配置
hover: {
lineWidth: 3
},
// 节点中icon配置
icon: {
// 是否显示icon值为 false 则不渲染icon
show: false,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 16,
height: 16,
offset: 0
// 选中节点状态下的配置
select: {
lineWidth: 5
}
},
// 鼠标hover状态下的配置
hover: {
lineWidth: 3
// 节点上左右上下四个方向上的链接circle配置
linkPoints: {
top: false,
right: false,
bottom: false,
left: false,
// circle的大小
size: 5,
lineWidth: 1,
fill: '#fff',
stroke: '#72CC4A'
},
// 选中节点状态下的配置
select: {
lineWidth: 5
// 节点中icon配置
icon: {
// 是否显示icon值为 false 则不渲染icon
show: false,
// icon的地址字符串类型
img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
width: 16,
height: 16,
offset: 6
}
},
shapeType: 'circle',
shapeType: 'triangle',
// 文本位置
labelPosition: 'bottom',
drawShape(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { icon, linkPoints,
direction, len, ...triangleStyle } = style;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, icon: defaultIcon, direction: defaultDirection } = this.options;
const { style: customStyle, icon: customIcon, direction: customDirection } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const icon = deepMix({}, defaultIcon, customIcon, cfg.icon);
const direction = cfg.direction || customDirection || defaultDirection;
const path = this.getPath(cfg);
const keyShape = group.addShape('path', {
attrs: {
path,
...triangleStyle
...style
}
});
@ -90,8 +93,28 @@ Shape.registerNode('triangle', {
image.set('capture', false);
}
const { top, left, right, bottom, size,
fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.drawLinkPoints(cfg, group);
return keyShape;
},
/**
* 绘制节点上的LinkPoints
* @param {Object} cfg data数据配置项
* @param {Group} group Group实例
*/
drawLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints, direction: defaultDirection } = this.options;
const { linkPoints: customLinkPoints, direction: customDirection } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const direction = cfg.direction || customDirection || defaultDirection;
const { top, left, right, bottom, size: markSize,
...markStyle } = linkPoints;
const size = this.getSize(cfg);
const len = size[0];
if (left) {
// up down left right 四个方向的坐标均不相同
let leftPos = null;
@ -109,14 +132,12 @@ Shape.registerNode('triangle', {
// left circle
group.addShape('circle', {
attrs: {
...markStyle,
x: leftPos[0],
y: leftPos[1],
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'triangle-anchor-left'
className: 'triangle-mark-left'
});
}
}
@ -138,14 +159,12 @@ Shape.registerNode('triangle', {
if (rightPos) {
group.addShape('circle', {
attrs: {
...markStyle,
x: rightPos[0],
y: rightPos[1],
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'triangle-anchor-right'
className: 'triangle-mark-right'
});
}
}
@ -167,14 +186,12 @@ Shape.registerNode('triangle', {
// top circle
group.addShape('circle', {
attrs: {
...markStyle,
x: topPos[0],
y: topPos[1],
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'triangle-anchor-top'
className: 'triangle-mark-top'
});
}
}
@ -196,28 +213,25 @@ Shape.registerNode('triangle', {
// bottom circle
group.addShape('circle', {
attrs: {
...markStyle,
x: bottomPos[0],
y: bottomPos[1],
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
},
className: 'triangle-anchor-bottom'
className: 'triangle-mark-bottom'
});
}
}
return keyShape;
},
shouldUpdate() {
return false;
},
getPath(cfg) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { len, direction } = style;
const customOptions = this.getCustomConfig(cfg) || {};
const { direction: defaultDirection } = this.options;
const { direction: customDirection } = customOptions;
const direction = cfg.direction || customDirection || defaultDirection;
const size = this.getSize(cfg);
const len = size[0];
const diffY = len * Math.sin((1 / 3) * Math.PI);
const r = len * Math.sin((1 / 3) * Math.PI);
let path = [
@ -251,36 +265,21 @@ Shape.registerNode('triangle', {
}
return path;
},
/**
* 获取节点宽高
* @internal 返回节点的大小 [width, height] 的方式维护
* @param {Object} cfg 节点的配置项
* @return {Array} 宽高
*/
getSize(cfg) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { len } = style;
return [ len, len ];
},
update(cfg, item) {
const group = item.getContainer();
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default || {};
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { icon, linkPoints, direction, len,
labelCfg: defaultLabelCfg, ...triangleStyle } = style;
const customOptions = this.getCustomConfig(cfg) || {};
const { style: defaultStyle, icon: defaultIcon, labelCfg: defaultLabelCfg } = this.options;
const { style: customStyle, icon: customIcon, labelCfg: customLabelCfg } = customOptions;
const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const icon = deepMix({}, defaultIcon, customIcon, cfg.icon);
const keyShape = item.get('keyShape');
const path = this.getPath(cfg);
keyShape.attr({
path,
...triangleStyle
...style
});
const labelCfg = deepMix({}, defaultLabelCfg, cfg.labelCfg);
const labelCfg = deepMix({}, defaultLabelCfg, customLabelCfg, cfg.labelCfg);
const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
const text = group.findByClassName('node-label');
@ -300,10 +299,29 @@ Shape.registerNode('triangle', {
});
}
const { size, fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
this.updateLinkPoints(cfg, group);
},
/**
* 更新linkPoints
* @param {Object} cfg 节点数据配置项
* @param {Group} group Item所在的group
*/
updateLinkPoints(cfg, group) {
const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints: defaultLinkPoints, direction: defaultDirection } = this.options;
const { linkPoints: customLinkPoints, direction: customDirection } = customOptions;
const linkPoints = deepMix({}, defaultLinkPoints, customLinkPoints, cfg.linkPoints);
const anchorLeft = group.findByClassName('triangle-anchor-left');
if (anchorLeft) {
const direction = cfg.direction || customDirection || defaultDirection;
const { size: markSize, ...markStyle } = linkPoints;
const size = this.getSize(cfg);
const len = size[0];
const markLeft = group.findByClassName('triangle-mark-left');
if (markLeft) {
let leftPos = null;
const diffY = len * Math.sin((1 / 3) * Math.PI);
const r = len * Math.sin((1 / 3) * Math.PI);
@ -317,19 +335,17 @@ Shape.registerNode('triangle', {
if (leftPos) {
// left circle
anchorLeft.attr({
markLeft.attr({
...markStyle,
x: leftPos[0],
y: leftPos[1],
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
}
const anchorRight = group.findByClassName('triangle-anchor-right');
if (anchorRight) {
const markRight = group.findByClassName('triangle-mark-right');
if (markRight) {
let rightPos = null;
const diffY = len * Math.sin((1 / 3) * Math.PI);
const r = len * Math.sin((1 / 3) * Math.PI);
@ -342,19 +358,17 @@ Shape.registerNode('triangle', {
}
if (rightPos) {
anchorRight.attr({
markRight.attr({
...markStyle,
x: rightPos[0],
y: rightPos[1],
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
}
const anchorTop = group.findByClassName('triangle-anchor-top');
if (anchorTop) {
const markTop = group.findByClassName('triangle-mark-top');
if (markTop) {
let topPos = null;
const diffY = len * Math.sin((1 / 3) * Math.PI);
const r = len * Math.sin((1 / 3) * Math.PI);
@ -368,19 +382,17 @@ Shape.registerNode('triangle', {
if (topPos) {
// top circle
anchorTop.attr({
markTop.attr({
...markStyle,
x: topPos[0],
y: topPos[1],
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
}
const anchorBottom = group.findByClassName('triangle-anchor-bottom');
if (anchorBottom) {
const markBottom = group.findByClassName('triangle-mark-bottom');
if (markBottom) {
let bottomPos = null;
const diffY = len * Math.sin((1 / 3) * Math.PI);
const r = len * Math.sin((1 / 3) * Math.PI);
@ -395,13 +407,11 @@ Shape.registerNode('triangle', {
if (bottomPos) {
// bottom circle
anchorBottom.attr({
markBottom.attr({
...markStyle,
x: bottomPos[0],
y: bottomPos[1],
r: size,
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
r: markSize
});
}
}

View File

@ -40,10 +40,11 @@ const SingleShape = {
},
drawLabel(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default;
const style = merge({}, this.options.default, defaultConfig);
const labelCfg = merge({}, style.labelCfg, cfg.labelCfg);
const customOptions = this.getCustomConfig(cfg) || {};
const { labelCfg: defaultLabelCfg } = this.options;
const { labelCfg: customLabelCfg } = customOptions;
const labelCfg = merge({}, defaultLabelCfg, customLabelCfg, cfg.labelCfg);
const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
const label = group.addShape('text', {
attrs: labelStyle
@ -154,15 +155,23 @@ const SingleShape = {
* @return {object} 样式
*/
getStateStyle(name, value, item) {
const defaultStyle = this.options;
const model = item.getModel();
const customStyle = this.getCustomConfig(model) || {};
const customOptions = this.getCustomConfig(model) || {};
const { style: defaultStyle, stateStyles: defaultStateStyle } = this.options;
const { style: customStyle, stateStyles: customStateStyle } = customOptions;
const stateStyles = merge({}, defaultStateStyle, customStateStyle);
let currentStateStyle = defaultStyle;
if (stateStyles[name]) {
currentStateStyle = stateStyles[name];
}
if (value) {
return merge({}, get(defaultStyle, name, {}), get(customStyle, name, {}));
return currentStateStyle;
}
const states = item.getStates();
const resultStyle = merge({}, get(defaultStyle, 'default', {}), get(customStyle, 'default', {}));
const resultStyle = merge({}, defaultStyle, customStyle);
const style = cloneDeep(resultStyle);
states.forEach(state => {
merge(style, get(defaultStyle, state, {}), get(customStyle, state, {}));