UNPKG

@antv/g6

Version:

graph visualization frame work

380 lines (334 loc) 12 kB
/** * @fileOverview 自定义边 * @description 自定义边中有大量逻辑同自定义节点重复,虽然可以提取成为 mixin ,但是考虑到代码的可读性,还是单独实现。 * @author dxq613@gmail.com */ var Shape = require('./shape'); var Util = require('../util'); var Global = require('../global'); var SingleShapeMixin = require('./single-shape-mixin'); var CLS_SHAPE = 'edge-shape'; // start,end 倒置,center 不变 function revertAlign(labelPosition) { var textAlign = labelPosition; if (labelPosition === 'start') { textAlign = 'end'; } else if (labelPosition === 'end') { textAlign = 'start'; } return textAlign; } // 注册 Edge 的工厂方法 Shape.registerFactory('edge', { defaultShapeType: 'line' }); var singleEdgeDefinition = Util.mix({}, SingleShapeMixin, { itemType: 'edge', /** * 文本的位置 * @type {String} */ labelPosition: 'center', // start, end, center /** * 文本是否跟着线自动旋转,默认 false * @type {Boolean} */ labelAutoRotate: false, /** * 获取边的 path * @internal 供扩展的边覆盖 * @param {Array} points 构成边的点的集合 * @return {Array} 构成 path 的数组 */ getPath: function getPath(points) { var path = []; Util.each(points, function (point, index) { if (index === 0) { path.push(['M', point.x, point.y]); } else { path.push(['L', point.x, point.y]); } }); return path; }, getShapeStyle: function getShapeStyle(cfg) { var customOptions = this.getCustomConfig(cfg) || {}; var defaultStyle = this.options.style; var customStyle = customOptions.style; var strokeStyle = { stroke: cfg.color }; // 如果设置了color,则覆盖默认的stroke属性 var style = Util.deepMix({}, defaultStyle, customStyle, strokeStyle, cfg.style); var size = cfg.size || Global.defaultEdge.size; cfg = this.getPathPoints(cfg); var startPoint = cfg.startPoint; var endPoint = cfg.endPoint; var controlPoints = this.getControlPoints(cfg); var points = [startPoint]; // 添加起始点 // 添加控制点 if (controlPoints) { points = points.concat(controlPoints); } // 添加结束点 points.push(endPoint); var path = this.getPath(points); var styles = Util.mix({}, Global.defaultEdge.style, { stroke: Global.defaultEdge.color, lineWidth: size, path: path }, style); return styles; }, getLabelStyleByPosition: function getLabelStyleByPosition(cfg, labelCfg, group) { var labelPosition = labelCfg.position || this.labelPosition; // 文本的位置用户可以传入 var style = {}; var pathShape = group.findByClassName(CLS_SHAPE); // 不对 pathShape 进行判空,如果线不存在,说明有问题了 var pointPercent; if (labelPosition === 'start') { pointPercent = 0; } else if (labelPosition === 'end') { pointPercent = 1; } else { pointPercent = 0.5; } var refX = labelCfg.refX, refY = labelCfg.refY; // 默认的偏移量 // 如果两个节点重叠,线就变成了一个点,这时候label的位置,就是这个点 + 绝对偏移 if (cfg.startPoint.x === cfg.endPoint.x && cfg.startPoint.y === cfg.endPoint.y) { style.x = cfg.startPoint.x + refX ? refX : 0; style.y = cfg.endPoint.y + refY ? refY : 0; return style; } var autoRotate = Util.isNil(labelCfg.autoRotate) ? this.labelAutoRotate : labelCfg.autoRotate; var offsetStyle = Util.getLabelPosition(pathShape, pointPercent, refX, refY, autoRotate); style.x = offsetStyle.x; style.y = offsetStyle.y; style.rotate = offsetStyle.rotate; style.textAlign = this._getTextAlign(labelPosition, offsetStyle.angle); return style; }, // 获取文本对齐方式 _getTextAlign: function _getTextAlign(labelPosition, angle) { var textAlign = 'center'; if (!angle) { return labelPosition; } angle = angle % (Math.PI * 2); // 取模 if (labelPosition !== 'center') { if (angle >= 0 && angle <= Math.PI / 2 || angle >= 3 / 2 * Math.PI && angle < 2 * Math.PI) { textAlign = labelPosition; } else { textAlign = revertAlign(labelPosition); } } return textAlign; }, /** * @internal 获取边的控制点 * @param {Object} cfg 边的配置项 * @return {Array} 控制点的数组 */ getControlPoints: function getControlPoints(cfg) { return cfg.controlPoints; }, /** * @internal 处理需要重计算点和边的情况 * @param {Object} cfg 边的配置项 * @return {Object} 边的配置项 */ getPathPoints: function getPathPoints(cfg) { return cfg; }, /** * 绘制边 * @override * @param {Object} cfg 边的配置项 * @param {G.Group} group 边的容器 * @return {G.Shape} 图形 */ drawShape: function drawShape(cfg, group) { var shapeStyle = this.getShapeStyle(cfg); var shape = group.addShape('path', { className: CLS_SHAPE, attrs: shapeStyle }); return shape; }, drawLabel: function drawLabel(cfg, group) { var customStyle = this.getCustomConfig(cfg) || {}; var defaultConfig = customStyle.default || {}; var labelCfg = Util.deepMix({}, this.options.labelCfg, defaultConfig.labelCfg, cfg.labelCfg); var labelStyle = this.getLabelStyle(cfg, labelCfg, group); var label = group.addShape('text', { attrs: labelStyle }); return label; } }); // // 直线 Shape.registerEdge('single-line', singleEdgeDefinition); // // 直线, 不支持控制点 Shape.registerEdge('line', { // 控制点不生效 getControlPoints: function getControlPoints() { return []; } }, 'single-line'); // // 折线,支持多个控制点 // Shape.registerEdge('polyline', {}, 'single-line'); // 直线 Shape.registerEdge('spline', { getPath: function getPath(points) { var path = Util.getSpline(points); return path; } }, 'single-line'); Shape.registerEdge('arc', { curveOffset: 20, clockwise: 1, getControlPoints: function getControlPoints(cfg) { var startPoint = cfg.startPoint; var endPoint = cfg.endPoint; var midPoint = { x: (startPoint.x + endPoint.x) / 2, y: (startPoint.y + endPoint.y) / 2 }; var center; var arcPoint; // 根据给定点计算圆弧 if (cfg.controlPoints !== undefined) { arcPoint = cfg.controlPoints[0]; center = Util.getCircleCenterByPoints(startPoint, arcPoint, endPoint); // 根据控制点和直线关系决定 clockwise值 if (startPoint.x <= endPoint.x && startPoint.y > endPoint.y) { this.clockwise = center.x > midPoint.x ? 1 : 0; } else if (startPoint.x <= endPoint.x && startPoint.y < endPoint.y) { this.clockwise = center.x > midPoint.x ? 0 : 1; } else if (startPoint.x > endPoint.x && startPoint.y <= endPoint.y) { this.clockwise = center.y < midPoint.y ? 1 : 0; } else { this.clockwise = center.y < midPoint.y ? 1 : 0; } // 若给定点和两端点共线,无法生成圆弧,绘制直线 if ((arcPoint.x - startPoint.x) / (arcPoint.y - startPoint.y) === (endPoint.x - startPoint.x) / (endPoint.y - startPoint.y)) { return []; } } else { // 根据直线连线中点的的偏移计算圆弧 // 若用户给定偏移量则根据其计算,否则按照默认偏移值计算 if (cfg.curveOffset !== undefined) { this.curveOffset = cfg.curveOffset; } if (this.curveOffset < 0) this.clockwise = 0;else this.clockwise = 1; var vec = { x: endPoint.x - startPoint.x, y: endPoint.y - startPoint.y }; var edgeAngle = Math.atan2(vec.y, vec.x); arcPoint = { x: this.curveOffset * Math.cos(-Math.PI / 2 + edgeAngle) + midPoint.x, y: this.curveOffset * Math.sin(-Math.PI / 2 + edgeAngle) + midPoint.y }; center = Util.getCircleCenterByPoints(startPoint, arcPoint, endPoint); } var radius = Util.distance(startPoint, center); var controlPoints = [{ x: radius, y: radius }]; return controlPoints; }, getPath: function getPath(points) { var path = []; path.push(['M', points[0].x, points[0].y]); // 控制点与端点共线 if (points.length === 2) { path.push(['L', points[1].x, points[1].y]); } else { path.push(['A', points[1].x, points[1].y, 0, 0, this.clockwise, points[2].x, points[2].y]); } return path; } }, 'single-line'); Shape.registerEdge('quadratic', { curvePosition: 0.5, // 弯曲的默认位置 curveOffset: -20, // 弯曲度,沿着startPoint, endPoint 的垂直向量(顺时针)方向,距离线的距离,距离越大越弯曲 getControlPoints: function getControlPoints(cfg) { var controlPoints = cfg.controlPoints; // 指定controlPoints if (!controlPoints || !controlPoints.length) { var startPoint = cfg.startPoint, endPoint = cfg.endPoint; var innerPoint = Util.getControlPoint(startPoint, endPoint, this.curvePosition, this.curveOffset); controlPoints = [innerPoint]; } return controlPoints; }, getPath: function getPath(points) { var path = []; path.push(['M', points[0].x, points[0].y]); path.push(['Q', points[1].x, points[1].y, points[2].x, points[2].y]); return path; } }, 'single-line'); Shape.registerEdge('cubic', { curvePosition: [1 / 2, 1 / 2], curveOffset: [-20, 20], getControlPoints: function getControlPoints(cfg) { var controlPoints = cfg.controlPoints; // 指定controlPoints if (!controlPoints || !controlPoints.length) { var startPoint = cfg.startPoint, endPoint = cfg.endPoint; var innerPoint1 = Util.getControlPoint(startPoint, endPoint, this.curvePosition[0], this.curveOffset[0]); var innerPoint2 = Util.getControlPoint(startPoint, endPoint, this.curvePosition[1], this.curveOffset[1]); controlPoints = [innerPoint1, innerPoint2]; } return controlPoints; }, getPath: function getPath(points) { var path = []; path.push(['M', points[0].x, points[0].y]); path.push(['C', points[1].x, points[1].y, points[2].x, points[2].y, points[3].x, points[3].y]); return path; } }, 'single-line'); // 垂直方向的三阶贝塞尔曲线,不再考虑用户外部传入的控制点 Shape.registerEdge('cubic-vertical', { curvePosition: [1 / 2, 1 / 2], getControlPoints: function getControlPoints(cfg) { var startPoint = cfg.startPoint, endPoint = cfg.endPoint; var innerPoint1 = { x: startPoint.x, y: (endPoint.y - startPoint.y) * this.curvePosition[0] + startPoint.y }; var innerPoint2 = { x: endPoint.x, y: (endPoint.y - startPoint.y) * this.curvePosition[1] + startPoint.y }; var controlPoints = [innerPoint1, innerPoint2]; return controlPoints; } }, 'cubic'); // 水平方向的三阶贝塞尔曲线,不再考虑用户外部传入的控制点 Shape.registerEdge('cubic-horizontal', { curvePosition: [1 / 2, 1 / 2], getControlPoints: function getControlPoints(cfg) { var startPoint = cfg.startPoint, endPoint = cfg.endPoint; var innerPoint1 = { x: (endPoint.x - startPoint.x) * this.curvePosition[0] + startPoint.x, y: startPoint.y }; var innerPoint2 = { x: (endPoint.x - startPoint.x) * this.curvePosition[1] + startPoint.x, y: endPoint.y }; var controlPoints = [innerPoint1, innerPoint2]; return controlPoints; } }, 'cubic'); Shape.registerEdge('loop', { getPathPoints: function getPathPoints(cfg) { return Util.getLoopCfgs(cfg); }, getControlPoints: function getControlPoints(cfg) { return cfg.controlPoints; }, afterDraw: function afterDraw(cfg) { cfg.controlPoints = null; }, afterUpdate: function afterUpdate(cfg) { cfg.controlPoints = null; } }, 'cubic');