@antv/f6
Version:
A Mobile Graph Visualization Framework in JavaScript
619 lines (618 loc) • 24 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.registerGraph = exports.default = void 0;
var _tslib = require("tslib");
var _gMobile = require("@antv/g-mobile");
var _matrixUtil = require("@antv/matrix-util");
var _util = require("@antv/util");
var _f6Core = require("@antv/f6-core");
var _global = _interopRequireDefault(require("../global"));
var _controller = require("./controller");
var _domUtil = require("@antv/dom-util");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var registerGraph = function registerGraph(graphName, GraphFunction, G6) {
if (G6[graphName]) {
console.warn("The graph with the name ".concat(graphName, " exists already, it will be overridden"));
}
G6[graphName] = GraphFunction(G6);
return G6;
};
exports.registerGraph = registerGraph;
var Graph = /** @class */function (_super) {
(0, _tslib.__extends)(Graph, _super);
function Graph(cfg) {
var _this = _super.call(this, cfg) || this;
if (_this.get('renderer').startsWith('mini')) {
_this.set('context', cfg.context);
}
_super.prototype.init.call(_this);
var defaultNode = _this.get('defaultNode');
if (!defaultNode) {
_this.set('defaultNode', {
type: 'circle'
});
}
if (!defaultNode.type) {
defaultNode.type = 'circle';
_this.set('defaultNode', defaultNode);
}
_this.destroyed = false;
return _this;
}
// 这里是为了规避基类那个deepmix,等待架构调整
Graph.prototype.init = function () {};
Graph.prototype.emitEvent = function (event) {
var canvas = this.get('canvas');
event.type = event.type.toLowerCase();
canvas.registerEventCallback(event);
};
Graph.prototype.initLayoutController = function () {
var layoutController = new _controller.LayoutController(this);
this.set({
layoutController: layoutController
});
};
Graph.prototype.initEventController = function () {
var eventController = new _controller.EventController(this);
this.set({
eventController: eventController
});
if (this.get('renderer').startsWith('mini')) {
return;
}
var canvas = this.get('canvas');
var canvasDom = canvas.get('el');
'touchstart touchmove touchend touchcancel'.split(' ').forEach(function (key) {
canvasDom.addEventListener(key, canvas.registerEventCallback.bind(canvas), false);
});
};
Graph.prototype.initCanvas = function () {
var container = this.get('container');
if (container !== null && typeof container === 'string') {
container = document.getElementById(container);
this.set('container', container);
}
var renderer = this.get('renderer');
if (!container && !renderer.startsWith('mini')) {
throw new Error('invalid container');
}
var width = this.get('width');
var height = this.get('height');
var context = this.get('context');
var fitView = this.get('fitView');
var canvasCfg = {
container: container,
context: context,
width: width,
height: height,
renderer: renderer,
fitView: fitView
};
var pixelRatio = this.get('pixelRatio');
if (pixelRatio) {
canvasCfg.pixelRatio = pixelRatio;
}
var canvas = new _gMobile.Canvas(canvasCfg);
this.set('canvas', canvas);
};
Graph.prototype.initPlugins = function () {
var self = this;
(0, _util.each)(self.get('plugins'), function (plugin) {
if (!plugin.destroyed && plugin.initPlugin) {
plugin.initPlugin(self);
}
});
};
/**
* 返回可见区域的图的 dataUrl,用于生成图片
* @param {String} type 图片类型,可选值:"image/png" | "image/jpeg" | "image/webp" | "image/bmp"
* @param {string} backgroundColor 图片背景色
* @return {string} 图片 dataURL
*/
Graph.prototype.toDataURL = function (type, backgroundColor) {
var canvas = this.get('canvas');
var renderer = canvas.getRenderer();
var canvasDom = canvas.get('el');
if (!type) type = 'image/png';
var dataURL = '';
if (renderer === 'svg') {
var cloneNode = canvasDom.cloneNode(true);
var svgDocType = document.implementation.createDocumentType('svg', '-//W3C//DTD SVG 1.1//EN', 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd');
var svgDoc = document.implementation.createDocument('http://www.w3.org/2000/svg', 'svg', svgDocType);
svgDoc.replaceChild(cloneNode, svgDoc.documentElement);
var svgData = new XMLSerializer().serializeToString(svgDoc);
dataURL = "data:image/svg+xml;charset=utf8,".concat(encodeURIComponent(svgData));
} else {
var imageData = void 0;
var context = canvasDom.getContext('2d');
var width = this.get('width');
var height = this.get('height');
var compositeOperation = void 0;
if (backgroundColor) {
var pixelRatio = typeof window !== 'undefined' ? window.devicePixelRatio : 1;
imageData = context.getImageData(0, 0, width * pixelRatio, height * pixelRatio);
compositeOperation = context.globalCompositeOperation;
context.globalCompositeOperation = 'destination-over';
context.fillStyle = backgroundColor;
context.fillRect(0, 0, width, height);
}
dataURL = canvasDom.toDataURL(type);
if (backgroundColor) {
context.clearRect(0, 0, width, height);
context.putImageData(imageData, 0, 0);
context.globalCompositeOperation = compositeOperation;
}
}
return dataURL;
};
/**
* 返回整个图(包括超出可见区域的部分)的 dataUrl,用于生成图片
* @param {Function} callback 异步生成 dataUrl 完成后的回调函数,在这里处理生成的 dataUrl 字符串
* @param {String} type 图片类型,可选值:"image/png" | "image/jpeg" | "image/webp" | "image/bmp"
* @param {Object} imageConfig 图片配置项,包括背景色和上下左右的 padding
*/
Graph.prototype.toFullDataURL = function (callback, type, imageConfig) {
var bbox = this.get('group').getCanvasBBox();
var height = bbox.height;
var width = bbox.width;
var renderer = this.get('renderer');
var vContainerDOM = (0, _domUtil.createDom)('<id="virtual-image"></div>');
var backgroundColor = imageConfig ? imageConfig.backgroundColor : undefined;
var padding = imageConfig ? imageConfig.padding : undefined;
if (!padding) padding = [0, 0, 0, 0];else if ((0, _util.isNumber)(padding)) padding = [padding, padding, padding, padding];
var vHeight = height + padding[0] + padding[2];
var vWidth = width + padding[1] + padding[3];
var canvasOptions = {
container: vContainerDOM,
height: vHeight,
width: vWidth,
quickHit: true
};
var vCanvas = new _gMobile.Canvas(canvasOptions);
var group = this.get('group');
var vGroup = group.clone();
var matrix = (0, _util.clone)(vGroup.getMatrix());
if (!matrix) matrix = [1, 0, 0, 0, 1, 0, 0, 0, 1];
var centerX = (bbox.maxX + bbox.minX) / 2;
var centerY = (bbox.maxY + bbox.minY) / 2;
_matrixUtil.mat3.translate(matrix, matrix, [-centerX, -centerY]);
_matrixUtil.mat3.translate(matrix, matrix, [width / 2 + padding[3], height / 2 + padding[0]]);
vGroup.resetMatrix();
vGroup.setMatrix(matrix);
vCanvas.add(vGroup);
var vCanvasEl = vCanvas.get('el');
var dataURL = '';
if (!type) type = 'image/png';
setTimeout(function () {
if (renderer === 'svg') {
var cloneNode = vCanvasEl.cloneNode(true);
var svgDocType = document.implementation.createDocumentType('svg', '-//W3C//DTD SVG 1.1//EN', 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd');
var svgDoc = document.implementation.createDocument('http://www.w3.org/2000/svg', 'svg', svgDocType);
svgDoc.replaceChild(cloneNode, svgDoc.documentElement);
var svgData = new XMLSerializer().serializeToString(svgDoc);
dataURL = "data:image/svg+xml;charset=utf8,".concat(encodeURIComponent(svgData));
} else {
var imageData = void 0;
var context = vCanvasEl.getContext('2d');
var compositeOperation = void 0;
if (backgroundColor) {
var pixelRatio = typeof window !== 'undefined' ? window.devicePixelRatio : 1;
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;
}
}
if (callback) callback(dataURL);
}, 16);
};
/**
* 导出包含全图的图片
* @param {String} name 图片的名称
* @param {String} type 图片类型,可选值:"image/png" | "image/jpeg" | "image/webp" | "image/bmp"
* @param {Object} imageConfig 图片配置项,包括背景色和上下左右的 padding
*/
Graph.prototype.downloadFullImage = function (name, type, imageConfig) {
var _this = this;
var bbox = this.get('group').getCanvasBBox();
var height = bbox.height;
var width = bbox.width;
var renderer = this.get('renderer');
var vContainerDOM = (0, _domUtil.createDom)('<id="virtual-image"></div>');
var backgroundColor = imageConfig ? imageConfig.backgroundColor : undefined;
var padding = imageConfig ? imageConfig.padding : undefined;
if (!padding) padding = [0, 0, 0, 0];else if ((0, _util.isNumber)(padding)) padding = [padding, padding, padding, padding];
var vHeight = height + padding[0] + padding[2];
var vWidth = width + padding[1] + padding[3];
var canvasOptions = {
container: vContainerDOM,
height: vHeight,
width: vWidth
};
var vCanvas = new _gMobile.Canvas(canvasOptions);
var group = this.get('group');
var vGroup = group.clone();
var matrix = (0, _util.clone)(vGroup.getMatrix());
if (!matrix) matrix = [1, 0, 0, 0, 1, 0, 0, 0, 1];
var centerX = (bbox.maxX + bbox.minX) / 2;
var centerY = (bbox.maxY + bbox.minY) / 2;
_matrixUtil.mat3.translate(matrix, matrix, [-centerX, -centerY]);
_matrixUtil.mat3.translate(matrix, matrix, [width / 2 + padding[3], height / 2 + padding[0]]);
vGroup.resetMatrix();
vGroup.setMatrix(matrix);
vCanvas.add(vGroup);
var vCanvasEl = vCanvas.get('el');
if (!type) type = 'image/png';
setTimeout(function () {
var dataURL = '';
if (renderer === 'svg') {
var cloneNode = vCanvasEl.cloneNode(true);
var svgDocType = document.implementation.createDocumentType('svg', '-//W3C//DTD SVG 1.1//EN', 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd');
var svgDoc = document.implementation.createDocument('http://www.w3.org/2000/svg', 'svg', svgDocType);
svgDoc.replaceChild(cloneNode, svgDoc.documentElement);
var svgData = new XMLSerializer().serializeToString(svgDoc);
dataURL = "data:image/svg+xml;charset=utf8,".concat(encodeURIComponent(svgData));
} else {
var imageData = void 0;
var context = vCanvasEl.getContext('2d');
var compositeOperation = void 0;
if (backgroundColor) {
var pixelRatio = typeof window !== 'undefined' ? window.devicePixelRatio : 1;
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;
}
}
var link = document.createElement('a');
var fileName = (name || 'graph') + (renderer === 'svg' ? '.svg' : ".".concat(type.split('/')[1]));
_this.dataURLToImage(dataURL, renderer, link, fileName);
var e = document.createEvent('MouseEvents');
e.initEvent('click', false, false);
link.dispatchEvent(e);
}, 16);
};
/**
* 画布导出图片,图片仅包含画布可见区域部分内容
* @param {String} name 图片的名称
* @param {String} type 图片类型,可选值:"image/png" | "image/jpeg" | "image/webp" | "image/bmp"
* @param {string} backgroundColor 图片背景色
*/
Graph.prototype.downloadImage = function (name, type, backgroundColor) {
var _this = this;
var self = this;
if (self.isAnimating()) {
self.stopAnimate();
}
var canvas = self.get('canvas');
var renderer = canvas.getRenderer();
if (!type) type = 'image/png';
var fileName = (name || 'graph') + (renderer === 'svg' ? '.svg' : type.split('/')[1]);
var link = document.createElement('a');
setTimeout(function () {
var dataURL = self.toDataURL(type, backgroundColor);
_this.dataURLToImage(dataURL, renderer, link, fileName);
var e = document.createEvent('MouseEvents');
e.initEvent('click', false, false);
link.dispatchEvent(e);
}, 16);
};
Graph.prototype.dataURLToImage = function (dataURL, renderer, link, fileName) {
if (typeof window !== 'undefined') {
if (window.Blob && window.URL && renderer !== 'svg') {
var arr = dataURL.split(',');
var mime = '';
if (arr && arr.length > 0) {
var match = arr[0].match(/:(.*?);/);
// eslint-disable-next-line prefer-destructuring
if (match && match.length >= 2) mime = match[1];
}
var bstr = atob(arr[1]);
var n = bstr.length;
var u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
var blobObj_1 = new Blob([u8arr], {
type: mime
});
if (window.navigator['msSaveBlob']) {
window.navigator['msSaveBlob'](blobObj_1, fileName);
} else {
link.addEventListener('click', function () {
link.download = fileName;
link.href = window.URL.createObjectURL(blobObj_1);
});
}
} else {
link.addEventListener('click', function () {
link.download = fileName;
link.href = dataURL;
});
}
}
};
/**
* 添加插件
* @param {object} plugin 插件实例
*/
Graph.prototype.addPlugin = function (plugin) {
var self = this;
if (plugin.destroyed) {
return;
}
self.get('plugins').push(plugin);
plugin.initPlugin(self);
};
/**
* 添加插件
* @param {object} plugin 插件实例
*/
Graph.prototype.removePlugin = function (plugin) {
var plugins = this.get('plugins');
var index = plugins.indexOf(plugin);
if (index >= 0) {
plugin.destroyPlugin();
plugins.splice(index, 1);
}
};
Graph.prototype.isMiniNative = function () {
return this.get('renderer') === 'mini-native';
};
Graph.prototype.isMini = function () {
return this.get('renderer').startsWith('mini');
};
Graph.prototype.isBrowser = function () {
return this.get('renderer') === 'canvas';
};
/**
* 设置图片水印
* @param {string} imgURL 图片水印的url地址
* @param {WaterMarkerConfig} config 文本水印的配置项
* @param {any} waterCanvas 小程序canvas
*/
Graph.prototype.setImageWaterMarker = function (imgURL, config, waterCanvas) {
var _this = this;
//水印的设置合并
var waterMarkerConfig = (0, _util.deepMix)({}, _global.default.imageWaterMarkerConfig, config);
var width = waterMarkerConfig.width,
height = waterMarkerConfig.height,
image = waterMarkerConfig.image;
var rotate = image.rotate,
x = image.x,
y = image.y,
imgWidth = image.width,
imgHeight = image.height;
//mini
if (this.isMini() && !this.isMiniNative()) {
//设置属性为背景图
}
//mini-native
if (this.isMiniNative()) {
//设定水印canvas的宽高
waterCanvas.width = width ? width : this.get('width');
waterCanvas.height = height ? height : this.get('height');
//获取context
var waterCanvasContext_1 = waterCanvas.getContext('2d');
var createImage = this.get('extra').createImage;
var img_1 = createImage();
img_1.crossOrigin = 'anonymous';
img_1.src = imgURL;
img_1.onload = function () {
if (_this.destroyed) return;
//计算缩放比例
var scaleX = imgWidth / img_1.width;
var scaleY = imgHeight / img_1.height;
//旋转
waterCanvasContext_1.rotate(-(rotate * Math.PI) / 180);
//循环重复绘制
var pattern = waterCanvasContext_1.createPattern(img_1, 'repeat');
waterCanvasContext_1.fillStyle = pattern;
waterCanvasContext_1.scale(scaleX, scaleY);
//调整位置
waterCanvasContext_1.fillRect(-_this.get('width') * 2, -_this.get('height'), _this.get('width') * 10, _this.get('height') * 10);
_this.get('waterGroup').addShape('image', {
attrs: {
img: waterCanvas
}
});
};
}
//render
if (this.isBrowser()) {
var waterCanvas_1 = document.createElement('canvas');
//获取整个画布的宽高,以确定水印层的宽高
waterCanvas_1.width = width ? width : this.get('width');
waterCanvas_1.height = height ? height : this.get('height');
var context_1 = waterCanvas_1.getContext('2d');
var img_2 = new Image();
img_2.crossOrigin = 'anonymous';
img_2.src = imgURL;
img_2.onload = function () {
if (_this.destroyed) return;
//计算图片宽高的缩放比例
var scaleX = imgWidth / img_2.width;
var scaleY = imgHeight / img_2.height;
//旋转
context_1.rotate(-(rotate * Math.PI) / 180);
//循环重复绘制
var pattern = context_1.createPattern(img_2, 'repeat');
context_1.fillStyle = pattern;
context_1.scale(scaleX, scaleY);
//调整位置
context_1.fillRect(-_this.get('width') * 2, -_this.get('height'), _this.get('width') * 10, _this.get('height') * 10);
var water_img_url = waterCanvas_1.toDataURL();
_this.get('waterGroup').addShape('image', {
attrs: {
img: water_img_url
}
});
};
}
};
/**
* 设置文本水印
* @param {string[]} texts 水印的文本内容
* @param {WaterMarkerConfig} config 文本水印的配置项
*/
Graph.prototype.setTextWaterMarker = function (texts, config) {
var container = this.get('container');
if ((0, _util.isString)(container)) {
container = document.getElementById(container);
}
if (!container.style.position) {
container.style.position = 'relative';
}
var canvas = this.get('graphWaterMarker');
var waterMarkerConfig = (0, _util.deepMix)({}, _global.default.textWaterMarkerConfig, config);
var width = waterMarkerConfig.width,
height = waterMarkerConfig.height,
compatible = waterMarkerConfig.compatible,
text = waterMarkerConfig.text;
if (!canvas) {
var canvasCfg = {
container: container,
width: width,
height: height,
capture: false
};
var pixelRatio = this.get('pixelRatio');
if (pixelRatio) {
canvasCfg.pixelRatio = pixelRatio;
}
canvas = new _gMobile.Canvas(canvasCfg);
this.set('graphWaterMarker', canvas);
}
canvas.get('el').style.display = 'none';
var ctx = canvas.get('context');
var rotate = text.rotate,
fill = text.fill,
fontFamily = text.fontFamily,
fontSize = text.fontSize,
baseline = text.baseline,
x = text.x,
y = text.y,
lineHeight = text.lineHeight;
// 旋转20度
ctx.rotate(-rotate * Math.PI / 180);
// 设置文字样式
ctx.font = "".concat(fontSize, "px ").concat(fontFamily);
// 设置文字颜色
ctx.fillStyle = fill;
ctx.textBaseline = baseline;
for (var i = texts.length - 1; i >= 0; i--) {
// 将文字绘制到画布
ctx.fillText(texts[i], x, y + i * lineHeight);
}
// 恢复旋转角度
ctx.rotate(rotate * Math.PI / 180);
// 默认按照现代浏览器处理
if (!compatible) {
var box = document.querySelector('.g6-graph-watermarker');
if (!box) {
box = document.createElement('div');
box.className = 'g6-graph-watermarker';
}
box.style.cssText = "background-image: url(".concat(canvas.get('el').toDataURL('image/png'), ");background-repeat:repeat;position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:none;z-index:99;");
container.appendChild(box);
} else {
// 当需要兼容不支持 pointer-events属性的浏览器时,将 compatible 设置为 true
container.style.cssText = "background-image: url(".concat(canvas.get('el').toDataURL('image/png'), ");background-repeat:repeat;");
}
};
/**
* 销毁画布
*/
Graph.prototype.destroy = function () {
var _a;
(0, _util.each)(this.get('plugins'), function (plugin) {
plugin.destroyPlugin();
});
// destroy tooltip doms, removed when upgrade G6 4.0
var tooltipDOMs = this.get('tooltips');
if (tooltipDOMs) {
for (var i = 0; i < tooltipDOMs.length; i++) {
var container = tooltipDOMs[i];
if (!container) continue;
var parent_1 = container.parentElement;
if (!parent_1) continue;
parent_1.removeChild(container);
}
}
this.get('eventController').destroy();
(_a = this.get('layoutController')) === null || _a === void 0 ? void 0 : _a.destroy();
if (this.get('graphWaterMarker')) {
this.get('graphWaterMarker').destroy();
}
if (typeof document !== 'undefined' && document.querySelector && document.querySelector('.g6-graph-watermarker')) {
document.querySelector('.g6-graph-watermarker').remove();
}
_super.prototype.destroy.call(this);
};
// 初始化所有 Group
Graph.prototype.initGroups = function () {
var canvas = this.get('canvas');
var group = canvas.addGroup({
id: 'root',
className: _global.default.rootContainerClassName
});
var waterGroup = canvas.addGroup({
id: 'water',
className: _global.default.waterContainerClassName
});
if (this.get('groupByTypes')) {
var edgeGroup = group.addGroup({
id: 'edge',
className: _global.default.edgeContainerClassName
});
var nodeGroup = group.addGroup({
id: 'node',
className: _global.default.nodeContainerClassName
});
var comboGroup = group.addGroup({
id: 'combo',
className: _global.default.comboContainerClassName
});
// 用于存储自定义的群组
comboGroup.toBack();
this.set({
nodeGroup: nodeGroup,
edgeGroup: edgeGroup,
comboGroup: comboGroup
});
}
var uiGroup = canvas.addGroup({
id: 'uiGroup',
className: _global.default.uiContainerClassName
});
var delegateGroup = group.addGroup({
id: 'delegate',
className: _global.default.delegateContainerClassName
});
this.set({
delegateGroup: delegateGroup
});
this.set('group', group);
this.set('uiGroup', uiGroup);
this.set('waterGroup', waterGroup);
};
return Graph;
}(_f6Core.AbstractGraph);
var _default = Graph;
exports.default = _default;