Merge pull request #649 from antvis/daily-190315

Daily 190315
This commit is contained in:
xiaoqing.dongxq 2019-03-15 17:41:40 +08:00
commit 3d894df93d
10 changed files with 191 additions and 28 deletions

View File

@ -6,6 +6,7 @@
</head>
<body>
<div id="mountNode"></div>
<button id="download">点击下载图片</button>
<script src="assets/hierarchy.js"></script>
<script src="../build/g6.js"></script>
<script src="./assets/jquery-3.2.1.min.js"></script>
@ -89,6 +90,13 @@ const graph = new G6.TreeGraph({
}
}, 'drag-canvas']
},
defaultNode: {
shape: 'tree-node',
anchorPoints: [ [0, 0.5], [1, 0.5] ],
},
defaultEdge: {
shape: 'cubic-horizontal'
},
edgeStyle: {
default: {
stroke: '#A3B1BF'
@ -108,18 +116,11 @@ const graph = new G6.TreeGraph({
$.getJSON('./assets/data/modeling-methods.json', data => {
graph.data(data);
graph.render();
graph.getNodes().forEach(node => {
const model = node.get('model');
model.anchorPoints = [ [0, 0.5], [1, 0.5] ];
model.shape = 'tree-node';
});
graph.getEdges().forEach((edge) => {
edge.get('model').shape = 'cubic-horizontal';
});
graph.refresh();
graph.fitView();
});
document.getElementById('download').addEventListener('click', () => {
graph.downloadImage();
});
</script>
</body>
</html>

View File

@ -349,23 +349,25 @@
data.edges.forEach((edge, i) => {
edge.id = i;
});
G6.Global.defaultNode = {
size: [10, 10],
color: 'steelblue',
style: {
lineWidth: 2,
fill: '#fff'
}
};
G6.Global.defaultEdge = {
size: 1,
color: '#e2e2e2'
};
const graph = new G6.Graph({
container: 'mountNode',
width: 1000,
height: 600,
autoPaint: false
autoPaint: false,
defaultNode: {
size: [10, 10],
color: 'steelblue'
},
defaultEdge: {
size: 1,
color: '#e2e2e2'
},
nodeStyle: {
default: {
lineWidth: 2,
fill: '#fff'
}
}
});
const simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }).strength(0.5))

View File

@ -64,6 +64,7 @@
stroke: '#A3B1BF'
}
},
linkCenter: true,
layout: layouts[currentLayout]
});
G6.Global.defaultNode.size = 16;

View File

@ -59,6 +59,7 @@
}
}, 'drag-canvas']
},
defaultNode: { size: 16 },
nodeStyle: {
default: {
fill: '#40a9ff',
@ -141,7 +142,6 @@
}
]
};
G6.Global.defaultNode.size = 16;
graph.data(data);
graph.render();
graph.getNodes().forEach(node => {

View File

@ -110,7 +110,7 @@
"silent": false
},
"dependencies": {
"@antv/g": "~3.4.0-beta.13",
"@antv/g": "~3.4.0",
"@antv/util": "~1.3.1"
},
"engines": {

View File

@ -95,7 +95,7 @@ module.exports = {
shape.set('capture', false);
item.set('delegateShape', shape);
}
shape.attr({ x, y });
shape.attr({ x: x - bbox.width / 2, y: y - bbox.height / 2 });
this.graph.paint();
}
};

View File

