Нарисовать стрелки между DOM элементами
На прошлой неделе занимался задачей реализии стрелок для функциональности onboarding (coach marks).
Для себя определил следующие требования:
- Декларативность API. Хочется иметь возможность указать 2 DOM объекта для отрисовки стрелки
- Использование кривых Безье
- Возможность настраивать внешний вид
- Возможность коррекции точек начала и конца стрелки
- Чистый JS или Angular библиотека
Вероятно это должно быть SVG или Canvas т.к. обычный HTML элемент не позволит легко сделать кривые.
Интересный момент заключается в том, что хорошая библиотека под эти требования сходу не гуглится. Косвенно это подтверждает множество (больше 20) решений на github с 0-10 звёздами, где авторы пытались решить подобную задачу. Альтернативное находится много ответов как самостоятельно реализовать SVG стрелки (один, два).
Рассмотренные варианты
Почти сходу находится react-archer. Решение хорошее, но легко встроить в Angular не представляется возможным.
swoopy
<div data-arrow-target="#a">A</div>
<div id="a">B</div>
<div data-arrow-target="#c">C</div>
<div id="c">D</div>
<!-- dependency -->
<script
src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"
charset="utf-8"
></script>
<!-- library -->
<script src="/src/libs/swoopy/swoopyArrow.js"></script>
<script src="/src/libs/swoopy/arrowConnector.js"></script>
<style type="text/css">
path.arrow-connector,
#arrowhead {
fill: none;
stroke: black;
}
</style>
<script>
// configure arrows
const connect = arrowConnector()
.type("swoopy") // options: 'straight', 'swoopy', 'kooky', 'loopy', 'random'
.edges(true) // allow arrows to attach to box edge midpoints (it'll pick closest pair)
.corners(false); // allow arrows to attach to box corners (it'll pick closest pair)
// draw arrows
connect();
</script>
Имеет зависимость от библиотеки d3. Внешний вид настраивается через d3. Непосредственно соединительные стрелки между DOM элементами не имеют настроек или плохо документированы.
domarrow
domarrow работает на веб компоненте внутри которого div.
Атрибуты компонента connection
- head, tail - концы стрелок
- fromX, fromY, toX, toY - значение от 0 до 1 положение стрелки на контейнере
<div id="a">A</div>
<div id="b">B</div>
<div id="c">C</div>
<link rel="stylesheet" href="/src/libs/domarrow/domarrow.css" />
<connection
from="#a"
to="#b"
color="red"
tail
fromX="0.8"
fromY="1.2"
toX="-0.1"
toY="0.25"
></connection>
<connection
from="#a"
to="#c"
color="green"
fromX="1"
tail
onlyVisible
></connection>
Работать на порядок проще, чем с swoopy. Не получится сделать кривые безье без переписываения на svg.
arrows-svg
arrows-svg достаточно проработанное решение на SVG, в проекте есть тесты. API удовлетворяет требованиям.
<div id="a">A</div>
<div id="b">B</div>
<script src="/src/libs/arrows-svg/arrows.js"></script>
<script>
const arrow = arrowCreate({
className: "arrow",
from: {
direction: "bottom",
node: document.getElementById("a"),
translation: [0, 1]
},
to: {
direction: "top",
node: document.getElementById("b"),
translation: [0.1, -0.5]
}
});
document.body.appendChild(arrow.node);
</script>
<style>
.arrow {
pointer-events: none;
}
.arrow__path {
stroke: white;
fill: transparent;
width: 4px;
stroke-width: 3px;
}
.arrow__head line {
stroke: white;
stroke-width: 3px;
}
</style>
Нет возможности корректировать начальное и конечное положение стрелок. Нет автоматических коэффициентов для кривых Безье, на каждую стрелку надо потратить время. Некорретно определяется указатель стрелки по отношению к элементу, при этом нет возможности явно указать его направление.
leader-line
leader-line - one love
<div id="a">A</div>
<div id="b">B</div>
<div id="c">C</div>
<div id="d">D</div>
<script src="/src/libs/leader-line/leader-line.min.js"></script>
<script>
new LeaderLine(
LeaderLine.pointAnchor(document.getElementById("a"), { x: "110%" }),
LeaderLine.pointAnchor(document.getElementById("b"), { x: 50, y: -20 }),
{
color: "white",
size: 3,
startSocket: "right",
endSocket: "top"
}
);
new LeaderLine(document.getElementById("c"), document.getElementById("d"), {
color: "white",
size: 3
});
</script>
Полностью удовлетворяет требованиям.
Результат
Для реализации выбран leader-line