Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by SupernovaTraveler109

How can I compute CSS offsets to correctly position a rotated arrow between fixed endpoints?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I'm working on a side project to draw diagrams by dragging sticky notes around a grid. I want to connect these notes with arrows. I have already calculated the arrow's length (using the Pythagorean theorem) and its angle using the following methods:

TYPESCRIPT
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))); }

My current CSS arrow solution works perfectly for horizontal and vertical cases, but when the arrow is rotated to an arbitrary angle (not divisible by 90°), the position becomes off. I need a good method to calculate the "new" top and left offsets after rotation, or a simpler way to adjust the positioning, possibly with margins. Any insights or solutions would be greatly appreciated.

1

Comments 0

Answers 4

Views 75

4 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

Below is one effective approach: rather than trying to “guess” a margin, you can compute how the rotated element’s bounding box shifts. One method is to calculate the rotated positions of your element’s corners (or at least the reference corner relative to your transform origin) and then derive the offset needed to realign the element as desired.

Assume you have an element (such as your arrow’s container) with a known width and height and that you rotate it by an angle (in degrees). If you set the transform‑origin to the center of the element then the visual center remains in place—but the CSS “top” and “left” that you might use for positioning do not automatically update. What you can do is calculate the offset induced by rotation.

How to Compute the Offsets

  1. Decide on a transform origin. For example, if you choose the element’s center then you know:

    • Center point:
    cx = width / 2
    cy = height / 2

  2. Pick the reference corner you want to “anchor” (say the top‑left of the unrotated element) whose new position you’d like to know after rotation.

  3. Rotate that point about the transform origin using the standard rotation matrix. For an angle θ (in radians) the formulas are:

    • x′ = cx + (x – cx) · cos(θ) – (y – cy) · sin(θ)
    • y′ = cy + (x – cx) · sin(θ) + (y – cy) · cos(θ)

    For the top‑left corner, (x, y) is (0, 0).

  4. The new coordinates (x′, y′) tell you where that originally positioned corner is now rendered. If you want the rotated element to appear in the same place as before, you can then offset the element’s top and left by –x′ and –y′.

Example Function in TypeScript

Below is an example function that calculates the new top‑left offset for an element of given width and height when rotated by angle (in degrees) about its center:

TYPESCRIPT
/** * Given an element's width/height and a rotation angle (in degrees, applied about its center), * this function calculates the offset that should be applied to keep the top-left of * the element in the same position as before rotation. */ function calcRotationOffset(width: number, height: number, angleDeg: number): { offsetX: number, offsetY: number } { const angleRad = angleDeg * Math.PI / 180; // Original top-left coordinates (0,0) // and transform origin (center of the element) const cx = width / 2; const cy = height / 2; // Calculate rotated position of original top-left (0, 0) const rotatedX = cx + (0 - cx) * Math.cos(angleRad) - (0 - cy) * Math.sin(angleRad); const rotatedY = cy + (0 - cx) * Math.sin(angleRad) + (0 - cy) * Math.cos(angleRad); // The rotation moves the top-left point to (rotatedX, rotatedY) // To compensate, you need to offset by the negative of this shift. return { offsetX: -rotatedX, offsetY: -rotatedY }; }

How to Use

  1. When you position your arrow element, set its transform origin to center using CSS:

    CSS
    .arrow { position: absolute; transform-origin: center; }
  2. Then, in your code, compute the offset depending on the arrow’s width, height, and the angle computed from your calculateAngle() function:

    TYPESCRIPT
    const arrowWidth = 100; // your arrow element width const arrowHeight = 10; // your arrow element height const angle = calculateAngle(); // your angle in degrees const { offsetX, offsetY } = calcRotationOffset(arrowWidth, arrowHeight, angle); // Now, set top and left with the offsets, and also your rotation transform: const arrowElement = document.getElementById('yourArrowId'); if (arrowElement) { arrowElement.style.left = (baseLeft + offsetX) + 'px'; arrowElement.style.top = (baseTop + offsetY) + 'px'; arrowElement.style.transform = `rotate(${angle}deg)`; // baseLeft and baseTop are the intended positions before rotation }

Explanation

  • The function calcRotationOffset computes where the top‑left corner ends up after rotation about the center.
  • By applying the negative of that offset to your element (after rotation), the visible (rotated) element will appear anchored at the desired initial (unrotated) position.
  • Adjusting the base offset in your code (here shown as baseLeft and baseTop), allows you to mix in your arrow placement logic along with this rotation compensation.

This approach avoids manual “trial and error” with margins and provides a mathematical basis for repositioning elements that are rotated in CSS.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by OrbitalResearcher438 1 month ago

0

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:

CSS
.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; }
HTML
<div class="arrow" style="--ax:10; --ay:10; --bx:50; --by:100;"></div>

