Appearance
Text on edge extremities
This example shows how to display texts at the edges' extremities. It uses the canvas layer API to do so.
ts
import Ogma, { Point, Edge } from '@linkurious/ogma';
const ogma = new Ogma({
container: 'graph-container'
});
ogma.styles.addEdgeRule({ shape: { head: 'arrow' } });
function distance(vec1: Point, vec2: Point) {
const dx = vec2.x - vec1.x;
const dy = vec2.y - vec1.y;
return Math.sqrt(dx * dx + dy * dy);
}
const SOURCE_SIDE = 0;
const TARGET_SIDE = 1;
function displayOnEdge({
edge,
ctx,
drawFunction,
rotateText = true,
// translation from the center of the edgeExtremity
distToExtremity = 12,
// margin between the edge and the text
margin = 2,
side = SOURCE_SIDE
}: {
edge: Edge;
ctx: CanvasRenderingContext2D;
drawFunction: (args: { ctx: CanvasRenderingContext2D; edge: Edge }) => void;
rotateText?: boolean;
distToExtremity?: number;
margin?: number;
side?: typeof SOURCE_SIDE | typeof TARGET_SIDE;
}) {
// compute the interpolation ratio that represents distToExtremity on the edge.
const [source, target] = edge
.getExtremities()
.map(node => node.getPosition());
const dist = distance(source, target);
let t = distToExtremity / dist;
// if we are on TARGET_SIDE, reverse the interpolation ratio
if (side === TARGET_SIDE) {
t = 1 - t;
}
// get the point, tangent and normal vectors where we want to display the text
const point = Ogma.geometry.getPointOnEdge(edge, t);
const tangent = Ogma.geometry.getTangentOnEdge(edge, t);
// compute the rotation angle for the text
let angle = Math.atan2(tangent.y, tangent.x);
if (angle <= -Math.PI / 2 || angle >= Math.PI / 2) {
angle += Math.PI;
}
ctx.save();
ctx.beginPath();
// translate the context to the text center
ctx.translate(point.x, point.y);
// rotate it so it's parrallel to the edge
if (rotateText) {
ctx.rotate(angle);
}
// translate it so there is margin between the edge and the text
ctx.translate(0, margin);
// draw the text
drawFunction({ ctx, edge });
ctx.stroke();
ctx.restore();
}
function draw(ctx: CanvasRenderingContext2D) {
ctx.font = '2px sans-serif';
ogma.getEdges().forEach(edge => {
// display on edge will give you a ctx rotated and translated
// to the right place
displayOnEdge({
edge,
ctx,
// draw a text at the begining of the edge
drawFunction: ({ edge, ctx }) => {
const text = edge.getData('sourceText');
const w2 = ctx.measureText(text).width / 2;
ctx.fillText(text, -w2, 0);
}
});
displayOnEdge({
edge,
ctx,
side: TARGET_SIDE,
// draw a text at the end of the edge
drawFunction: ({ edge, ctx }) => {
const text = edge.getData('targetText');
const w2 = ctx.measureText(text).width / 2;
ctx.fillText(text, -w2, 0);
}
});
});
}
const graph = {
nodes: [{ id: 0 }, { id: 1 }],
edges: [
{
source: 0,
target: 1,
data: { sourceText: 'Source 0', targetText: 'Target 0' }
},
{
source: 1,
target: 0,
data: { sourceText: 'Source 1', targetText: 'Target 1' }
},
{
source: 1,
target: 0,
data: { sourceText: 'Source 2', targetText: 'Target 2' }
},
{
source: 1,
target: 0,
data: { sourceText: 'Source 3', targetText: 'Target 3' }
},
{
source: 1,
target: 0,
data: { sourceText: 'Source 4', targetText: 'Target 4' }
}
]
};
// setup the graph
await ogma.setGraph(graph);
await ogma.layouts.force({ locate: true });
// create the layer
const edgeTextLayer = ogma.layers.addCanvasLayer(draw);
// hide and show on draging
ogma.events
.on('nodesDragStart', () => {
edgeTextLayer.hide();
})
.on('nodesDragEnd', () => {
edgeTextLayer.show();
});
html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<link type="text/css" rel="stylesheet" href="styles.css" />
</head>
<body>
<div id="graph-container"></div>
<script type="module" src="index.ts"></script>
</body>
</html>
css
#graph-container {
top: 0;
bottom: 0;
left: 0;
right: 0;
position: absolute;
margin: 0;
overflow: hidden;
}
.brand {
background-color: rgba(255, 255, 255, 0.7);
font-size: 10px;
padding: 5px;
margin: 0;
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
}