I am currently working on a side project, where I want to "draw" diagrams. At the moment you just drag "sticky notes" to a grid, move, resize and label them. I now want to connect these notes with arrows. I got the calculations for length (pythagoras) and the angle of the line down.
calculateAngle(): number {
return Math.atan2(this._start.y - this._end.y, this._start.x - this._end.x) * 180 / Math.PI + 180;
}
calculateLength(): number {
return Math.round(Math.sqrt(Math.pow(this._start.y - this._end.y, 2) + Math.pow(this._start.x - this._end.x, 2)));
}
I also found a simple CSS solution for the arrows which work perfectly for horizontal and vertical arrows, but as soon the angle isn't dividable by 90deg the position is of course of.
This is based of a solution I found in SO and just added the transform. If you play around with the deg value, you can see how the base and head move around in the 2D space: https://jsfiddle.net/0r7k6L4c/
Does anyone know a good way to calculate the "new" top and left values if the rotation is taken into account? Or maybe I just offset the position with margins if this calulation is easier.
I used to be good at math in school but that was a few decades ago.
You could use the newly available CSS hypot
and atan2
functions. Define the two points A, B using CSS Properties ax, ay
and bx, by
.
Make your element position fixed or absolute, set the CSS transform-origin
point to be X left Y 50%, and rotate it by atan2
radians:
.arrow {
--t: 5px; /* tail size */
position: absolute;
transform-origin: left 50%;
left: calc(var(--ax) * 1px);
top: calc(var(--ay) * 1px);
height: var(--t);
width: calc(hypot(calc(var(--by) - var(--ay)), calc(var(--bx) - var(--ax))) * 1px);
rotate: atan2(calc(var(--by) - var(--ay)), calc(var(--bx) - var(--ax)));
background: #000;
}
<div class="arrow" style="--ax:10; --ay:10; --bx:50; --by:100;"></div>
and to create the arrow shape, either use the :before
pseudo or like the following where I borrowed Temani's cool solution using clip-path:
.arrow {
--t: 5px; /* tail size */
--h: 10px; /* head size */
position: absolute;
transform-origin: left 50%;
left: calc(var(--ax) * 1px);
top: calc(var(--ay) * 1px);
height: var(--h);
width: calc(hypot(calc(var(--by) - var(--ay)), calc(var(--bx) - var(--ax))) * 1px);
rotate: atan2(calc(var(--by) - var(--ay)), calc(var(--bx) - var(--ax)));
clip-path: polygon(0 calc(50% - var(--t)/2),calc(100% - var(--h)) calc(50% - var(--t)/2),calc(100% - var(--h)) 0,100% 50%,calc(100% - var(--h)) 100%,calc(100% - var(--h)) calc(50% + var(--t)/2),0 calc(50% + var(--t)/2));
background: #000;
}
<div class="arrow" style="--ax:10; --ay:10; --bx:50; --by:100;"></div>