Run code snippetHide resultsExpand snippet

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:

CSS
.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; }
HTML
<div class="arrow" style="--ax:10; --ay:10; --bx:50; --by:100;"></div>

Run code snippetHide resultsExpand snippet

No comments yet.

Answer by AstralWatcher159 1 month ago

0

By default transform-origin refers to the center. So if your original (left, top) is (100, 100) and width is 80, then center is at (100 + 80 / 2) etc.

In our example I've used 4 variables to make the point clear. The x, y, radius and deg of angle. I added also 3 pixels for the origin, the center, and the target point. The formula is simple sin and cos, you can look at the code.

I've also added some JS as a proof of concept.

JAVASCRIPT
var deg = 0 setInterval(function() { deg += 1 document.documentElement.style.setProperty("--deg", deg + "deg") }, 40)
CSS
:root { --origin-x: 100px; --origin-y: 100px; --radius: 40px; --deg: 45deg; } .arrow { position: absolute; left: var(--origin-x); top: var(--origin-y); width: calc(var(--radius) * 2); height: 0; border-bottom: 5px solid #000000; transform: rotate(var(--deg)); } .arrow::after { content: ""; border-top: 10px solid transparent; border-bottom: 10px solid transparent; border-left: 20px solid #000000; position: absolute; right: -10px; top: -8px; } .cavnas { border: 1px solid gray; position: relative; width: 300px; height: 180px; overflow: hidden; } .pixel { width: 3px; height: 3px; background: red; position: absolute; } .pixel-origin { left: var(--origin-x); top: var(--origin-y); } .pixel-center { left: calc(var(--origin-x) + var(--radius)); top: var(--origin-y); } .pixel-target { left: calc(var(--origin-x) + var(--radius) + var(--radius) * cos(var(--deg))); top: calc(var(--origin-y) + var(--radius) * sin(var(--deg))); }
HTML
<div class="cavnas"> <div class="arrow"></div> <div class="pixel pixel-origin"></div> <div class="pixel pixel-center"></div> <div class="pixel pixel-target"></div> </div>

Run code snippetHide resultsExpand snippet

No comments yet.

Answer by AstralGuide272 1 month ago

0

You can make it a lot simpler if you allow CSS to calculate rotation for your.

Here is the idea:

CSS
.arrow { margin-bottom: 60px; --t: 45%; --h: 20%; aspect-ratio: 10/2; width: 100px; background: black; 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)); } .r10 { transform: rotate(10deg); } .r45 { transform: rotate(45deg); } .r90 { transform: rotate(90deg); } .r135 { transform: rotate(135deg); } .r180 { transform: rotate(180deg); }
HTML
<!DOCTYPE html> <html> <body> <section class="arrow"></section> <section class="arrow r10"></section> <section class="arrow r45"></section> <section class="arrow r90"></section> <section class="arrow r135"></section> <section class="arrow r180"></section> </body> </html>

Run code snippetHide resultsExpand snippet

Pay attention for transform: rotate().

The main outline is borrowed from The Ultimate Collection of CSS-only Shapes.

No comments yet.

Discussion

No comments yet.