Skip to content

Instantly share code, notes, and snippets.

@dhilip89
Created February 19, 2017 05:09
Show Gist options
  • Save dhilip89/5f63d0e033dfa7c4865bca3afa8f42bd to your computer and use it in GitHub Desktop.
Save dhilip89/5f63d0e033dfa7c4865bca3afa8f42bd to your computer and use it in GitHub Desktop.
Dynamic SVG arrow line, no dependencies.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Dynamic Arrow Line - By DK</title>
<style>
span:hover,
td:hover,
th:hover {
background-color: rgba(255, 0, 0, 0.3);
cursor: crosshair;
}
</style>
</head>
<body>
<h1>Dynamic Arrow Line - By DK</h1>
<table border="1">
<thead>
<tr>
<th id="pointA">Column 1</th>
<th>Column 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Row 1,1</td>
<td>Row 1,2</td>
</tr>
<tr>
<td>Row 2,1</td>
<td>Row 2,2</td>
</tr>
<tr>
<td id="pointB">Row 3,1</td>
<td>Row 3,2</td>
</tr>
<tr>
<td>Row 4,1</td>
<td id="pointC">Row 4,2</td>
</tr>
</tbody>
</table>
<br>
<table border="1">
<thead>
<tr>
<th>Column 1</th>
<th id="pointE">Column 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Row 1,1</td>
<td id="pointF">Row 1,2</td>
</tr>
<tr>
<td>Row 2,1</td>
<td id="pointI">Row 2,2</td>
</tr>
<tr>
<td id="pointG">Row 3,1</td>
<td>Row 3,2</td>
</tr>
<tr>
<td>Row 4,1</td>
<td id="pointH">Row 4,2</td>
</tr>
</tbody>
</table>
<hr>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras quis sapien non ipsum porttitor euismod. Nam vitae nisi nulla. Cras pharetra vel mi sed mattis. Phasellus laoreet quam ac varius scelerisque. Sed placerat, dui nec maximus euismod, sapien tellus laoreet massa, ac varius est diam sit amet sem. Vestibulum tincidunt leo nec efficitur rutrum. Cras at leo vitae sem hendrerit placerat. Aliquam erat volutpat. Morbi pharetra libero vel nibh ullamcorper, in sollicitudin lectus pulvinar. Cras dictum ullamcorper mauris in vestibulum. Fusce rutrum nulla consequat enim sodales malesuada. Vestibulum nec lectus nec odio mollis fermentum. Phasellus vitae nisi vel quam ultricies pellentesque.</p>
<span id="pointD">Hello World!</span>
<div id="pointJ" style="position: absolute; left: 600px; top: 200px; border: 2px dotted blue; background-color: azure; width: 100px; height: 100px;"></div>
<div id="pointK" style="position: absolute; left: 450px; top: 100px; border: 2px dashed green; background-color: honeydew; width: 100px; height: 100px;"></div>
<script>
(function (exports) {
"use strict";
// 老板对我说:不管黑猫白猫,能捉到老鼠就是好猫
// 这次甭jQuery, 科科
// My LinkedIn: https://www.linkedin.com/in/dhilip89/
var isDefined = function (variable) {
return (typeof(variable) !== "undefined");
};
// Reference: https://jsfiddle.net/briguy37/2MVFd/
var generateUUID = function () {
var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16);
});
return uuid;
};
exports.drawArrowLine = function (fromElementId, toElementId, options) {
var defaultOptions = {
lineColor: "#000000",
lineWidth: 2,
lineWithDash: false,
lineDashArray: [5, 5],
arrowSize: 5,
arrowColor: "#000000"
};
var finalizedOptions = { };
if (isDefined(options)) {
if (isDefined(options.lineColor)) {
finalizedOptions.lineColor = options.lineColor;
} else {
finalizedOptions.lineColor = defaultOptions.lineColor;
}
if (isDefined(options.lineWidth)) {
finalizedOptions.lineWidth = options.lineWidth;
} else {
finalizedOptions.lineWidth = defaultOptions.lineWidth;
}
if (isDefined(options.lineWithDash)) {
finalizedOptions.lineWithDash = options.lineWithDash;
} else {
finalizedOptions.lineWithDash = defaultOptions.lineWithDash;
}
if (isDefined(options.lineDashArray)) {
finalizedOptions.lineDashArray = options.lineDashArray;
} else {
finalizedOptions.lineDashArray = defaultOptions.lineDashArray;
}
if (isDefined(options.arrowSize)) {
finalizedOptions.arrowSize = options.arrowSize;
} else {
finalizedOptions.arrowSize = defaultOptions.arrowSize;
}
if (isDefined(options.arrowColor)) {
finalizedOptions.arrowColor = options.arrowColor;
} else {
finalizedOptions.arrowColor = defaultOptions.arrowColor;
}
} else {
finalizedOptions = defaultOptions;
}
var fromElement = document.getElementById(fromElementId);
var toElement = document.getElementById(toElementId);
var uuid = generateUUID();
var svgId = "dk__" + uuid;
var svgArrowMarkerId = svgId + "_marker_arrow";
var render = function () {
var svgNamespace = "http://www.w3.org/2000/svg";
var fromElementBoundingClientRect = fromElement.getBoundingClientRect();
var toElementBoundingClientRect = toElement.getBoundingClientRect();
var fromElementCenterPoint = {
x: fromElementBoundingClientRect.left + window.scrollX + (fromElementBoundingClientRect.width / 2),
y: fromElementBoundingClientRect.top + window.scrollY + (fromElementBoundingClientRect.height / 2)
};
var toElementCenterPoint = {
x: toElementBoundingClientRect.left + window.scrollX + (toElementBoundingClientRect.width / 2),
y: toElementBoundingClientRect.top + window.scrollY + (toElementBoundingClientRect.height / 2)
};
var linePosition = {
x1: 0,
y1: 0,
x2: 0,
y2: 0
};
var svgDimension = {
width: 0,
height: 0
};
var extraPadding = (finalizedOptions.lineWidth + (finalizedOptions.arrowSize * 1.5)) * 2;
var halfExtraPadding = extraPadding / 2;
if ((fromElementCenterPoint.x - toElementCenterPoint.x) >= 0) {
svgDimension.width = (fromElementCenterPoint.x - toElementCenterPoint.x) + extraPadding;
linePosition.x1 = (fromElementCenterPoint.x - toElementCenterPoint.x) + halfExtraPadding;
linePosition.x2 = halfExtraPadding;
} else {
svgDimension.width = (toElementCenterPoint.x - fromElementCenterPoint.x) + extraPadding;
linePosition.x1 = halfExtraPadding;
linePosition.x2 = (toElementCenterPoint.x - fromElementCenterPoint.x) + halfExtraPadding;
}
if ((fromElementCenterPoint.y - toElementCenterPoint.y) >= 0) {
svgDimension.height = (fromElementCenterPoint.y - toElementCenterPoint.y) + extraPadding;
linePosition.y1 = (fromElementCenterPoint.y - toElementCenterPoint.y) + halfExtraPadding;
linePosition.y2 = halfExtraPadding;
} else {
svgDimension.height = (toElementCenterPoint.y - fromElementCenterPoint.y) + extraPadding;
linePosition.y1 = halfExtraPadding;
linePosition.y2 = (toElementCenterPoint.y - fromElementCenterPoint.y) + halfExtraPadding;
}
var svgPosition = {
x: 0,
y: 0
};
if (fromElementCenterPoint.y <= toElementCenterPoint.y) {
svgPosition.y = fromElementCenterPoint.y - halfExtraPadding;
} else {
svgPosition.y = toElementCenterPoint.y - halfExtraPadding;
}
if (fromElementCenterPoint.x <= toElementCenterPoint.x) {
svgPosition.x = fromElementCenterPoint.x - halfExtraPadding;
} else {
svgPosition.x = toElementCenterPoint.x - halfExtraPadding;
}
var svg = document.createElementNS(svgNamespace, "svg");
svg.setAttributeNS(null, "id", svgId);
svg.setAttributeNS(null, "width", svgDimension.width);
svg.setAttributeNS(null, "height", svgDimension.height);
svg.setAttributeNS(null, "shape-rendering", "geometricPrecision");
svg.style.position = "absolute";
svg.style.display = "block";
svg.style.pointerEvents = "none";
svg.style.top = svgPosition.y + "px";
svg.style.left = svgPosition.x + "px";
var svgDefs = document.createElementNS(svgNamespace, "defs");
var svgArrowMarker = document.createElementNS(svgNamespace, "marker");
svgArrowMarker.setAttributeNS(null, "id", svgArrowMarkerId);
svgArrowMarker.setAttributeNS(null, "viewBox", "0 -5 10 10");
svgArrowMarker.setAttributeNS(null, "refX", 5);
svgArrowMarker.setAttributeNS(null, "refY", 0);
svgArrowMarker.setAttributeNS(null, "markerWidth", finalizedOptions.arrowSize);
svgArrowMarker.setAttributeNS(null, "markerHeight", finalizedOptions.arrowSize);
svgArrowMarker.setAttributeNS(null, "orient", "auto");
svgArrowMarker.setAttributeNS(null, "stroke-width", 1);
svgArrowMarker.setAttributeNS(null, "stroke-dasharray", 0);
svgArrowMarker.setAttributeNS(null, "fill", finalizedOptions.arrowColor);
var svgArrowMarkerPath = document.createElementNS(svgNamespace, "path");
svgArrowMarkerPath.setAttributeNS(null, "d", "M0,-5L10,0L0,5");
var svgLine = document.createElementNS(svgNamespace, "line");
svgLine.setAttributeNS(null, "marker-end", "url(#" + svgArrowMarkerId + ")");
svgLine.setAttributeNS(null, "x1", linePosition.x1);
svgLine.setAttributeNS(null, "y1", linePosition.y1);
svgLine.setAttributeNS(null, "x2", linePosition.x2);
svgLine.setAttributeNS(null, "y2", linePosition.y2);
svgLine.setAttributeNS(null, "stroke", finalizedOptions.lineColor);
svgLine.setAttributeNS(null, "stroke-width", finalizedOptions.lineWidth);
if (finalizedOptions.lineWithDash) {
svgLine.style.strokeDasharray = finalizedOptions.lineDashArray.join(", ");
}
svgArrowMarker.appendChild(svgArrowMarkerPath);
svgDefs.appendChild(svgArrowMarker);
svg.appendChild(svgDefs);
svg.appendChild(svgLine);
document.body.appendChild(svg);
};
var destroy = function () {
var svg = document.getElementById(svgId);
if (svg !== null) {
svg.remove();
}
};
render();
return {
fromElementId: fromElementId,
toElementId: toElementId,
options: finalizedOptions,
arrowLineId: svgId,
render: function () {
destroy();
render();
},
destroy: function () {
destroy();
}
}
};
})(window.DK = window.DK || {});
</script>
<script>
// 大概是这样用的吧...
DK.drawArrowLine("pointA", "pointB");
DK.drawArrowLine("pointC", "pointD", {
lineColor: "black",
arrowColor: "black",
lineWithDash: true,
lineDashArray: [5, 5, 1, 5]
});
DK.drawArrowLine("pointE", "pointF", {
lineColor: "red",
arrowColor: "lime",
lineWidth: 3,
arrowSize: 5
});
DK.drawArrowLine("pointG", "pointH", {
lineColor: "blue",
arrowColor: "orange",
lineWithDash: true
});
DK.drawArrowLine("pointI", "pointJ", {
lineColor: "red",
arrowColor: "red",
lineWithDash: true,
lineDashArray: [3, 5, 1, 5]
});
DK.drawArrowLine("pointA", "pointJ", {
lineColor: "purple",
arrowColor: "purple",
lineWithDash: true,
lineDashArray: [3, 5, 1, 5]
});
var gg = DK.drawArrowLine("pointA", "pointK", {
lineColor: "purple",
arrowColor: "purple",
lineWithDash: true,
lineDashArray: [3, 5, 1, 5]
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment