150 lines
4.0 KiB
Java
150 lines
4.0 KiB
Java
![]() |
var Format = require('../../util/format');
|
|||
|
|
|||
|
var PathSegment = require('../util/path-segment');
|
|||
|
|
|||
|
var PI = Math.PI;
|
|||
|
var sin = Math.sin;
|
|||
|
var cos = Math.cos;
|
|||
|
var atan2 = Math.atan2;
|
|||
|
var DEFAULT_LENGTH = 10;
|
|||
|
var DEFAULT_ANGLE = PI / 3;
|
|||
|
|
|||
|
function _addArrow(ctx, attrs, x1, y1, x2, y2, isStart) {
|
|||
|
var leftX;
|
|||
|
var leftY;
|
|||
|
var rightX;
|
|||
|
var rightY;
|
|||
|
var offsetX;
|
|||
|
var offsetY;
|
|||
|
var angle;
|
|||
|
|
|||
|
if (!attrs.fill) {
|
|||
|
// 闭合的不绘制箭头
|
|||
|
var arrowLength = attrs.arrowLength || DEFAULT_LENGTH;
|
|||
|
var arrowAngle = attrs.arrowAngle ? attrs.arrowAngle * PI / 180 : DEFAULT_ANGLE; // 转换为弧
|
|||
|
// Calculate angle
|
|||
|
|
|||
|
angle = atan2(y1 - y2, x1 - x2);
|
|||
|
/* // Adjust angle correctly
|
|||
|
angle -= PI;*/
|
|||
|
// Calculate offset to place arrow at edge of path
|
|||
|
|
|||
|
offsetX = Math.abs(attrs.lineWidth * cos(angle)) / 2;
|
|||
|
offsetY = Math.abs(attrs.lineWidth * sin(angle)) / 2;
|
|||
|
|
|||
|
if (isStart) {
|
|||
|
offsetX = -offsetX;
|
|||
|
offsetY = -offsetY;
|
|||
|
} // Calculate coordinates for left half of arrow
|
|||
|
|
|||
|
|
|||
|
leftX = x2 + arrowLength * cos(angle + arrowAngle / 2);
|
|||
|
leftY = y2 + arrowLength * sin(angle + arrowAngle / 2); // Calculate coordinates for right half of arrow
|
|||
|
|
|||
|
rightX = x2 + arrowLength * cos(angle - arrowAngle / 2);
|
|||
|
rightY = y2 + arrowLength * sin(angle - arrowAngle / 2);
|
|||
|
ctx.beginPath(); // Draw left half of arrow
|
|||
|
|
|||
|
ctx.moveTo(leftX - offsetX, leftY - offsetY);
|
|||
|
ctx.lineTo(x2 - offsetX, y2 - offsetY); // Draw right half of arrow
|
|||
|
|
|||
|
ctx.lineTo(rightX - offsetX, rightY - offsetY); // Visually connect arrow to path
|
|||
|
|
|||
|
ctx.moveTo(x2 - offsetX, y2 - offsetY);
|
|||
|
ctx.lineTo(x2 + offsetX, y2 + offsetY); // Move back to end of path
|
|||
|
|
|||
|
ctx.moveTo(x2, y2);
|
|||
|
ctx.stroke();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function parsePath(attrs) {
|
|||
|
var segments = [];
|
|||
|
var pathArray = Format.parsePath(attrs.path);
|
|||
|
var preSegment;
|
|||
|
|
|||
|
if (!Array.isArray(pathArray) || pathArray.length === 0 || pathArray[0][0] !== 'M' && pathArray[0][0] !== 'm') {
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
var count = pathArray.length;
|
|||
|
|
|||
|
for (var i = 0; i < pathArray.length; i++) {
|
|||
|
var item = pathArray[i];
|
|||
|
preSegment = new PathSegment(item, preSegment, i === count - 1);
|
|||
|
segments.push(preSegment);
|
|||
|
}
|
|||
|
|
|||
|
return segments;
|
|||
|
}
|
|||
|
/**
|
|||
|
* 如果自定义箭头并且有 d 需要做偏移,如果直接画,线条会超出箭头尖端,因此需要根据箭头偏移 d, 返回线需要缩短的距离
|
|||
|
* |----------------
|
|||
|
* |<|--------------
|
|||
|
* |
|
|||
|
* @param {Number} x1 起始点 x
|
|||
|
* @param {number} y1 起始点 y
|
|||
|
* @param {number} x2 箭头作用点 x
|
|||
|
* @param {number} y2 箭头作用点 y
|
|||
|
* @param {number} d 箭头沿线条方向的偏移距离
|
|||
|
* @return {{dx: number, dy: number}} 返回线条偏移距离
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
function getShortenOffset(x1, y1, x2, y2, d) {
|
|||
|
var rad = Math.atan2(y2 - y1, x2 - x1);
|
|||
|
return {
|
|||
|
dx: cos(rad) * d,
|
|||
|
dy: sin(rad) * d
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
function _addCustomizedArrow(ctx, attrs, x1, y1, x2, y2, isStart) {
|
|||
|
var shape = isStart ? attrs.startArrow : attrs.endArrow;
|
|||
|
var d = shape.d;
|
|||
|
var x = x2 - x1;
|
|||
|
var y = y2 - y1;
|
|||
|
var rad = Math.atan2(y, x);
|
|||
|
var path = parsePath(shape);
|
|||
|
|
|||
|
if (!path) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (d) {
|
|||
|
x2 = x2 - cos(rad) * d;
|
|||
|
y2 = y2 - sin(rad) * d;
|
|||
|
}
|
|||
|
|
|||
|
ctx.save();
|
|||
|
ctx.beginPath();
|
|||
|
ctx.translate(x2, y2);
|
|||
|
ctx.rotate(rad);
|
|||
|
|
|||
|
for (var i = 0; i < path.length; i++) {
|
|||
|
path[i].draw(ctx);
|
|||
|
}
|
|||
|
|
|||
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|||
|
ctx.fillStyle = ctx.strokeStyle;
|
|||
|
ctx.fill();
|
|||
|
ctx.restore();
|
|||
|
}
|
|||
|
|
|||
|
module.exports = {
|
|||
|
addStartArrow: function addStartArrow(ctx, attrs, x1, y1, x2, y2) {
|
|||
|
if (typeof attrs.startArrow === 'object') {
|
|||
|
_addCustomizedArrow(ctx, attrs, x1, y1, x2, y2, true);
|
|||
|
} else if (attrs.startArrow) {
|
|||
|
_addArrow(ctx, attrs, x1, y1, x2, y2, true);
|
|||
|
}
|
|||
|
},
|
|||
|
addEndArrow: function addEndArrow(ctx, attrs, x1, y1, x2, y2) {
|
|||
|
if (typeof attrs.endArrow === 'object') {
|
|||
|
_addCustomizedArrow(ctx, attrs, x1, y1, x2, y2, false);
|
|||
|
} else if (attrs.endArrow) {
|
|||
|
_addArrow(ctx, attrs, x1, y1, x2, y2, false);
|
|||
|
}
|
|||
|
},
|
|||
|
getShortenOffset: getShortenOffset
|
|||
|
};
|