mirror of
https://gitee.com/antv/g6.git
synced 2024-12-02 19:58:46 +08:00
feat: downloadFullImage for full graph.
This commit is contained in:
parent
f82d5ecfd2
commit
b56c83d3eb
@ -50,7 +50,7 @@
|
||||
"site:deploy": "npm run site:build && gh-pages -d public",
|
||||
"start": "npm run site:develop",
|
||||
"test": "jest",
|
||||
"test-live": "DEBUG_MODE=1 jest --watch ./tests/unit/plugins/minimap-spec.ts",
|
||||
"test-live": "DEBUG_MODE=1 jest --watch ./tests/unit/graph/graph-spec.ts",
|
||||
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx",
|
||||
"watch": "father build -w",
|
||||
"cdn": "antv-bin upload -n @antv/g6"
|
||||
|
@ -10,6 +10,7 @@ import deepMix from '@antv/util/lib/deep-mix';
|
||||
import each from '@antv/util/lib/each';
|
||||
import isPlainObject from '@antv/util/lib/is-plain-object';
|
||||
import isString from '@antv/util/lib/is-string';
|
||||
import isNumber from '@antv/util/lib/is-number';
|
||||
import {
|
||||
GraphAnimateConfig,
|
||||
GraphOptions,
|
||||
@ -46,6 +47,7 @@ import {
|
||||
ViewController,
|
||||
} from './controller';
|
||||
import PluginBase from '../plugins/base';
|
||||
import createDom from '@antv/dom-util/lib/create-dom';
|
||||
|
||||
const NODE = 'node';
|
||||
const SVG = 'svg';
|
||||
@ -1364,7 +1366,91 @@ export default class Graph extends EventEmitter implements IGraph {
|
||||
}
|
||||
|
||||
/**
|
||||
* 画布导出图片
|
||||
* 导出包含全图的图片
|
||||
* @param {String} name 图片的名称
|
||||
*/
|
||||
public downloadFullImage(name?: string, imageConfig?: { backgroundColor?: string, padding?: number | number[]}): void {
|
||||
const bbox = this.get('group').getCanvasBBox();
|
||||
const height = bbox.height;
|
||||
const width = bbox.width;
|
||||
const renderer = this.get('renderer');
|
||||
const vContainerDOM = createDom('<div id="test"></div>');
|
||||
|
||||
let backgroundColor = imageConfig ? imageConfig.backgroundColor : undefined;
|
||||
let padding = imageConfig ? imageConfig.padding : undefined;
|
||||
if (!padding) padding = [ 0, 0, 0, 0 ];
|
||||
else if (isNumber(padding)) padding = [padding, padding, padding, padding];
|
||||
|
||||
const vHeight = height + padding[0] + padding[2];
|
||||
const vWidth = width + padding[1] + padding[3];
|
||||
const canvasOptions = {
|
||||
container: vContainerDOM,
|
||||
height: vHeight,
|
||||
width: vWidth
|
||||
};
|
||||
const vCanvas = renderer === 'svg' ? new GSVGCanvas(canvasOptions) : new GCanvas(canvasOptions);
|
||||
|
||||
const group = this.get('group');
|
||||
const vGroup = group.clone();
|
||||
|
||||
let matrix = vGroup.getMatrix();
|
||||
if (!matrix) matrix = mat3.create();
|
||||
const centerX = (bbox.maxX + bbox.minX) / 2;
|
||||
const centerY = (bbox.maxY + bbox.minY) / 2;
|
||||
mat3.translate(matrix, matrix, [-centerX, -centerY]);
|
||||
mat3.translate(matrix, matrix, [width / 2 + padding[3], height / 2 + padding[0]]);
|
||||
|
||||
vGroup.resetMatrix();
|
||||
vGroup.setMatrix(matrix);
|
||||
vCanvas.add(vGroup);
|
||||
|
||||
const vCanvasEl = vCanvas.get('el');
|
||||
|
||||
setTimeout(() => {
|
||||
const type = 'image/png';
|
||||
let dataURL = '';
|
||||
if (renderer === 'svg') {
|
||||
const clone = vCanvasEl.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 {
|
||||
let imageData;
|
||||
const context = vCanvasEl.getContext('2d');
|
||||
let compositeOperation;
|
||||
if (backgroundColor) {
|
||||
const pixelRatio = window.devicePixelRatio;
|
||||
imageData = context.getImageData(0, 0, vWidth * pixelRatio, vHeight * pixelRatio);
|
||||
compositeOperation = context.globalCompositeOperation;
|
||||
context.globalCompositeOperation = "destination-over";
|
||||
context.fillStyle = backgroundColor;
|
||||
context.fillRect(0, 0, vWidth, vHeight);
|
||||
}
|
||||
dataURL = vCanvasEl.toDataURL(type);
|
||||
if (backgroundColor) {
|
||||
context.clearRect(0, 0, vWidth, vHeight);
|
||||
context.putImageData(imageData, 0, 0);
|
||||
context.globalCompositeOperation = compositeOperation;
|
||||
}
|
||||
}
|
||||
|
||||
const link: HTMLAnchorElement = document.createElement('a');
|
||||
const fileName: string = (name || 'graph') + (renderer === 'svg' ? '.svg' : '.png');
|
||||
|
||||
this.dataURLToImage(dataURL, renderer, link, fileName);
|
||||
|
||||
const e = document.createEvent('MouseEvents');
|
||||
e.initEvent('click', false, false);
|
||||
link.dispatchEvent(e);
|
||||
}, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* 画布导出图片,图片仅包含画布可见区域部分内容
|
||||
* @param {String} name 图片的名称
|
||||
*/
|
||||
public downloadImage(name?: string, backgroundColor?: string): void {
|
||||
@ -1380,41 +1466,7 @@ export default class Graph extends EventEmitter implements IGraph {
|
||||
const link: HTMLAnchorElement = document.createElement('a');
|
||||
setTimeout(() => {
|
||||
const dataURL = self.toDataURL('image/png', backgroundColor);
|
||||
if (typeof window !== 'undefined') {
|
||||
if (window.Blob && window.URL && renderer !== 'svg') {
|
||||
const arr = dataURL.split(',');
|
||||
let mime = '';
|
||||
if (arr && arr.length > 0) {
|
||||
const match = arr[0].match(/:(.*?);/);
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
if (match && match.length >= 2) mime = 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', () => {
|
||||
link.download = fileName;
|
||||
link.href = window.URL.createObjectURL(blobObj);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
link.addEventListener('click', function() {
|
||||
link.download = fileName;
|
||||
link.href = dataURL;
|
||||
});
|
||||
}
|
||||
}
|
||||
this.dataURLToImage(dataURL, renderer, link, fileName);
|
||||
|
||||
const e = document.createEvent('MouseEvents');
|
||||
e.initEvent('click', false, false);
|
||||
@ -1422,6 +1474,43 @@ export default class Graph extends EventEmitter implements IGraph {
|
||||
}, 16);
|
||||
}
|
||||
|
||||
private dataURLToImage(dataURL: string, renderer: string, link, fileName) {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (window.Blob && window.URL && renderer !== 'svg') {
|
||||
const arr = dataURL.split(',');
|
||||
let mime = '';
|
||||
if (arr && arr.length > 0) {
|
||||
const match = arr[0].match(/:(.*?);/);
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
if (match && match.length >= 2) mime = 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', () => {
|
||||
link.download = fileName;
|
||||
link.href = window.URL.createObjectURL(blobObj);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
link.addEventListener('click', function() {
|
||||
link.download = fileName;
|
||||
link.href = dataURL;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 更换布局配置项
|
||||
* @param {object} cfg 新布局配置项
|
||||
|
@ -495,11 +495,17 @@ export interface IGraph extends EventEmitter {
|
||||
toDataURL(type?: string, backgroundColor?: string): string;
|
||||
|
||||
/**
|
||||
* 画布导出图片
|
||||
* 画布导出图片,图片仅包含画布可见区域部分内容
|
||||
* @param {String} name 图片的名称
|
||||
*/
|
||||
downloadImage(name?: string, backgroundColor?: string): void;
|
||||
|
||||
/**
|
||||
* 导出包含全图的图片
|
||||
* @param {String} name 图片的名称
|
||||
*/
|
||||
downloadFullImage(name?: string, imageConfig?: { backgroundColor?: string, padding?: number | number[]}): void;
|
||||
|
||||
// TODO 需要添加布局配置类型
|
||||
/**
|
||||
* 更换布局配置项
|
||||
|
@ -1375,4 +1375,48 @@ describe('auto rotate label on edge', () => {
|
||||
expect(groupMatrix[6]).toBe(100);
|
||||
expect(groupMatrix[7]).toBe(120);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('auto rotate label on edge', () => {
|
||||
const graph = new Graph({
|
||||
container: div,
|
||||
width: 500,
|
||||
height: 500,
|
||||
modes: {
|
||||
default: ['drag-node', 'zoom-canvas', 'drag-canvas'],
|
||||
},
|
||||
});
|
||||
const data = {
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
x: 100,
|
||||
y: 200,
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
x: 800,
|
||||
y: 200,
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: 'edge1',
|
||||
target: 'node2',
|
||||
source: 'node1',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
it('downloadFullImage', () => {
|
||||
graph.data(data);
|
||||
graph.render();
|
||||
graph.on('canvas:click', evt => {
|
||||
graph.downloadFullImage('graph', {
|
||||
backgroundColor: '#fff',
|
||||
padding: [ 40, 10, 10, 10 ]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user