UNPKG

d3-path-arrows

Version:

Add arrows to your paths using CSS dash-array and SVG triangle shapes

110 lines (89 loc) 3.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = pathArrows; var _d3Selection = require("d3-selection"); var _d3Array = require("d3-array"); // Function that appends a path to selection that has sankey path data attached // The path is formatted as dash array, and triangle paths to create arrows along the path function pathArrows() { var arrowLength = 10; var gapLength = 50; var arrowHeadSize = 4; var path = null; function appendArrows(selection) { let totalDashArrayLength = arrowLength + gapLength; let arrows = selection.append('path').attr('d', path).style('stroke-width', 1).style('stroke', 'black').style('stroke-dasharray', arrowLength + ',' + gapLength); arrows.each(function (arrow) { let thisPath = (0, _d3Selection.select)(this).node(); let parentG = (0, _d3Selection.select)(this.parentNode); let pathLength = thisPath.getTotalLength(); let numberOfArrows = Math.ceil(pathLength / totalDashArrayLength); // remove the last arrow head if it will overlap the target node if ((numberOfArrows - 1) * totalDashArrayLength + (arrowLength + (arrowHeadSize + 1)) > pathLength) { numberOfArrows = numberOfArrows - 1; } let arrowHeadData = (0, _d3Array.range)(numberOfArrows).map(function (d, i) { let length = i * totalDashArrayLength + arrowLength; let point = thisPath.getPointAtLength(length); let previousPoint = thisPath.getPointAtLength(length - 2); let rotation = 0; if (point.y == previousPoint.y) { rotation = point.x < previousPoint.x ? 180 : 0; } else if (point.x == previousPoint.x) { rotation = point.y < previousPoint.y ? -90 : 90; } else { let adj = Math.abs(point.x - previousPoint.x); let opp = Math.abs(point.y - previousPoint.y); let angle = Math.atan(opp / adj) * (180 / Math.PI); if (point.x < previousPoint.x) { angle = angle + (90 - angle) * 2; } if (point.y < previousPoint.y) { rotation = -angle; } else { rotation = angle; } } return { x: point.x, y: point.y, rotation: rotation }; }); let arrowHeads = parentG.selectAll('.arrow-heads').data(arrowHeadData).enter().append('path').attr('d', function (d) { return 'M' + d.x + ',' + (d.y - arrowHeadSize / 2) + ' ' + 'L' + (d.x + arrowHeadSize) + ',' + d.y + ' ' + 'L' + d.x + ',' + (d.y + arrowHeadSize / 2); }).attr('class', 'arrow-head').attr('transform', function (d) { return 'rotate(' + d.rotation + ',' + d.x + ',' + d.y + ')'; }).style('fill', 'black'); }); } appendArrows.arrowLength = function (value) { if (!arguments.length) return arrowLength; arrowLength = value; return appendArrows; }; appendArrows.gapLength = function (value) { if (!arguments.length) return gapLength; gapLength = value; return appendArrows; }; appendArrows.arrowHeadSize = function (value) { if (!arguments.length) return arrowHeadSize; arrowHeadSize = value; return appendArrows; }; appendArrows.path = function (pathFunction) { if (!arguments.length) { return path; } else { if (typeof pathFunction === "function") { path = pathFunction; return appendArrows; } else { path = function () { return pathFunction; }; return appendArrows; } } }; return appendArrows; }