diff --git a/demos/defaultNode2.html b/demos/defaultNode2.html index 39b5d9eadd..bee9d7e96c 100644 --- a/demos/defaultNode2.html +++ b/demos/defaultNode2.html @@ -206,6 +206,106 @@ } } }, 'modelRect') + + G6.registerNode('star1', { + labelPosition: 'bottom', + getCustomConfig() { + return { + default: { + fill: '#ff4d4f', + stroke: '#ffa39e', + icon: { + show: true + }, + labelCfg: { + style: { + fontSize: 16, + fill: '#ff7875' + }, + offset: 20 + } + } + } + } + }, 'star') + + G6.registerNode('star2', { + labelPosition: 'bottom', + getCustomConfig() { + return { + default: { + fill: '#bae637', + stroke: '#eaff8f', + icon: { + show: true, + width: 25, + height: 25, + img: 'https://gw.alipayobjects.com/zos/basement_prod/300a2523-67e0-4cbf-9d4a-67c077b40395.svg' + }, + linkPoints: { + top: true + } + } + } + } + }, 'star') + + G6.registerNode('star3', { + labelPosition: 'top', + getCustomConfig() { + return { + default: { + fill: '#73d13d', + stroke: '#b7eb8f', + icon: { + show: true, + width: 25, + height: 25 + }, + labelCfg: { + offset: 35 + }, + linkPoints: { + top: true, + left: true, + right: true, + leftBottom: true, + rightBottom: true, + fill: '#ff85c0', + size: 5 + } + } + } + } + }, 'star') + + G6.registerNode('star4', { + labelPosition: 'bottom', + getCustomConfig() { + return { + default: { + outerR: 100, + innerR: 30, + fill: '#ffc53d', + stroke: '#ffe58f', + icon: { + show: true, + width: 35, + height: 35 + }, + labelCfg: { + offset: 40 + }, + linkPoints: { + leftBottom: true, + rightBottom: true, + fill: '#40a9ff', + size: 6 + } + } + } + } + }, 'star') const data = { nodes: [ @@ -290,6 +390,40 @@ x: 630, y: 280, shape: 'modelRect5' + }, + { + id: 'star', + label: '逻辑回归', + x: 150, + y: 450, + shape: 'star' + }, + { + id: 'star1', + label: '二次元逻辑', + x: 300, + y: 450, + shape: 'star1' + }, + { + id: 'star2', + x: 450, + y: 450, + shape: 'star2' + }, + { + id: 'star3', + label: '逻辑回归二次元逻辑回归二次元', + x: 600, + y: 450, + shape: 'star3' + }, + { + id: 'star4', + label: '逻辑回归二次元逻辑回归二次元', + x: 800, + y: 450, + shape: 'star4' } ] } diff --git a/src/shape/nodes/circle.js b/src/shape/nodes/circle.js index 600711bd8d..59b2cccdec 100644 --- a/src/shape/nodes/circle.js +++ b/src/shape/nodes/circle.js @@ -35,7 +35,7 @@ Shape.registerNode('circle', { icon: { // 是否显示icon,值为 false 则不渲染icon show: false, - // icon的地址,可以是字符串或Image + // icon的地址,字符串类型 img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg', width: 16, height: 16 diff --git a/src/shape/nodes/diamond.js b/src/shape/nodes/diamond.js index ec00953669..5186aeb61c 100644 --- a/src/shape/nodes/diamond.js +++ b/src/shape/nodes/diamond.js @@ -34,7 +34,7 @@ Shape.registerNode('diamond', { icon: { // 是否显示icon,值为 false 则不渲染icon show: false, - // icon的地址,可以是字符串或Image + // icon的地址,字符串类型 img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg', width: 16, height: 16 diff --git a/src/shape/nodes/ellipse.js b/src/shape/nodes/ellipse.js index 0e6deff74c..24f4de9085 100644 --- a/src/shape/nodes/ellipse.js +++ b/src/shape/nodes/ellipse.js @@ -26,7 +26,7 @@ Shape.registerNode('ellipse', { icon: { // 是否显示icon,值为 false 则不渲染icon show: false, - // icon的地址,可以是字符串或Image + // icon的地址,字符串类型 img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg', width: 36, height: 36 diff --git a/src/shape/nodes/index.js b/src/shape/nodes/index.js index e6a4192320..7c4d2dfbd7 100644 --- a/src/shape/nodes/index.js +++ b/src/shape/nodes/index.js @@ -4,4 +4,4 @@ require('./ellipse'); require('./diamond'); require('./triangle'); require('./modelRect'); - +require('./star'); diff --git a/src/shape/nodes/modelRect.js b/src/shape/nodes/modelRect.js index 16907a9742..8ba450a741 100644 --- a/src/shape/nodes/modelRect.js +++ b/src/shape/nodes/modelRect.js @@ -48,7 +48,7 @@ Shape.registerNode('modelRect', { show: true, x: 0, y: 0, - // icon的地址,可以是字符串或Image + // icon的地址,字符串类型 img: 'https://gw.alipayobjects.com/zos/basement_prod/4f81893c-1806-4de4-aff3-9a6b266bc8a2.svg', width: 16, height: 16 @@ -57,7 +57,7 @@ Shape.registerNode('modelRect', { stateIcon: { // 是否显示icon,值为 false 则不渲染icon show: true, - // icon的地址,可以是字符串或Image + // icon的地址,字符串类型 img: 'https://gw.alipayobjects.com/zos/basement_prod/300a2523-67e0-4cbf-9d4a-67c077b40395.svg', width: 16, height: 16 diff --git a/src/shape/nodes/star.js b/src/shape/nodes/star.js new file mode 100644 index 0000000000..1dd00d93ba --- /dev/null +++ b/src/shape/nodes/star.js @@ -0,0 +1,349 @@ +const Shape = require('../shape'); +const deepMix = require('@antv/util/lib/deep-mix'); + +// 菱形shape +Shape.registerNode('star', { + // 自定义节点时的配置 + options: { + // 默认配置 + default: { + outerR: 60, + innerR: 20, + stroke: '#69c0ff', + fill: '#e6f7ff', + lineWidth: 1, + // 文本样式配置 + labelCfg: { + style: { + fill: '#595959' + }, + offset: 0 + }, + // 节点上左右上下四个方向上的链接circle配置 + linkPoints: { + top: false, + right: false, + left: false, + leftBottom: false, + rightBottom: false, + // circle的大小 + size: 3, + lineWidth: 1, + fill: '#fff', + 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状态下的配置 + hover: { + lineWidth: 3 + }, + // 选中节点状态下的配置 + select: { + lineWidth: 5 + } + }, + 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 path = this.getPath(cfg); + const keyShape = group.addShape('path', { + attrs: { + path, + ...starStyle + } + }); + + const { width: w, height: h, show } = icon; + if (show) { + const image = group.addShape('image', { + attrs: { + x: -w / 2, + y: -h / 2, + ...icon + }, + className: 'star-icon' + }); + + image.set('capture', false); + } + + const { top, left, right, leftBottom, rightBottom, size, + fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints; + + if (right) { + // right circle + // up down left right 四个方向的坐标均不相同 + const x1 = Math.cos((18 + 72 * 0) / 180 * Math.PI) * outerR; + const y1 = Math.sin((18 + 72 * 0) / 180 * Math.PI) * outerR; + + group.addShape('circle', { + attrs: { + x: x1, + y: -y1, + r: size, + fill: anchorFill, + stroke: anchorStroke, + lineWidth: borderWidth + }, + className: 'star-anchor-right' + }); + } + + if (top) { + // up down left right 四个方向的坐标均不相同 + const x1 = Math.cos((18 + 72 * 1) / 180 * Math.PI) * outerR; + const y1 = Math.sin((18 + 72 * 1) / 180 * Math.PI) * outerR; + + // top circle + group.addShape('circle', { + attrs: { + x: x1, + y: -y1, + r: size, + fill: anchorFill, + stroke: anchorStroke, + lineWidth: borderWidth + }, + className: 'star-anchor-top' + }); + } + + if (left) { + // up down left right 四个方向的坐标均不相同 + const x1 = Math.cos((18 + 72 * 2) / 180 * Math.PI) * outerR; + const y1 = Math.sin((18 + 72 * 2) / 180 * Math.PI) * outerR; + + // left circle + group.addShape('circle', { + attrs: { + x: x1, + y: -y1, + r: size, + fill: anchorFill, + stroke: anchorStroke, + lineWidth: borderWidth + }, + className: 'star-anchor-left' + }); + } + + if (leftBottom) { + // up down left right 四个方向的坐标均不相同 + const x1 = Math.cos((18 + 72 * 3) / 180 * Math.PI) * outerR; + const y1 = Math.sin((18 + 72 * 3) / 180 * Math.PI) * outerR; + + // left bottom circle + group.addShape('circle', { + attrs: { + x: x1, + y: -y1, + r: size, + fill: anchorFill, + stroke: anchorStroke, + lineWidth: borderWidth + }, + className: 'star-anchor-left-bottom' + }); + } + + if (rightBottom) { + // up down left right 四个方向的坐标均不相同 + const x1 = Math.cos((18 + 72 * 4) / 180 * Math.PI) * outerR; + const y1 = Math.sin((18 + 72 * 4) / 180 * Math.PI) * outerR; + + // left bottom circle + group.addShape('circle', { + attrs: { + x: x1, + y: -y1, + r: size, + fill: anchorFill, + stroke: anchorStroke, + lineWidth: borderWidth + }, + className: 'star-anchor-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 path = []; + for (let i = 0; i < 5; i++) { + const x1 = Math.cos((18 + 72 * i) / 180 * Math.PI) * outerR; + const y1 = Math.sin((18 + 72 * i) / 180 * Math.PI) * outerR; + const x2 = Math.cos((54 + 72 * i) / 180 * Math.PI) * innerR; + const y2 = Math.sin((54 + 72 * i) / 180 * Math.PI) * innerR; + + if (i === 0) { + path.push([ + 'M', x1, -y1 + ]); + } else { + path.push([ + 'L', x1, -y1 + ]); + } + path.push([ + 'L', x2, -y2 + ]); + } + + path.push([ + 'Z' + ]); + + 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 { icon, linkPoints, outerR, + labelCfg: defaultLabelCfg, ...starStyle } = style; + const keyShape = item.get('keyShape'); + const path = this.getPath(cfg); + keyShape.attr({ + path, + ...starStyle + }); + + const labelCfg = deepMix({}, defaultLabelCfg, cfg.labelCfg); + const labelStyle = this.getLabelStyle(cfg, labelCfg, group); + + const text = group.findByClassName('node-label'); + if (text) { + text.attr({ + ...labelStyle + }); + } + + const starIcon = group.findByClassName('star-icon'); + if (starIcon) { + const { width: w, height: h } = icon; + starIcon.attr({ + x: -w / 2, + y: -h / 2, + ...icon + }); + } + + const { size, fill: anchorFill, stroke: anchorStroke, lineWidth: borderWidth } = linkPoints; + + const anchorRight = group.findByClassName('star-anchor-right'); + if (anchorRight) { + const x = Math.cos((18 + 72 * 0) / 180 * Math.PI) * outerR; + const y = Math.sin((18 + 72 * 0) / 180 * Math.PI) * outerR; + + anchorRight.attr({ + x, + y: -y, + r: size, + fill: anchorFill, + stroke: anchorStroke, + lineWidth: borderWidth + }); + } + + const anchorTop = group.findByClassName('star-anchor-top'); + if (anchorTop) { + 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({ + x, + y: -y, + r: size, + fill: anchorFill, + stroke: anchorStroke, + lineWidth: borderWidth + }); + } + + const anchorLeft = group.findByClassName('star-anchor-left'); + if (anchorLeft) { + 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({ + x, + y: -y, + r: size, + fill: anchorFill, + stroke: anchorStroke, + lineWidth: borderWidth + }); + } + + const anchorLeftBottom = group.findByClassName('star-anchor-left-bottom'); + if (anchorLeftBottom) { + 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({ + x, + y: -y, + r: size, + fill: anchorFill, + stroke: anchorStroke, + lineWidth: borderWidth + }); + } + + const anchorRightBottom = group.findByClassName('star-anchor-right-bottom'); + if (anchorRightBottom) { + 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({ + x, + y: -y, + r: size, + fill: anchorFill, + stroke: anchorStroke, + lineWidth: borderWidth + }); + } + } +}, 'single-shape'); diff --git a/src/shape/nodes/triangle.js b/src/shape/nodes/triangle.js index 22c7973efb..1d3881efca 100644 --- a/src/shape/nodes/triangle.js +++ b/src/shape/nodes/triangle.js @@ -15,7 +15,7 @@ Shape.registerNode('triangle', { // 文本样式配置 labelCfg: { style: { - fill: '#000' + fill: '#595959' }, offset: 15 }, @@ -35,7 +35,7 @@ Shape.registerNode('triangle', { icon: { // 是否显示icon,值为 false 则不渲染icon show: false, - // icon的地址,可以是字符串或Image + // icon的地址,字符串类型 img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg', width: 16, height: 16, diff --git a/src/shape/single-shape-mixin.js b/src/shape/single-shape-mixin.js index d82da7c102..a7a792c627 100644 --- a/src/shape/single-shape-mixin.js +++ b/src/shape/single-shape-mixin.js @@ -40,13 +40,6 @@ const SingleShape = { }, drawLabel(cfg, group) { - // const labelCfg = cfg.labelCfg || {}; - // const labelStyle = this.getLabelStyle(cfg, labelCfg, group); - // const label = group.addShape('text', { - // attrs: labelStyle - // }); - // return label; - const customStyle = this.getCustomConfig(cfg) || {}; const defaultConfig = customStyle.default; const style = merge({}, this.options.default, defaultConfig);