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') }, 'triangle')
G6.registerNode('triangle2', { G6.registerNode('triangle2', {
labelPosition: 'center', labelPosition: 'center',
getCustomConfig() { getCustomConfig(cfg) {
return { return {
default: { default: {
direction: 'down', 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 EDGE = 'edge';
const CFG_PREFIX = 'default'; const CFG_PREFIX = 'default';
const MAPPER_SUFFIX = 'Mapper'; const MAPPER_SUFFIX = 'Mapper';
const STATE_SUFFIX = 'stateStyles';
const hasOwnProperty = Object.hasOwnProperty; const hasOwnProperty = Object.hasOwnProperty;
class ItemController { class ItemController {
@ -16,14 +17,14 @@ class ItemController {
const parent = graph.get(type + 'Group') || graph.get('group'); const parent = graph.get(type + 'Group') || graph.get('group');
const upperType = Util.upperFirst(type); const upperType = Util.upperFirst(type);
let item; 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 defaultModel = graph.get(CFG_PREFIX + upperType);
const mapper = graph.get(type + MAPPER_SUFFIX); const mapper = graph.get(type + MAPPER_SUFFIX);
if (mapper) { if (mapper) {
const mappedModel = mapper(model); const mappedModel = mapper(model);
if (mappedModel.styles) { if (mappedModel[STATE_SUFFIX]) {
styles = mappedModel.styles; styles = mappedModel[STATE_SUFFIX];
delete mappedModel.styles; delete mappedModel[STATE_SUFFIX];
} }
Util.each(mappedModel, (val, cfg) => { Util.each(mappedModel, (val, cfg) => {
model[cfg] = val; model[cfg] = val;

View File

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

View File

@ -30,7 +30,8 @@ const singleNodeDefinition = Util.mix({}, SingleShapeMixin, {
* @return {Array} 宽高 * @return {Array} 宽高
*/ */
getSize(cfg) { 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)) { if (!Util.isArray(size)) {
size = [ size, size ]; size = [ size, size ];
} }

View File

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

View File

@ -5,63 +5,65 @@ const deepMix = require('@antv/util/lib/deep-mix');
Shape.registerNode('diamond', { Shape.registerNode('diamond', {
// 自定义节点时的配置 // 自定义节点时的配置
options: { options: {
// 默认配置 size: [ 100, 100 ],
default: { style: {
width: 100, stroke: '#87e8de',
height: 100, fill: '#36cfc9',
stroke: '#69c0ff', lineWidth: 1
fill: '#e6f7ff', },
lineWidth: 1, // 文本样式配置
// 文本样式配置 labelCfg: {
labelCfg: { style: {
style: { fill: '#595959'
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
} }
}, },
// 鼠标hover状态下的配置 stateStyles: {
hover: { // 鼠标hover状态下的配置
lineWidth: 3 hover: {
lineWidth: 3
},
// 选中节点状态下的配置
select: {
lineWidth: 5
}
}, },
// 选中节点状态下的配置 // 节点上左右上下四个方向上的链接circle配置
select: { linkPoints: {
lineWidth: 5 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', shapeType: 'circle',
// 文本位置 // 文本位置
labelPosition: 'center', labelPosition: 'center',
drawShape(cfg, group) { drawShape(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {}; const customOptions = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default; const { style: defaultStyle, icon: defaultIcon } = this.options;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style); const { style: customStyle, icon: customIcon } = customOptions;
const { icon, linkPoints, width, height, ...diamondStyle } = style; const style = deepMix({}, defaultStyle, customStyle, cfg.style);
const icon = deepMix({}, defaultIcon, customIcon, cfg.icon);
const path = this.getPath(cfg); const path = this.getPath(cfg);
const keyShape = group.addShape('path', { const keyShape = group.addShape('path', {
attrs: { attrs: {
path, path,
...diamondStyle ...style
} }
}); });
@ -79,20 +81,36 @@ Shape.registerNode('diamond', {
image.set('capture', false); image.set('capture', false);
} }
const { top, left, right, bottom, size, this.drawLinkPoints(cfg, group);
fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
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) { if (left) {
// left circle // left circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: -width / 2, x: -width / 2,
y: 0, y: 0,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'diamond-anchor-left' className: 'diamond-mark-left'
}); });
} }
@ -100,14 +118,12 @@ Shape.registerNode('diamond', {
// right circle // right circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: width / 2, x: width / 2,
y: 0, y: 0,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'diamond-anchor-right' className: 'diamond-mark-right'
}); });
} }
@ -115,14 +131,12 @@ Shape.registerNode('diamond', {
// top circle // top circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: 0, x: 0,
y: -height / 2, y: -height / 2,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'diamond-anchor-top' className: 'diamond-mark-top'
}); });
} }
@ -130,27 +144,19 @@ Shape.registerNode('diamond', {
// bottom circle // bottom circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: 0, x: 0,
y: height / 2, y: height / 2,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'diamond-anchor-bottom' className: 'diamond-mark-bottom'
}); });
} }
return keyShape;
},
shouldUpdate() {
return false;
}, },
getPath(cfg) { getPath(cfg) {
const customStyle = this.getCustomConfig(cfg) || {}; const size = this.getSize(cfg);
const defaultConfig = customStyle.default; const width = size[0];
const style = deepMix({}, this.options.default, defaultConfig, cfg.style); const height = size[1];
const { width, height } = style;
const path = [ const path = [
[ 'M', 0, -height / 2 ], // 上部顶点 [ 'M', 0, -height / 2 ], // 上部顶点
[ 'L', width / 2, 0 ], // 右侧点 [ 'L', width / 2, 0 ], // 右侧点
@ -160,35 +166,22 @@ Shape.registerNode('diamond', {
]; ];
return path; 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) { update(cfg, item) {
const group = item.getContainer(); const group = item.getContainer();
const customStyle = this.getCustomConfig(cfg) || {}; const customOptions = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default || {}; const { style: defaultStyle, icon: defaultIcon, labelCfg: defaultLabelCfg } = this.options;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style); 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 keyShape = item.get('keyShape');
const path = this.getPath(cfg); const path = this.getPath(cfg);
keyShape.attr({ keyShape.attr({
path, path,
...diamondStyle ...style
}); });
const labelCfg = deepMix({}, defaultLabelCfg, cfg.labelCfg); const labelCfg = deepMix({}, defaultLabelCfg, customLabelCfg, cfg.labelCfg);
const labelStyle = this.getLabelStyle(cfg, labelCfg, group); const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
const text = group.findByClassName('node-label'); 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'); const { size: markSize, fill: markFill, stroke: markStroke, lineWidth: borderWidth } = linkPoints;
if (anchorLeft) {
anchorLeft.attr({ 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, x: -width / 2,
y: 0, y: 0,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }
const anchorRight = group.findByClassName('diamond-anchor-right'); const markRight = group.findByClassName('diamond-mark-right');
if (anchorRight) { if (markRight) {
anchorRight.attr({ markRight.attr({
x: width / 2, x: width / 2,
y: 0, y: 0,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }
const anchorTop = group.findByClassName('diamond-anchor-top'); const markTop = group.findByClassName('diamond-mark-top');
if (anchorTop) { if (markTop) {
anchorTop.attr({ markTop.attr({
x: 0, x: 0,
y: -height / 2, y: -height / 2,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }
const anchorBottom = group.findByClassName('diamond-anchor-bottom'); const markBottom = group.findByClassName('diamond-mark-bottom');
if (anchorBottom) { if (markBottom) {
anchorBottom.attr({ markBottom.attr({
x: 0, x: 0,
y: height / 2, y: height / 2,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }

View File

@ -7,68 +7,78 @@ const deepMix = require('@antv/util/lib/deep-mix');
Shape.registerNode('ellipse', { Shape.registerNode('ellipse', {
// 自定义节点时的配置 // 自定义节点时的配置
options: { options: {
// 默认配置 size: [ 60, 30 ],
default: { style: {
rx: 60,
ry: 30,
stroke: '#69c0ff',
fill: '#e6f7ff',
lineWidth: 1,
x: 0, x: 0,
y: 0, y: 0,
// 文本样式配置 stroke: '#87e8de',
labelCfg: { fill: '#36cfc9',
style: { lineWidth: 1
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 ]]
}, },
// 鼠标hover状态下的配置 // 文本样式配置
hover: { labelCfg: {
lineWidth: 3 style: {
fill: '#595959'
}
}, },
// 选中节点状态下的配置 stateStyles: {
select: { // 鼠标hover状态下的配置
lineWidth: 5 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', shapeType: 'ellipse',
// 文本位置 // 文本位置
labelPosition: 'center', labelPosition: 'center',
drawShape(cfg, group) { drawShape(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {}; // const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default; // const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style); // const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
// 使用用户配置的size大小 const customOptions = this.getCustomConfig(cfg) || {};
const { linkPoints, icon, ...ellipseStyle } = style; 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', { const keyShape = group.addShape('ellipse', {
attrs: ellipseStyle attrs: {
...style,
rx,
ry
}
}); });
const { width, height, show } = icon; const { width, height, show } = icon;
@ -85,20 +95,37 @@ Shape.registerNode('ellipse', {
image.set('capture', false); image.set('capture', false);
} }
const { top, left, right, bottom, size, this.drawLinkPoints(cfg, group);
fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints;
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) { if (left) {
// left circle // left circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: -rx, x: -rx,
y: 0, y: 0,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'ellipse-anchor-left' className: 'ellipse-mark-left'
}); });
} }
@ -106,14 +133,12 @@ Shape.registerNode('ellipse', {
// right circle // right circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: rx, x: rx,
y: 0, y: 0,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'ellipse-anchor-right' className: 'ellipse-mark-right'
}); });
} }
@ -121,14 +146,12 @@ Shape.registerNode('ellipse', {
// top circle // top circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: 0, x: 0,
y: -ry, y: -ry,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'ellipse-anchor-top' className: 'ellipse-mark-top'
}); });
} }
@ -136,47 +159,44 @@ Shape.registerNode('ellipse', {
// bottom circle // bottom circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: 0, x: 0,
y: ry, y: ry,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
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) { update(cfg, item) {
const customStyle = this.getCustomConfig(cfg) || {}; // const customStyle = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default || {}; // const defaultConfig = customStyle.default || {};
const style = deepMix({}, this.options.default, defaultConfig, cfg.style); // const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { icon, linkPoints, labelCfg: defaultLabelCfg, ...ellipseStyle } = 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'); const keyShape = item.get('keyShape');
keyShape.attr(ellipseStyle); keyShape.attr({
...style,
rx,
ry
});
const group = item.getContainer(); 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 labelStyle = this.getLabelStyle(cfg, labelCfg, group);
const text = group.findByClassName('node-label'); const text = group.findByClassName('node-label');
if (text) { 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'); const { size: markSize, ...markStyles } = linkPoints;
if (anchorLeft) {
anchorLeft.attr({ 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, x: -rx,
y: 0, y: 0,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}); });
} }
const anchorRight = group.findByClassName('ellipse-anchor-right'); const markRight = group.findByClassName('ellipse-mark-right');
if (anchorRight) { if (markRight) {
anchorRight.attr({ markRight.attr({
...markStyles,
x: rx, x: rx,
y: 0, y: 0,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}); });
} }
const anchorTop = group.findByClassName('ellipse-anchor-top'); const markTop = group.findByClassName('ellipse-mark-top');
if (anchorTop) { if (markTop) {
anchorTop.attr({ markTop.attr({
...markStyles,
x: 0, x: 0,
y: -ry, y: -ry,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}); });
} }
const anchorBottom = group.findByClassName('ellipse-anchor-bottom'); const markBottom = group.findByClassName('ellipse-mark-bottom');
if (anchorBottom) { if (markBottom) {
anchorBottom.attr({ markBottom.attr({
...markStyles,
x: 0, x: 0,
y: ry, y: ry,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}); });
} }
} }

View File

@ -6,95 +6,98 @@ Shape.registerNode('modelRect', {
// labelPosition: 'center', // labelPosition: 'center',
// 自定义节点时的配置 // 自定义节点时的配置
options: { options: {
// 默认配置 size: [ 185, 70 ],
default: { style: {
width: 185,
height: 70,
radius: 5, radius: 5,
stroke: '#69c0ff', stroke: '#69c0ff',
fill: '#ffffff', fill: '#ffffff',
lineWidth: 1, lineWidth: 1,
fillOpacity: 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 ]]
}, },
// hover状态下的配置 // 文本样式配置
hover: { labelCfg: {
lineWidth: 2, style: {
stroke: '#1890ff', fill: '#595959',
fill: '#e6f7ff' fontSize: 14
},
offset: 30
}, },
// 节点选中状态下的配置 stateStyles: {
select: { // hover状态下的配置
lineWidth: 3, hover: {
stroke: '#1890ff', lineWidth: 2,
fill: '#e6f7ff' 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) { drawShape(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {}; const customOptions = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default; const { style: defaultStyle, preRect: defaultPreRect } = this.options;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style); const { style: customStyle, preRect: customPreRect } = customOptions;
const { width, height, linkPoints, preRect, const style = deepMix({}, defaultStyle, customStyle, cfg.style);
logoIcon, stateIcon, ...rectStyle } = style; const size = this.getSize(cfg);
const width = size[0];
const height = size[1];
const keyShape = group.addShape('rect', { const keyShape = group.addShape('rect', {
attrs: { attrs: {
...style,
x: -width / 2, x: -width / 2,
y: -height / 2, y: -height / 2,
width, width,
height, height
...rectStyle
} }
}); });
const preRect = deepMix({}, defaultPreRect, customPreRect, cfg.preRect);
const { show: preRectShow, ...preRectStyle } = preRect; const { show: preRectShow, ...preRectStyle } = preRect;
if (preRectShow) { if (preRectShow) {
group.addShape('rect', { 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) { if (logoIcon.show) {
const { width: w, height: h, x, y, ...logoIconStyle } = logoIcon; const { width: w, height: h, x, y, ...logoIconStyle } = logoIcon;
const image = group.addShape('image', { const image = group.addShape('image', {
@ -115,14 +138,25 @@ Shape.registerNode('modelRect', {
...logoIconStyle, ...logoIconStyle,
x: x || -width / 2 + w, x: x || -width / 2 + w,
y: y || -h / 2 y: y || -h / 2
// width: w,
// height: h
}, },
className: 'rect-logo-icon' className: 'rect-logo-icon'
}); });
image.set('capture', false); 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) { if (stateIcon.show) {
const { width: w, height: h, x, y } = stateIcon; const { width: w, height: h, x, y } = stateIcon;
@ -137,21 +171,34 @@ Shape.registerNode('modelRect', {
image.set('capture', false); 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) { if (left) {
// left circle // left circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: -width / 2, x: -width / 2,
y: 0, y: 0,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'rect-anchor-left' className: 'rect-mark-left'
}); });
} }
@ -159,14 +206,12 @@ Shape.registerNode('modelRect', {
// right circle // right circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: width / 2, x: width / 2,
y: 0, y: 0,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'rect-anchor-right' className: 'rect-mark-right'
}); });
} }
@ -174,14 +219,12 @@ Shape.registerNode('modelRect', {
// top circle // top circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: 0, x: 0,
y: -height / 2, y: -height / 2,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'rect-anchor-top' className: 'rect-mark-top'
}); });
} }
@ -189,26 +232,29 @@ Shape.registerNode('modelRect', {
// bottom circle // bottom circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: 0, x: 0,
y: height / 2, y: height / 2,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'rect-anchor-bottom' className: 'rect-mark-bottom'
}); });
} }
return keyShape;
}, },
drawLabel(cfg, group) { drawLabel(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {}; const customOptions = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default || {}; const { labelCfg: defaultLabelCfg, logoIcon: defaultLogoIcon } = this.options;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style); const { labelCfg: customLabelCfg, logoIcon: customLogoIcon } = customOptions;
const labelCfg = deepMix({}, style.labelCfg, cfg.labelCfg);
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; let label = null;
const { logoIcon, width } = style;
const { show, width: w } = logoIcon; const { show, width: w } = logoIcon;
let offsetX = -width / 2 + labelCfg.offset; let offsetX = -width / 2 + labelCfg.offset;
@ -223,13 +269,13 @@ Shape.registerNode('modelRect', {
...fontStyle, ...fontStyle,
y: -5, y: -5,
x: offsetX, x: offsetX,
text: Util.fittingString(cfg.label, 70, 14) text: Util.fittingString(cfg.label, 100, 14)
} }
}); });
group.addShape('text', { group.addShape('text', {
attrs: { attrs: {
text: Util.fittingString(cfg.description, 80, 12), text: Util.fittingString(cfg.description, 75, 12),
fontSize: 12, fontSize: 12,
x: offsetX, x: offsetX,
y: 17, y: 17,
@ -249,40 +295,39 @@ Shape.registerNode('modelRect', {
} }
return label; return label;
}, },
getAnchorPoints() { getAnchorPoints(cfg) {
const defaultOptions = this.options; const customOptions = this.getCustomConfig(cfg) || {};
return defaultOptions.anchorPoints; const { anchorPoints: defaultAnchorPoints } = this.options;
}, const { anchorPoints: customAnchorPoints } = customOptions;
getSize(cfg) { const anchorPoints = deepMix({}, defaultAnchorPoints, customAnchorPoints);
const customStyle = this.getCustomConfig(cfg) || {}; return anchorPoints;
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { width, height } = style;
return [ width, height ];
}, },
update(cfg, item) { update(cfg, item) {
const customStyle = this.getCustomConfig(cfg) || {}; const customOptions = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default; const { style: defaultStyle, labelCfg: defaultLabelCfg, preRect: defaultPreRect,
const style = deepMix({}, this.options.default, defaultConfig, cfg.style); logoIcon: defaultLogoIcon, stateIcon: defaultStateIcon } = this.options;
const { width, height, linkPoints, logoIcon, stateIcon, const { style: customStyle, labelCfg: customLabelCfg, preRect: customPreRect,
labelCfg: defaultLabelCfg, ...rectStyle } = style; 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'); const keyShape = item.get('keyShape');
keyShape.attr({ keyShape.attr({
...style,
x: -width / 2, x: -width / 2,
y: -height / 2, y: -height / 2,
width, width,
height, height
...rectStyle
}); });
const group = item.getContainer(); 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'); const text = group.findByClassName('node-label');
const logoIcon = deepMix({}, defaultLogoIcon, customLogoIcon, cfg.logoIcon);
const { show, width: w } = logoIcon; const { show, width: w } = logoIcon;
const { offset, style: fontStyle } = labelCfg; const { offset, style: fontStyle } = labelCfg;
@ -318,7 +363,9 @@ Shape.registerNode('modelRect', {
const preRectShape = group.findByClassName('pre-rect'); const preRectShape = group.findByClassName('pre-rect');
if (preRectShape) { if (preRectShape) {
const preRect = deepMix({}, defaultPreRect, customPreRect, cfg.preRect);
preRectShape.attr({ preRectShape.attr({
...preRect,
x: -width / 2, x: -width / 2,
y: -height / 2, y: -height / 2,
height height
@ -327,8 +374,9 @@ Shape.registerNode('modelRect', {
const logoIconShape = group.findByClassName('rect-logo-icon'); const logoIconShape = group.findByClassName('rect-logo-icon');
if (logoIconShape) { if (logoIconShape) {
const { width: w, height: h, x, y } = logoIcon; const { width: w, height: h, x, y, ...logoIconStyle } = logoIcon;
logoIconShape.attr({ logoIconShape.attr({
...logoIconStyle,
x: x || -width / 2 + w, x: x || -width / 2 + w,
y: y || -h / 2, y: y || -h / 2,
width: w, width: w,
@ -338,8 +386,10 @@ Shape.registerNode('modelRect', {
const stateIconShape = group.findByClassName('rect-state-icon'); const stateIconShape = group.findByClassName('rect-state-icon');
if (stateIconShape) { 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({ stateIconShape.attr({
...stateIconStyle,
x: x || width / 2 - w * 2 + 8, x: x || width / 2 - w * 2 + 8,
y: y || -h / 2, y: y || -h / 2,
width: w, 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'); const { size: markSize, fill: markFill, stroke: markStroke, lineWidth: borderWidth } = linkPoints;
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, x: -width / 2,
y: 0, y: 0,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }
const anchorRight = group.findByClassName('rect-anchor-right'); const markRight = group.findByClassName('rect-mark-right');
if (anchorRight) { if (markRight) {
anchorRight.attr({ markRight.attr({
x: width / 2, x: width / 2,
y: 0, y: 0,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }
const anchorTop = group.findByClassName('rect-anchor-top'); const markTop = group.findByClassName('rect-mark-top');
if (anchorTop) { if (markTop) {
anchorTop.attr({ markTop.attr({
x: 0, x: 0,
y: -height / 2, y: -height / 2,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }
const anchorBottom = group.findByClassName('rect-anchor-bottom'); const markBottom = group.findByClassName('rect-mark-bottom');
if (anchorBottom) { if (markBottom) {
anchorBottom.attr({ markBottom.attr({
x: 0, x: 0,
y: height / 2, y: height / 2,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }

View File

@ -4,81 +4,100 @@ const deepMix = require('@antv/util/lib/deep-mix');
Shape.registerNode('rect', { Shape.registerNode('rect', {
// 自定义节点时的配置 // 自定义节点时的配置
options: { options: {
// 默认配置 size: [ 100, 30 ],
default: { style: {
width: 100,
height: 30,
radius: 0, radius: 0,
stroke: '#69c0ff', stroke: '#87e8de',
fill: '#e6f7ff', fill: '#36cfc9',
lineWidth: 1, lineWidth: 1,
fillOpacity: 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 ]]
}, },
// hover状态下的配置 // 文本样式配置
hover: { labelCfg: {
lineWidth: 2, style: {
stroke: '#1890ff' fill: '#595959',
fontSize: 12
}
}, },
// 节点选中状态下的配置 stateStyles: {
select: { // hover状态下的配置
lineWidth: 3, hover: {
stroke: '#1890ff', lineWidth: 2,
fill: '#91d5ff' 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', shapeType: 'rect',
drawShape(cfg, group) { drawShape(cfg, group) {
const customStyle = this.getCustomConfig(cfg) || {}; const customOptions = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default; const { style: defaultStyle } = this.options;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style); const { style: customStyle } = customOptions;
const { width, height, linkPoints, ...rectStyle } = style; 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', { const keyShape = group.addShape('rect', {
attrs: { attrs: {
...style,
x: -width / 2, x: -width / 2,
y: -height / 2, y: -height / 2,
width, width,
height, height
...rectStyle
}, },
className: 'rect-keyShape' className: 'rect-keyShape'
}); });
const { top, left, right, bottom, size, this.drawLinkPoints(cfg, group);
fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints; 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) { if (left) {
// left circle // left circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: -width / 2, x: -width / 2,
y: 0, y: 0,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'rect-anchor-left' className: 'rect-mark-left'
}); });
} }
@ -86,14 +105,12 @@ Shape.registerNode('rect', {
// right circle // right circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: width / 2, x: width / 2,
y: 0, y: 0,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'rect-anchor-right' className: 'rect-mark-right'
}); });
} }
@ -101,14 +118,12 @@ Shape.registerNode('rect', {
// top circle // top circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: 0, x: 0,
y: -height / 2, y: -height / 2,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'rect-anchor-top' className: 'rect-mark-top'
}); });
} }
@ -116,35 +131,30 @@ Shape.registerNode('rect', {
// bottom circle // bottom circle
group.addShape('circle', { group.addShape('circle', {
attrs: { attrs: {
...markStyle,
x: 0, x: 0,
y: height / 2, y: height / 2,
r: size, r: markSize
fill: anchorFill,
stroke: anchorStroke,
lineWidth: borderWidth
}, },
className: 'rect-anchor-bottom' className: 'rect-mark-bottom'
}); });
} }
return keyShape;
}, },
getAnchorPoints() { getAnchorPoints(cfg) {
const defaultOptions = this.options; const customOptions = this.getCustomConfig(cfg) || {};
return defaultOptions.anchorPoints; const { anchorPoints: defaultAnchorPoints } = this.options;
}, const { anchorPoints: customAnchorPoints } = customOptions;
getSize(cfg) { const anchorPoints = deepMix({}, defaultAnchorPoints, customAnchorPoints);
const customStyle = this.getCustomConfig(cfg) || {}; return anchorPoints;
const defaultConfig = customStyle.default;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style);
const { width, height } = style;
return [ width, height ];
}, },
update(cfg, item) { update(cfg, item) {
const customStyle = this.getCustomConfig(cfg) || {}; const customOptions = this.getCustomConfig(cfg) || {};
const defaultConfig = customStyle.default; const { style: defaultStyle, labelCfg: defaultLabelCfg } = this.options;
const style = deepMix({}, this.options.default, defaultConfig, cfg.style); const { style: customStyle, labelCfg: customLabelCfg } = customOptions;
const { width, height, linkPoints, labelCfg: defaultLabelCfg, ...rectStyle } = style; 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'); const keyShape = item.get('keyShape');
keyShape.attr({ keyShape.attr({
@ -152,12 +162,12 @@ Shape.registerNode('rect', {
y: -height / 2, y: -height / 2,
width, width,
height, height,
...rectStyle ...style
}); });
const group = item.getContainer(); 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 labelStyle = this.getLabelStyle(cfg, labelCfg, group);
const text = group.findByClassName('node-label'); const text = group.findByClassName('node-label');
if (text) { if (text) {
@ -165,53 +175,69 @@ Shape.registerNode('rect', {
...labelStyle ...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'); const size = this.getSize(cfg);
if (anchorLeft) { const width = size[0];
anchorLeft.attr({ const height = size[1];
const markLeft = group.findByClassName('rect-mark-left');
if (markLeft) {
markLeft.attr({
x: -width / 2, x: -width / 2,
y: 0, y: 0,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }
const anchorRight = group.findByClassName('rect-anchor-right'); const markRight = group.findByClassName('rect-mark-right');
if (anchorRight) { if (markRight) {
anchorRight.attr({ markRight.attr({
x: width / 2, x: width / 2,
y: 0, y: 0,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }
const anchorTop = group.findByClassName('rect-anchor-top'); const markTop = group.findByClassName('rect-mark-top');
if (anchorTop) { if (markTop) {
anchorTop.attr({ markTop.attr({
x: 0, x: 0,
y: -height / 2, y: -height / 2,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }
const anchorBottom = group.findByClassName('rect-anchor-bottom'); const markBottom = group.findByClassName('rect-mark-bottom');
if (anchorBottom) { if (markBottom) {
anchorBottom.attr({ markBottom.attr({
x: 0, x: 0,
y: height / 2, y: height / 2,
r: size, r: markSize,
fill: anchorFill, fill: markFill,
stroke: anchorStroke, stroke: markStroke,
lineWidth: borderWidth lineWidth: borderWidth
}); });
} }

View File

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

View File

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

View File

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