@ -3,6 +3,8 @@ const Item = require('../../item');
const NODE = 'node';
const EDGE = 'edge';
const CFG_PREFIX = 'default';
const hasOwnProperty = Object.hasOwnProperty;
class ItemController {
constructor(graph) {
@ -11,8 +13,22 @@ class ItemController {
addItem(type, model) {
const graph = this.graph;
const parent = graph.get(type + 'Group') || graph.get('group');
const upperType = Util.upperFirst(type);
let item;
const styles = graph.get(type + 'Style');
const defaultModel = graph.get(CFG_PREFIX + upperType);
if (defaultModel) {
// 很多布局会直接修改原数据模型,所以不能用 merge 的形式,逐个写入原 model 中
Util.each(defaultModel, (val, cfg) => {
if (!hasOwnProperty.call(model, cfg)) {
if (Util.isObject(val)) {
model[cfg] = Util.clone(val);
} else {
model[cfg] = defaultModel[cfg];
}
}
});
}
if (type === EDGE) {
let source = model.source;
let target = model.target;
@ -26,7 +42,7 @@ class ItemController {
console.warn('The source or target node of edge ' + model.id + ' does not exist!');
return;
}
item = new Item[Util.upperFirst(type)]({
item = new Item[upperType]({
model,
source,
target,
@ -35,7 +51,7 @@ class ItemController {
group: parent.addGroup()
});
} else {
item = new Item[Util.upperFirst(type)]({
item = new Item[upperType]({
model,
styles,
group: parent.addGroup()

View File

@ -109,6 +109,22 @@ class Graph extends EventEmitter {
* @type {Boolean}
*/
linkCenter: false,
/**
* 默认的节点配置data 上定义的配置会覆盖这些配置例如
* defaultNode: {
* shape: 'rect',
* size: [60, 40]
* }
* 若数据项为 { id: 'node', x: 100, y: 100 }
* 实际创建的节点模型是 { id: 'node', x: 100, y: 100 shape: 'rect', size: [60, 40] }
* 若数据项为 { id: 'node', x: 100, y: 100, shape: 'circle' }
* 实际创建的节点模型是 { id: 'node', x: 100, y: 100 shape: 'circle', size: [60, 40] }
*/
defaultNode: {},
/**
* 默认边配置data 上定义的配置会覆盖这些配置用法同 defaultNode
*/
defaultEdge: {},
/**
* 节点默认样式也可以添加状态样式
* 例如
@ -805,6 +821,75 @@ class Graph extends EventEmitter {
this.set('autoPaint', auto);
}
/**
* 返回图表的 dataUrl 用于生成图片
* @return {string/Object} 图片 dataURL
*/
toDataURL() {
const canvas = this.get('canvas');
const renderer = canvas.getRenderer();
const canvasDom = canvas.get('el');
let dataURL = '';
if (renderer === 'svg') {
const clone = canvasDom.cloneNode(true);
const svgDocType = document.implementation.createDocumentType(
'svg', '-//W3C//DTD SVG 1.1//EN', 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'
);
const svgDoc = document.implementation.createDocument('http://www.w3.org/2000/svg', 'svg', svgDocType);
svgDoc.replaceChild(clone, svgDoc.documentElement);
const svgData = (new XMLSerializer()).serializeToString(svgDoc);
dataURL = 'data:image/svg+xml;charset=utf8,' + encodeURIComponent(svgData);
} else if (renderer === 'canvas') {
dataURL = canvasDom.toDataURL('image/png');
}
return dataURL;
}
/**
* 画布导出图片
* @param {String} name 图片的名称
*/
downloadImage(name) {
const self = this;
if (self.isAnimating()) {
self.stopAnimate();
}
const canvas = self.get('canvas');
const renderer = canvas.getRenderer();
const fileName = (name || 'graph') + (renderer === 'svg' ? '.svg' : '.png');
const link = document.createElement('a');
setTimeout(() => {
const dataURL = self.toDataURL();
if (window.Blob && window.URL && renderer !== 'svg') {
const arr = dataURL.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
const blobObj = new Blob([ u8arr ], { type: mime });
if (window.navigator.msSaveBlob) {
window.navigator.msSaveBlob(blobObj, fileName);
} else {
link.addEventListener('click', function() {
link.download = fileName;
link.href = window.URL.createObjectURL(blobObj);
});
}
} else {
link.addEventListener('click', function() {
link.download = fileName;
link.href = dataURL;
});
}
const e = document.createEvent('MouseEvents');
e.initEvent('click', false, false);
link.dispatchEvent(e);
}, 16);
}
/**
* 清除画布元素
* @return {object} this

View File

@ -15,6 +15,7 @@ const BaseUtil = {
isArray: require('@antv/util/lib/type/is-array'),
createDom: require('@antv/util/lib/dom/create-dom'),
modifyCSS: require('@antv/util/lib/dom/modify-css'),
isObject: require('@antv/util/lib/type/is-object'),
isPlainObject: require('@antv/util/lib/type/is-plain-object'),
isNumber: require('@antv/util/lib/type/is-number'),
isString: require('@antv/util/lib/type/is-string'),

View File

@ -461,6 +461,63 @@ describe('all node link center', () => {
expect(edgeKeyShape.attr('shadowColor')).to.be.null;
graph.destroy();
});
it('graph with default cfg', () => {
const graph = new G6.Graph({
container: div,
width: 500,
height: 500,
defaultNode: {
shape: 'rect',
size: [ 60, 40 ],
color: '#ccc',
labelCfg: {
position: 'right',
offset: 5,
style: {
fontSize: 14,
fill: 'blue'
}
}
},
defaultEdge: {
shape: 'cubic',
color: '#666'
}
});
const node = graph.addItem('node', { id: 'node1', x: 100, y: 150, label: '111' });
let model = node.get('model');
expect(model.id).to.equal('node1');
expect(model.x).to.equal(100);
expect(model.y).to.equal(150);
expect(model.shape).to.equal('rect');
expect(model.size[0]).to.equal(60);
expect(model.size[1]).to.equal(40);
expect(model.color).to.equal('#ccc');
expect(model.labelCfg.position).to.equal('right');
expect(model.labelCfg.style.fill).to.equal('blue');
const node2 = graph.addItem('node', { id: 'node2', x: 150, y: 100, label: '222', color: '#666', shape: 'circle' });
model = node2.get('model');
expect(model.shape).to.equal('circle');
expect(model.size[0]).to.equal(60);
expect(model.size[1]).to.equal(40);
expect(model.color).to.equal('#666');
model.size[1] = 50;
expect(model.size[1]).to.equal(50);
expect(node.get('model').size[1]).to.equal(40);
expect(model.labelCfg.position).to.equal('right');
expect(model.labelCfg.style.fill).to.equal('blue');
model.labelCfg.position = 'left';
model.labelCfg.style.fill = 'red';
expect(node.get('model').labelCfg.position).to.equal('right');
expect(node.get('model').labelCfg.style.fill).to.equal('blue');
const edge = graph.addItem('edge', { id: 'edge', source: 'node1', target: 'node2', shape: 'line' });
model = edge.get('model');
expect(model.id).to.equal('edge');
expect(model.source).to.equal('node1');
expect(model.shape).to.equal('line');
expect(model.color).to.equal('#666');
graph.destroy();
});
it('clear', () => {
graph.destroy();
expect(graph.destroyed).eql(true);