UNPKG

chartx

Version:

Data Visualization Chart Library

1,389 lines (1,362 loc) 60.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _canvax = _interopRequireDefault(require("canvax")); var _index = _interopRequireDefault(require("../index")); var _tools = require("../../../utils/tools"); var _zoom = _interopRequireDefault(require("../../../utils/zoom")); function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } var _ = _canvax.default._, event = _canvax.default.event; var Rect = _canvax.default.Shapes.Rect; var Diamond = _canvax.default.Shapes.Diamond; var Line = _canvax.default.Shapes.Line; var Path = _canvax.default.Shapes.Path; var BrokenLine = _canvax.default.Shapes.BrokenLine; var Circle = _canvax.default.Shapes.Circle; var Arrow = _canvax.default.Shapes.Arrow; var iconWidth = 20; /** * 关系图中 包括了 配置,数据,和布局数据, * 默认用配置和数据可以完成绘图, 但是如果有布局数据,就绘图玩额外调用一次绘图,把布局数据传入修正布局效果 * * relation 也好, tree也好, 最后都要转换成 nodes edges * */ var RelationBase = /*#__PURE__*/function (_GraphsBase) { function RelationBase(opt, app, preComp) { var _this; (0, _classCallCheck2.default)(this, RelationBase); _this = _callSuper(this, RelationBase, [opt, app]); _this.type = "relation"; _.extend(true, _this, (0, _tools.getDefaultProps)(RelationBase.defaultProps()), opt); _this.domContainer = app.canvax.domView; _this.induce = null; _this.init(preComp); return _this; } (0, _inherits2.default)(RelationBase, _GraphsBase); return (0, _createClass2.default)(RelationBase, [{ key: "init", value: function init(preComp) { this._initInduce(); this.nodesSp = new _canvax.default.Display.Sprite({ id: "nodesSp" }); this.nodesContentSp = new _canvax.default.Display.Sprite({ id: "nodesContentSp" }); this.edgesSp = new _canvax.default.Display.Sprite({ id: "edgesSp" }); this.arrowsSp = new _canvax.default.Display.Sprite({ id: "arrowsSp" }); this.labelsSp = new _canvax.default.Display.Sprite({ id: "labelsSp" }); this.graphsSp = new _canvax.default.Display.Sprite({ id: "graphsSp" }); //这个view和induce是一一对应的,在induce上面执行拖拽和滚轮缩放,操作的目标元素就是graphsView this.graphsView = new _canvax.default.Display.Sprite({ id: "graphsView" }); this.graphsSp.addChild(this.edgesSp); this.graphsSp.addChild(this.nodesSp); this.graphsSp.addChild(this.nodesContentSp); this.graphsSp.addChild(this.arrowsSp); this.graphsSp.addChild(this.labelsSp); this.graphsView.addChild(this.graphsSp); this.sprite.addChild(this.graphsView); if (preComp.zoom) { this.preGraphsSpPosition = preComp.graphsSpPosition; this.zoom = preComp.zoom; this.offset(); } else { this.zoom = new _zoom.default(); } } //这个node是放在 nodes 和 edges 中的数据结构 }, { key: "getDefNode", value: function getDefNode() { var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var node = _objectSpread({ type: "relation", //tree中会覆盖为tree iNode: 0, rowData: null, key: "", content: '', ctype: 'canvas', //下面三个属性在_setElementAndSize中设置 contentElement: null, //外面传的layout数据可能没有element,widget的时候要检测下 width: null, height: null, boundingClientWidth: 0, //通过width,然后看有多少icon,计算出来用于layout计算的width //这个在layout的时候设置 x: null, y: null, shapeType: null, focused: false, selected: false }, opt); return node; } }, { key: "checkNodeSizeForShapeType", value: function checkNodeSizeForShapeType(node) { //如果是菱形,还需要重新调整新的尺寸 if (node.shapeType == 'diamond') { //因为node的尺寸前面计算出来的是矩形的尺寸,如果是菱形的话,这里就是指内接矩形的尺寸, //需要换算成外接矩形的尺寸 var includedAngle = this.node.includedAngle / 2; var includeRad = includedAngle * Math.PI / 180; var boundingClientWidth = node.boundingClientWidth, height = node.height; node._innerBound = { width: boundingClientWidth, height: height }; var newWidthDiff = height / Math.tan(includeRad); var newHeightDiff = boundingClientWidth * Math.tan(includeRad); //在内接矩形基础上扩展出来的外界矩形 var newWidth = boundingClientWidth + newWidthDiff; var newHeight = height + newHeightDiff; //node上面记录的width 和 height 永远是内容的 高宽, 但是像 diamond 等, 布局的时候的bound是要计算一个新的 //布局的时候, 布局算法要优先取 layoutWidth 和 layoutHeight node.boundingClientWidth = newWidth; node.height = newHeight; } ; } }, { key: "_initInduce", value: function _initInduce() { var me = this; this.induce = new Rect({ id: "induce", context: { width: 0, height: 0, fillStyle: "#000000", globalAlpha: 0 } }); this.sprite.addChild(this.induce); var _mosedownIng = false; //滚轮缩放相关 var _wheelHandleTimeLen = 32; //16*2 var _wheelHandleTimeer = null; var _deltaY = 0; this.induce.on(event.types.get(), function (e) { if (me.status.transform.enabled) { var _contextmenu = me.app.getComponent({ name: 'contextmenu' }); if (!_contextmenu || !_contextmenu.isShow) { e.preventDefault(); var point = e.target.localToGlobal(e.point, me.sprite); //鼠标拖拽移动 if (e.type == "mousedown") { me.induce.toFront(); _mosedownIng = true; me.app.canvax.domView && (me.app.canvax.domView.style.cursor = "move"); me.zoom.mouseMoveTo(point); } ; if (e.type == "mouseup" || e.type == "mouseout") { me.induce.toBack(); _mosedownIng = false; me.app.canvax.domView && (me.app.canvax.domView.style.cursor = ''); } ; if (e.type == "mousemove") { if (_mosedownIng) { var _me$zoom$move = me.zoom.move(point), x = _me$zoom$move.x, y = _me$zoom$move.y; me.graphsView.context.x = parseInt(x); me.graphsView.context.y = parseInt(y); } } ; //滚轮缩放 if (e.type == "wheel") { //console.log( _deltaY, e.deltaY ) if (Math.abs(e.deltaY) > Math.abs(_deltaY)) { _deltaY = e.deltaY; } ; if (!_wheelHandleTimeer) { _wheelHandleTimeer = setTimeout(function () { if (me.status.transform.wheelAction == 'offset') { //移动的话用offset,偏移多少像素 var offsetPoint = { x: -e.deltaX * 2, y: -e.deltaY * 2 }; var leftDiss = parseInt(me.graphsView.context.x + me.graphsSp.context.x + me.data.viewPort.maxLeft + offsetPoint.x + me.app.padding.left); if (leftDiss < 0) { offsetPoint.x = parseInt(offsetPoint.x - leftDiss); } var rightDiss = parseInt(me.graphsView.context.x + me.graphsSp.context.x + me.data.viewPort.maxRight + offsetPoint.x + me.app.padding.right); if (rightDiss >= me.app.width) { offsetPoint.x = parseInt(offsetPoint.x - (rightDiss - me.app.width)); } var topDiss = parseInt(me.graphsView.context.y + me.graphsSp.context.y + me.data.viewPort.maxTop + offsetPoint.y + me.app.padding.top); if (topDiss < 0) { offsetPoint.y = parseInt(offsetPoint.y - topDiss); } var bottomDiss = parseInt(me.graphsView.context.y + me.graphsSp.context.y + me.data.viewPort.maxBottom + offsetPoint.y + me.app.padding.bottom); if (bottomDiss >= me.app.height) { offsetPoint.y = parseInt(offsetPoint.y - (bottomDiss - me.app.height)); } //console.log( offsetPoint ) var _me$zoom$offset = me.zoom.offset(offsetPoint), _x = _me$zoom$offset.x, _y = _me$zoom$offset.y; //me.zoom.move( {x:zx, y:zy} ); me.graphsView.context.x = _x; me.graphsView.context.y = _y; } if (me.status.transform.wheelAction == 'scale') { // 缩放 var _me$zoom$wheel = me.zoom.wheel(e, point), scale = _me$zoom$wheel.scale, _x2 = _me$zoom$wheel.x, _y2 = _me$zoom$wheel.y; me.graphsView.context.x = _x2; me.graphsView.context.y = _y2; me.graphsView.context.scaleX = scale; me.graphsView.context.scaleY = scale; me.status.transform.scale = scale; } _wheelHandleTimeer = null; _deltaY = 0; }, _wheelHandleTimeLen); } ; } ; } } ; //induce 的 事件都 在 graph 上面派发,可以用 e.eventInfo = { trigger: me, iNode: -1 //TODO:这里设置了的话,会导致多graphs里获取不到别的graphs的nodes信息了 //nodes : me.getNodesAt( this.iNode ) }; me.app.fire(e.type, e); }); } }, { key: "_resetData", value: function _resetData(data, dataTrigger) { var _this2 = this; var me = this; this._preData = this.data; return new Promise(function (resolve) { _this2.initData(data, dataTrigger).then(function (_data) { _this2.data = _data; _this2.layoutData(); var _preNodes = _this2._preData && _this2._preData.nodes || []; var _preEdges = _this2._preData && _this2._preData.edges || []; _.each(_preNodes, function (preNode) { var nodeData = _.find(me.data.nodes, function (node) { return preNode.key == node.key; }); if (!nodeData) { me._destroy(preNode); } else { //如果找到了,要从前面 复制几个属性过来 nodeData.focused = preNode.focused; nodeData.selected = preNode.selected; //TODO:把原来的对象的 contentElement 搞过来, 就可以减少getChild的消耗 //还有个更加重要的原因,这段代码解决了展开收起的抖动 if (nodeData.ctype == preNode.ctype) { //类型没变, 就可以用同一个 contentElement nodeData.contentElement = preNode.contentElement; } ; } }); _.each(_preEdges, function (preEdge) { if (!_.find(me.data.edges, function (edge) { return preEdge.key.join('_') == edge.key.join('_'); })) { me._destroy(preEdge); } }); _this2.widget(); if (dataTrigger) { var origin = dataTrigger.origin || (dataTrigger.params || {}).origin; //兼容老的配置里面没有params,直接传origin的情况 //钉住某个node为参考点(不移动) if (origin != undefined) { var preOriginNode = _.find(_preNodes, function (node) { return node.key == origin; }); var originNode = _.find(_this2.data.nodes, function (node) { return node.key == origin; }); if (preOriginNode && originNode) { var offsetPos = { x: parseInt(preOriginNode.x) - parseInt(originNode.x), y: parseInt(preOriginNode.y) - parseInt(originNode.y) }; var _this2$zoom$offset = _this2.zoom.offset(offsetPos), x = _this2$zoom$offset.x, y = _this2$zoom$offset.y; me.graphsView.context.x = parseInt(x); me.graphsView.context.y = parseInt(y); } ; } ; } ; resolve(); }); }); } }, { key: "_destroy", value: function _destroy(item) {} }, { key: "_drawEdges", value: function _drawEdges() { var me = this; _.each(this.data.edges, function (edge) { //console.log(edge.points) var lineShapeOpt = me._getLineShape(edge, me.line.inflectionRadius); var key = edge.key.join('_'); var type = lineShapeOpt.type; var path = lineShapeOpt.path; var pointList = lineShapeOpt.pointList; var shape = type == 'path' ? Path : BrokenLine; var lineWidth = me.getProp(me.line.lineWidth, edge); var strokeStyle = me.getProp(me.line.strokeStyle, edge); var lineType = me.getProp(me.line.lineType, edge); var cursor = me.getProp(me.line.cursor, edge); var edgeId = 'edge_' + key; var _path = me.edgesSp.getChildById(edgeId); if (_path) { if (type == 'path') { _path.context.path = path; } if (type == 'brokenLine') { _path.context.pointList = pointList; } _path.context.lineWidth = lineWidth; _path.context.strokeStyle = strokeStyle; _path.context.lineType = lineType; } else { var _ctx = { lineWidth: lineWidth, strokeStyle: strokeStyle, lineType: lineType, cursor: cursor }; if (type == 'path') { _ctx.path = path; } if (type == 'brokenLine') { //_ctx.smooth = true; //_ctx.curvature = 0.25; _ctx.pointList = pointList; } _path = new shape({ id: edgeId, context: _ctx }); _path.on(event.types.get(), function (e) { var node = this.nodeData; node.__no_value = true; e.eventInfo = { trigger: me.line, nodes: [node] }; me.app.fire(e.type, e); }); me.edgesSp.addChild(_path); } ; edge.pathElement = _path; _path.nodeData = edge; //edge也是一个node数据 var arrowControl = edge.points.slice(-2, -1)[0]; if (me.line.shapeType == "bezier") { if (me.rankdir == "TB" || me.rankdir == "BT") { arrowControl.x += (edge.source.x - edge.target.x) / 20; } if (me.rankdir == "LR" || me.rankdir == "RL") { arrowControl.y += (edge.source.y - edge.target.y) / 20; } } ; var edgeLabelId = 'label_' + key; var enabled = me.getProp(me.line.edgeLabel.enabled, edge); if (enabled) { var textAlign = me.getProp(me.node.content.textAlign, edge); var textBaseline = me.getProp(me.node.content.textBaseline, edge); var fontSize = me.getProp(me.line.edgeLabel.fontSize, edge); var fontColor = me.getProp(me.line.edgeLabel.fontColor, edge); // let offsetX = me.getProp( me.line.edgeLabel.offsetX , edge ); // let offsetY = me.getProp( me.line.edgeLabel.offsetY , edge ); var offset = me.getProp(me.line.icon.offset, edge); if (!offset) { //default 使用edge.x edge.y 也就是edge label的位置 offset = { x: edge.x, y: edge.y }; } ; var _edgeLabel = me.labelsSp.getChildById(edgeLabelId); if (_edgeLabel) { _edgeLabel.resetText(edge.content); _edgeLabel.context.x = offset.x; _edgeLabel.context.y = offset.y; _edgeLabel.context.fontSize = fontSize; _edgeLabel.context.fillStyle = fontColor; _edgeLabel.context.textAlign = textAlign; _edgeLabel.context.textBaseline = textBaseline; } else { _edgeLabel = new _canvax.default.Display.Text(edge.content, { id: edgeLabelId, context: { x: offset.x, y: offset.y, fontSize: fontSize, fillStyle: fontColor, textAlign: textAlign, textBaseline: textBaseline } }); _edgeLabel.on(event.types.get(), function (e) { var node = this.nodeData; node.__no_value = true; e.eventInfo = { trigger: me.line, nodes: [node] }; me.app.fire(e.type, e); }); me.labelsSp.addChild(_edgeLabel); } edge.labelElement = _edgeLabel; _edgeLabel.nodeData = edge; } ; var edgeIconEnabled = me.getProp(me.line.icon.enabled, edge); if (edgeIconEnabled) { var _chartCode = me.getProp(me.line.icon.charCode, edge); var charCode = String.fromCharCode(parseInt(_chartCode, 16)); if (_chartCode != '') { var _lineWidth = me.getProp(me.line.icon.lineWidth, edge); var _strokeStyle = me.getProp(me.line.icon.strokeStyle, edge); var fontFamily = me.getProp(me.line.icon.fontFamily, edge); var _fontSize = me.getProp(me.line.icon.fontSize, edge); var _fontColor = me.getProp(me.line.icon.fontColor, edge); var background = me.getProp(me.line.icon.background, edge); var _textAlign = 'center'; var _textBaseline = 'middle'; var _offset2 = me.getProp(me.line.icon.offset, edge); var offsetX = me.getProp(me.line.icon.offsetX, edge); var offsetY = me.getProp(me.line.icon.offsetY, edge); if (!_offset2) { //default 使用edge.x edge.y 也就是edge label的位置 _offset2 = { x: parseInt(edge.x) + offsetX, y: parseInt(edge.y) + offsetY }; } ; var _iconBackCtx = { x: _offset2.x, y: _offset2.y - 1, r: parseInt(_fontSize * 0.5) + 2, fillStyle: background, strokeStyle: _strokeStyle, lineWidth: _lineWidth }; var edgeIconBackId = 'edge_item_icon_back_' + key; var _iconBack = me.labelsSp.getChildById(edgeIconBackId); if (_iconBack) { //_.extend( true, _iconBack.context, _iconBackCtx ) Object.assign(_iconBack.context, _iconBackCtx); } else { _iconBack = new Circle({ id: edgeIconBackId, context: _iconBackCtx }); me.labelsSp.addChild(_iconBack); } ; edge.edgeIconBack = _iconBack; _iconBack.nodeData = edge; var edgeIconId = 'edge_item_icon_' + key; var _edgeIcon = me.labelsSp.getChildById(edgeIconId); if (_edgeIcon) { _edgeIcon.resetText(charCode); _edgeIcon.context.x = _offset2.x; _edgeIcon.context.y = _offset2.y; _edgeIcon.context.fontSize = _fontSize; _edgeIcon.context.fillStyle = _fontColor; _edgeIcon.context.textAlign = _textAlign; _edgeIcon.context.textBaseline = _textBaseline; _edgeIcon.context.fontFamily = fontFamily; //_edgeIcon.context.lineWidth = lineWidth; //_edgeIcon.context.strokeStyle = strokeStyle; } else { _edgeIcon = new _canvax.default.Display.Text(charCode, { id: edgeIconId, context: { x: _offset2.x, y: _offset2.y, fillStyle: _fontColor, cursor: 'pointer', fontSize: _fontSize, textAlign: _textAlign, textBaseline: _textBaseline, fontFamily: fontFamily } }); _edgeIcon.on(event.types.get(), function (e) { var node = this.nodeData; node.__no_value = true; var trigger = me.line; if (me.line.icon['on' + e.type]) { trigger = me.line.icon; } ; e.eventInfo = { trigger: trigger, nodes: [node] }; me.app.fire(e.type, e); }); me.labelsSp.addChild(_edgeIcon); } edge.edgeIconElement = _edgeIcon; _edgeIcon.nodeData = edge; } } ; if (me.line.arrow.enabled) { var arrowId = "arrow_" + key; var _arrow = me.arrowsSp.getChildById(arrowId); if (_arrow) { //arrow 只监听了x y 才会重绘,,,暂时只能这样处理,手动的赋值control.x control.y //而不是直接把 arrowControl 赋值给 control _arrow.context.x = me.line.arrow.offsetX; _arrow.context.y = me.line.arrow.offsetY; _arrow.context.fillStyle = strokeStyle; _arrow.context.control.x = arrowControl.x; _arrow.context.control.y = arrowControl.y; _arrow.context.point = edge.points.slice(-1)[0]; _arrow.context.strokeStyle = strokeStyle; _arrow.context.fillStyle = strokeStyle; // _.extend(true, _arrow, { // control: arrowControl, // point: edge.points.slice(-1)[0], // strokeStyle: strokeStyle // } ); } else { _arrow = new Arrow({ id: arrowId, context: { x: me.line.arrow.offsetX, y: me.line.arrow.offsetY, control: arrowControl, point: edge.points.slice(-1)[0], strokeStyle: strokeStyle, fillStyle: strokeStyle } }); me.arrowsSp.addChild(_arrow); } ; edge.arrowElement = _arrow; } ; }); } }, { key: "_drawNode", value: function _drawNode(node) { var me = this; var shape = Rect; var nodeId = "node_" + node.key; var cursor = me.node.cursor; var _me$_getNodeStyle = me._getNodeStyle(node), lineWidth = _me$_getNodeStyle.lineWidth, fillStyle = _me$_getNodeStyle.fillStyle, strokeStyle = _me$_getNodeStyle.strokeStyle, radius = _me$_getNodeStyle.radius, shadowOffsetX = _me$_getNodeStyle.shadowOffsetX, shadowOffsetY = _me$_getNodeStyle.shadowOffsetY, shadowBlur = _me$_getNodeStyle.shadowBlur, shadowColor = _me$_getNodeStyle.shadowColor; var context = { x: parseInt(node.x) - parseInt(node.boundingClientWidth / 2), y: parseInt(node.y) - parseInt(node.height / 2), width: node.boundingClientWidth, height: node.height, cursor: cursor, lineWidth: lineWidth, fillStyle: fillStyle, strokeStyle: strokeStyle, radius: radius, shadowOffsetX: shadowOffsetX, shadowOffsetY: shadowOffsetY, shadowBlur: shadowBlur, shadowColor: shadowColor }; if (node.shapeType == 'diamond') { shape = Diamond; context = { x: parseInt(node.x), y: parseInt(node.y), cursor: cursor, innerRect: node._innerBound, lineWidth: lineWidth, fillStyle: fillStyle, strokeStyle: strokeStyle, shadowOffsetX: shadowOffsetX, shadowOffsetY: shadowOffsetY, shadowBlur: shadowBlur, shadowColor: shadowColor }; } ; var _boxShape = me.nodesSp.getChildById(nodeId); if (_boxShape) { _.extend(_boxShape.context, context); _boxShape.nodeData = node; _boxShape.fire('transform'); } else { _boxShape = new shape({ id: nodeId, hoverClone: false, context: context }); me.nodesSp.addChild(_boxShape); _boxShape.on(event.types.get(), function (e) { var node = this.nodeData; node.__no_value = true; e.eventInfo = { trigger: me.node, nodes: [node] }; if (me.node.focus.enabled) { if (e.type == "mouseover") { me.focusAt(this.nodeData); } if (e.type == "mouseout") { me.unfocusAt(this.nodeData); } } ; if (me.node.select.enabled && me.node.select.triggerEventType.indexOf(e.type) > -1) { //如果开启了图表的选中交互 //TODO:这里不能 var onbefore = me.node.select.onbefore; var onend = me.node.select.onend; if (!onbefore || typeof onbefore == 'function' && onbefore.apply(me, [this.nodeData, e]) !== false) { if (this.nodeData.selected) { //说明已经选中了 me.unselectAt(this.nodeData); } else { me.selectAt(this.nodeData); } onend && typeof onend == 'function' && onend.apply(me, [this.nodeData, e]); } } ; me.app.fire(e.type, e); }); _boxShape.nodeData = node; } ; node.shapeElement = _boxShape; if (me.node.select.list.indexOf(node.key) > -1) { me.selectAt(node); } ; if (node.ctype == "canvas") { node.contentElement.context.visible = true; } ; _boxShape.on("transform", function () { var node = this.nodeData; if (node.ctype == "canvas") { node.contentElement.context.x = parseInt(node.x - node.boundingClientWidth / 2 + me.node.padding + (node.preIconCharCode ? iconWidth : 0)); node.contentElement.context.y = parseInt(node.y); } else if (node.ctype == "html") { var devicePixelRatio = typeof window !== 'undefined' ? window.devicePixelRatio : 1; var contentMatrix = _boxShape.worldTransform.clone(); contentMatrix = contentMatrix.scale(1 / devicePixelRatio, 1 / devicePixelRatio); node.contentElement.style.transform = "matrix(" + contentMatrix.toArray().join() + ")"; node.contentElement.style.transformOrigin = "left top"; //修改为左上角为旋转中心点来和canvas同步 if (node.shapeType == 'diamond') { //菱形的位置 node.contentElement.style.left = -parseInt((node.boundingClientWidth - node._innerBound.width) / 2 * me.status.transform.scale) + "px"; node.contentElement.style.top = -parseInt(node.height / 2 * me.status.transform.scale) + "px"; } ; node.contentElement.style.visibility = "visible"; } ; }); } }, { key: "_getNodeStyle", value: function _getNodeStyle(nodeData, targetPath) { var me = this; var radius = _.flatten([me.getProp(me.node.radius, nodeData)]); var target = me.node; if (targetPath == 'select') { target = me.node.select; } if (targetPath == 'focus') { target = me.node.focus; } var lineWidth = me.getProp(target.lineWidth, nodeData); var fillStyle = me.getProp(target.fillStyle, nodeData); var strokeStyle = me.getProp(target.strokeStyle, nodeData); var shadowOffsetX = me.getProp(target.shadow.shadowOffsetX, nodeData); var shadowOffsetY = me.getProp(target.shadow.shadowOffsetY, nodeData); var shadowBlur = me.getProp(target.shadow.shadowBlur, nodeData); var shadowColor = me.getProp(target.shadow.shadowColor, nodeData); return { lineWidth: lineWidth, fillStyle: fillStyle, strokeStyle: strokeStyle, radius: radius, shadowOffsetX: shadowOffsetX, shadowOffsetY: shadowOffsetY, shadowBlur: shadowBlur, shadowColor: shadowColor }; } }, { key: "_setNodeStyle", value: function _setNodeStyle(nodeData, targetPath) { var _this$_getNodeStyle = this._getNodeStyle(nodeData, targetPath), lineWidth = _this$_getNodeStyle.lineWidth, fillStyle = _this$_getNodeStyle.fillStyle, strokeStyle = _this$_getNodeStyle.strokeStyle, shadowOffsetX = _this$_getNodeStyle.shadowOffsetX, shadowOffsetY = _this$_getNodeStyle.shadowOffsetY, shadowBlur = _this$_getNodeStyle.shadowBlur, shadowColor = _this$_getNodeStyle.shadowColor; if (nodeData.shapeElement && nodeData.shapeElement.context) { var ctx = nodeData.shapeElement.context; ctx.lineWidth = lineWidth; ctx.fillStyle = fillStyle; ctx.strokeStyle = strokeStyle; ctx.shadowOffsetX = shadowOffsetX; ctx.shadowOffsetY = shadowOffsetY; ctx.shadowBlur = shadowBlur; ctx.shadowColor = shadowColor; } } //画布偏移量 }, { key: "offset", value: function offset() { var _offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { x: 0, y: 0 }; var _this$zoom$offset = this.zoom.offset(_offset), x = _this$zoom$offset.x, y = _this$zoom$offset.y; this.graphsView.context.x = parseInt(x); this.graphsView.context.y = parseInt(y); } //把某个节点移动到居中位置 }, { key: "setNodeToCenter", value: function setNodeToCenter(key) { var nodeData = this.getNodeDataAt(key); if (nodeData) { var globalPos = nodeData.shapeElement.localToGlobal(); var toGlobalPos = { x: this.app.width / 2 - nodeData.width / 2, y: this.app.height / 2 - nodeData.height / 2 }; var toCenterOffset = { x: parseInt(toGlobalPos.x - globalPos.x), y: parseInt(toGlobalPos.y - globalPos.y) }; this.offset(toCenterOffset); } } }, { key: "focusAt", value: function focusAt(key) { var nodeData = this.getNodeDataAt(key); if (nodeData) { !nodeData.selected && this._setNodeStyle(nodeData, 'focus'); nodeData.focused = true; } } }, { key: "unfocusAt", value: function unfocusAt(key) { var nodeData = this.getNodeDataAt(key); if (nodeData) { !nodeData.selected && this._setNodeStyle(nodeData); nodeData.focused = false; } } }, { key: "selectAt", value: function selectAt(key) { var nodeData = this.getNodeDataAt(key); if (nodeData) { this._setNodeStyle(nodeData, 'select'); nodeData.selected = true; if (this.node.select.list.indexOf(nodeData.key) == -1) { this.node.select.list.push(nodeData.key); } } } }, { key: "selectAll", value: function selectAll() { var _this3 = this; this.data.nodes.forEach(function (nodeData) { _this3.selectAt(nodeData); }); } }, { key: "unselectAt", value: function unselectAt(key) { var nodeData = this.getNodeDataAt(key); if (nodeData) { nodeData.focused ? this._setNodeStyle(nodeData, 'focus') : this._setNodeStyle(nodeData); nodeData.selected = false; var selectedKeyInd = this.node.select.list.indexOf(nodeData.key); if (selectedKeyInd > -1) { this.node.select.list.splice(selectedKeyInd, 1); } } } }, { key: "unselectAll", value: function unselectAll() { var _this4 = this; this.data.nodes.forEach(function (nodeData) { _this4.unselectAt(nodeData); }); //有些被折叠了的selected也要清除干净的话,必须多做一步list = [] this.node.select.list = []; } }, { key: "getNodeDataAt", value: function getNodeDataAt(key) { if (key.type && (key.type == "relation" || key.type == "tree")) { return key; } ; if (typeof key == 'string') { var keys = key.split(','); if (keys.length == 1) { return this.data.nodes.find(function (item) { return item.key == key; }); } if (keys.length == 2) { return this.data.edges.find(function (item) { return item.key.join() == keys.join(); }); } } ; } /** * * @param {shapeType,points} edge * @param {number} inflectionRadius 拐点的圆角半径 */ }, { key: "_getLineShape", value: function _getLineShape(edge, inflectionRadius) { var points = edge.points; var line = { type: 'path', // pah or brokenLine pointList: null, path: str }; var head = points.splice(0, 1)[0]; var str = "M" + head.x + " " + head.y; var start = points[0]; str += ",L" + start.x + " " + start.y; var end = points.slice(-1)[0]; if (edge.shapeType == "bezier") { if (points.length == 3) { str += ",Q" + points[1].x + " " + points[1].y + " " + end.x + " " + end.y; } if (points.length == 4) { str += ",C" + points[1].x + " " + points[1].y + " " + points[2].x + " " + points[2].y + " " + end.x + " " + end.y; } if (points.length >= 5) { line.type = 'brokenLine'; line.pointList = points.map(function (item) { return [item.x, item.y]; }); return line; } } ; if (edge.shapeType == "brokenLine") { if (points.length == 3) { points.splice(1, 0, { x: points[1].x, y: start.y }); } _.each(points, function (point, i) { if (i) { if (inflectionRadius && i < points.length - 1) { //圆角连线 var prePoint = points[i - 1]; var nextPoint = points[i + 1]; //要从这个点到上个点的半径距离,已point为控制点,绘制nextPoint的半径距离 var radius = inflectionRadius; //radius要做次二次校验,取radius 以及 point 和prePoint距离以及和 nextPoint 的最小值 //let _disPre = Math.abs(Math.sqrt( (prePoint.x - point.x)*(prePoint.x - point.x) + (prePoint.y - point.y)*(prePoint.y - point.y) )); //let _disNext = Math.abs(Math.sqrt( (nextPoint.x - point.x)*(nextPoint.x - point.x) + (nextPoint.y - point.y)*(nextPoint.y - point.y) )); var _disPre = Math.max(Math.abs(prePoint.x - point.x) / 2, Math.abs(prePoint.y - point.y) / 2); var _disNext = Math.max(Math.abs(nextPoint.x - point.x) / 2, Math.abs(nextPoint.y - point.y) / 2); radius = _.min([radius, _disPre, _disNext]); //console.log(Math.atan2( point.y - prePoint.y , point.x - prePoint.x ),Math.atan2( nextPoint.y - point.y , nextPoint.x - point.x )) if (point.x == prePoint.x && point.y == prePoint.y || point.x == nextPoint.x && point.y == nextPoint.y || Math.atan2(point.y - prePoint.y, point.x - prePoint.x) == Math.atan2(nextPoint.y - point.y, nextPoint.x - point.x)) { //如果中间的这个点 , 和前后的点在一个直线上面,就略过 return; } else { var getPointOf = function getPointOf(p) { var _atan2 = Math.atan2(p.y - point.y, p.x - point.x); return { x: point.x + radius * Math.cos(_atan2), y: point.y + radius * Math.sin(_atan2) }; }; ; var bezierBegin = getPointOf(prePoint); var bezierEnd = getPointOf(nextPoint); str += ",L" + bezierBegin.x + " " + bezierBegin.y + ",Q" + point.x + " " + point.y + " " + bezierEnd.x + " " + bezierEnd.y; } } else { //直角连线 str += ",L" + point.x + " " + point.y; } ; } }); } ; if (edge.target.shapeType == 'underLine') { var w = edge.target.rowData._node.boundingClientWidth; var x = parseInt(edge.target.x) + parseInt(w / 2); str += ",L" + x + " " + (parseInt(edge.target.y) + parseInt(edge.target.height / 2)); } ; line.path = str; //str += "z" return line; } /** * 字符串是否含有html标签的检测 */ }, { key: "_checkHtml", value: function _checkHtml(str) { var reg = /<[^>]+>/g; return reg.test(str); } }, { key: "_getContent", value: function _getContent(rowData) { var me = this; var _c; //this.node.content; var field = this.node.content.field; if (this._isField(field)) { _c = rowData[field]; } ; if (me.node.content.format && _.isFunction(me.node.content.format)) { _c = me.node.content.format.apply(this, [_c, rowData]); } else { //否则用fieldConfig上面的 var _coord = me.app.getComponent({ name: 'coord' }); var fieldConfig = _coord.getFieldConfig(field); if (fieldConfig) { _c = fieldConfig.getFormatValue(_c); } ; } return _c; } }, { key: "_isField", value: function _isField(str) { return ~this.dataFrame.fields.indexOf(str); } }, { key: "_getEleAndsetCanvasSize", value: function _getEleAndsetCanvasSize(node) { var _this5 = this; var me = this; return new Promise(function (resolve) { var content = node.content; var width = me.getProp(me.node.width, node); if (!width && node.width) { width = node.width; } var height = me.getProp(me.node.height, node); if (!height && node.height) { height = node.height; } var fontColor = me.getProp(me.node.content.fontColor, node); if (node.rowData.fontColor) { fontColor = node.rowData.fontColor; } if (node.rowData.style && node.rowData.style.fontColor) { fontColor = node.rowData.style.fontColor; } var context = { fillStyle: fontColor, textAlign: me.getProp(me.node.content.textAlign, node), textBaseline: me.getProp(me.node.content.textBaseline, node), fontSize: me.getProp(me.node.content.fontSize, node) }; var contentLabelId = "content_label_" + node.key; var _contentLabel = node.contentElement || me.nodesContentSp.getChildById(contentLabelId); if (_contentLabel) { //已经存在的label _contentLabel.resetText(content); _.extend(_contentLabel.context, context); } else { //新创建text,根据 text 来计算node需要的width和height _contentLabel = new _canvax.default.Display.Text(content, { id: contentLabelId, context: context }); _contentLabel.context.visible = false; if (!_.isArray(node.key)) { me.nodesContentSp.addChild(_contentLabel); } ; } ; var inited; if (_this5.node.content.init && typeof _this5.node.content.init === 'function') { inited = _this5.node.content.init(node, _contentLabel); } ; var _handle = function _handle() { if (!width) { width = _contentLabel.getTextWidth() + me.getProp(me.node.padding, node) * me.status.transform.scale * 2; } ; if (!height) { height = _contentLabel.getTextHeight() + me.getProp(me.node.padding, node) * me.status.transform.scale * 2; } ; resolve({ contentElement: _contentLabel, width: parseInt(width), height: parseInt(height) }); }; if (inited && typeof inited.then == 'function') { inited.then(function () { _handle(); }); } else { _handle(); } }); } }, { key: "_getEleAndsetHtmlSize", value: function _getEleAndsetHtmlSize(node) { var _this6 = this; var me = this; return new Promise(function (resolve) { var content = node.content; var width = me.getProp(me.node.width, node); if (!width && me.node.rowData && node.rowData.width) { width = node.rowData.width; } var height = me.getProp(me.node.height, node); if (!height && me.node.rowData && node.rowData.height) { height = node.rowData.height; } var contentLabelClass = "__content_label_" + node.key; var _dom = node.contentElement || _this6.domContainer.getElementsByClassName(contentLabelClass)[0]; if (!_dom) { _dom = document.createElement("div"); _dom.className = "chartx_relation_node " + contentLabelClass; _dom.style.cssText += "; position:absolute;visibility:hidden;"; _this6.domContainer.appendChild(_dom); } // else { // _dom = _dom[0] // }; _dom.style.cssText += "; color:" + me.getProp(me.node.content.fontColor, node) + ";"; _dom.style.cssText += "; text-align:" + me.getProp(me.node.content.textAlign, node) + ";"; _dom.style.cssText += "; vertical-align:" + me.getProp(me.node.content.textBaseline, node) + ";"; //TODO 这里注释掉, 就让dom自己内部去控制padding吧 //_dom.style.cssText += "; padding:"+me.getProp(me.node.padding, node)+"px;"; _dom.innerHTML = content; var inited; if (_this6.node.content.init && typeof _this6.node.content.init === 'function') { inited = _this6.node.content.init(node, _dom); } ; var _handle = function _handle() { if (!width) { width = _dom.offsetWidth; // + me.getProp(me.node.padding, node) * me.status.transform.scale * 2; } ; if (!height) { height = _dom.offsetHeight; // + me.getProp(me.node.padding, node) * me.status.transform.scale * 2; } ; resolve({ contentElement: _dom, width: parseInt(width), height: parseInt(height) }); }; if (inited && typeof inited.then == 'function') { inited.then(function (opt) { _handle(); }); } else { _handle(); } ; }); } }, { key: "getNodesAt", value: function getNodesAt() {} }, { key: "getProp", value: function getProp(prop, nodeData) { var _prop = prop; if (this._isField(prop)) { _prop = nodeData.rowData[prop]; } else { if (_.isArray(prop)) { _prop = prop[nodeData.iNode]; } ; if (_.isFunction(prop)) { _prop = prop.apply(this, Array.prototype.slice.call(arguments).slice(1)); } ; } ; return _prop; } }], [{ key: "defaultProps", value: function defaultProps() { return { field: { detail: 'key字段设置', documentation: '', default: null }, //rankdir: "TB", //align: "DR", //nodesep: 0,//同级node之间的距离 //edgesep: 0, //ranksep: 0, //排与排之间的距离 rankdir: { detail: '布局方向', default: null }, ranksep: { detail: '排与排之间的距离', default: 40 }, nodesep: { detail: '同级node之间的距离', default: 20 }, node: { detail: '单个节点的配置', propertys: { shapeType: { detail: '节点图形,支持rect,diamond,underLine(adc用)', default: 'rect' }, maxWidth: { detail: '节点最大的width', default: 200 }, cursor: { detail: '节点的鼠标样式', default: 'pointer' }, width: { detail: '节点的width,默认null(系统自动计算), 也可以是个function,用户来计算每一个节点的width', default: null }, height: { detail: '节点的height,默认null(系统自动计算), 也可以是个function,用户来计算每一个节点的height', default: null }, radius: { detail: '圆角角度,对rect生效', default: 4 }, includedAngle: { detail: 'shapeType为 diamond (菱形)的时候生效,x方向的夹角', default: 60 }, fillStyle: { detail: '节点背景色', default: '#ffffff' }, lineWidth: { detail: '描边宽度', default: 1 }, strokeStyle: { detail: '描边颜色', default: '#e5e5e5' }, shadow: { detail: '阴影设置', propertys: { shadowOffsetX: { detail: 'x偏移量', default: 0 }, shadowOffsetY: { detail: 'y偏移量', default: 0 }, shadowBlur: { detail: '阴影模糊值', default: 0 }, shadowColor: { detail: '阴影颜色', default: '#000000' } } }, select: { detail: '选中效果', propertys: { enabled: { detail: '是否开启选中', default: false }, list: { detail: '选中的node.key的集合,外部传入可以选中', default: [] }, triggerEventType: { detail: '触发事件', default: 'click,tap' }, shadow: { detail: '选中效果的阴影设置', propertys: { shadowOffsetX: { detail: 'x偏移量', default: 0 }, shadowOffsetY: { detail: 'y偏移量', default: 0 }, shadowBlur: { detail: '阴影模糊值', default: 0 }, shadowColor: { detail: '阴影颜色', default: '#000000' } } }, fillStyle: { detail: 'hover节点背景色', default: '#ffffff' }, lineWidth: { detail: 'hover描边宽度', default: 1 }, strokeStyle: { detail: 'hover描边颜色', default: '#e5e5e5' }, onbefore: { detail: '执行select处理函数的前处理函数,返回false则取消执行select', default: null }, onend: { detail: '执行select处理函数的后处理函数', default: null }, content: { detail: '选中后节点内容配置', propertys: { fontColor: { detail: '内容文本颜色', default: '#666' }, fontSize: { detail: '内容文本大小(在canvas格式下有效)', default: 14 }, format: { detail: '内容格式化处理函数', default: null } } } } }, focus: { detail: 'hover效果', propertys: { enabled: { detail: '是否开启hover效果', default: false }, shadow: { detail: '选中效果的阴影设置', propertys: { shadowOffse