Skip to content

Commit dd6f147

Browse files
authored
feat: add arrow start offset option for improved arrow positioning (#225)
* feat: add arrow start offset option for improved arrow positioning - Introduced a new `arrowStartOffset` property to control the starting position of arrows relative to the square's center. - Updated the ArrowOptions story to include a range input for adjusting the start offset. - Modified arrow rendering logic to account for the new start offset, enhancing visual accuracy. * chore: add back old comments and update variable names * refactor: prettier fomatting
1 parent b844916 commit dd6f147

File tree

3 files changed

+61
-42
lines changed

3 files changed

+61
-42
lines changed

docs/stories/options/ArrowOptions.stories.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export const ArrowOptions: Story = {
2525
activeArrowWidthMultiplier: 0.9,
2626
opacity: 0.65,
2727
activeOpacity: 0.5,
28+
arrowStartOffset: 0,
2829
};
2930

3031
// arrows
@@ -157,6 +158,24 @@ export const ArrowOptions: Story = {
157158
/>
158159
</div>
159160

161+
{/* Start Offset */}
162+
<div>
163+
<label>Start Offset ({arrowOptions.arrowStartOffset}):</label>
164+
<input
165+
type="range"
166+
min="0"
167+
max="0.5"
168+
step="0.02"
169+
value={arrowOptions.arrowStartOffset}
170+
onChange={(e) =>
171+
setarrowOptions({
172+
...arrowOptions,
173+
arrowStartOffset: Number(e.target.value),
174+
})
175+
}
176+
/>
177+
</div>
178+
160179
{/* Opacity and Active Settings */}
161180
<div>
162181
<label>Opacity ({arrowOptions.opacity}):</label>

src/Arrows.tsx

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -92,60 +92,59 @@ export function Arrows() {
9292
// This gives us the length of the arrow if it went from center to center
9393
const r = Math.hypot(dy, dx);
9494

95+
// Distance to offset the arrow start from the square center
96+
const startOffset = squareWidth * arrowOptions.arrowStartOffset;
97+
9598
let pathD: string;
9699

97-
// Is Knight move
100+
// Knight move - draw an L-shaped arrow
98101
if (r === Math.hypot(1, 2) * squareWidth) {
99-
// The mid point is only used in Knight move drawing
100-
// and here we prioritise drawing along the long edge
101-
// by defining the midpoint depending on which is bigger X or Y
102-
const mid =
103-
Math.abs(dx) < Math.abs(dy)
104-
? {
105-
x: from.x,
106-
y: to.y,
107-
}
108-
: {
109-
x: to.x,
110-
y: from.y,
111-
};
112-
113-
// Calculate the difference in x and y coordinates between mid and end points
114-
const dxEnd = to.x - mid.x;
115-
const dyEnd = to.y - mid.y;
116-
117-
// End arrow distance is always one squareWidth for Knight moves
118-
const rEnd = squareWidth;
119-
120-
// Calculate the new end point for the arrow
121-
// We subtract ARROW_LENGTH_REDUCER from the end line distance to make the arrow
122-
// stop before reaching the center of the target square
102+
// Determine which direction to draw the long leg first
103+
// We prioritize the longer axis for visual clarity
104+
const isVerticalFirst = Math.abs(dx) < Math.abs(dy);
105+
106+
// Offset start point in the direction of the first leg
107+
const start = isVerticalFirst
108+
? { x: from.x, y: from.y + Math.sign(dy) * startOffset }
109+
: { x: from.x + Math.sign(dx) * startOffset, y: from.y };
110+
111+
// Corner where the L-shape turns
112+
const corner = isVerticalFirst
113+
? { x: from.x, y: to.y }
114+
: { x: to.x, y: from.y };
115+
116+
// Calculate the final leg from corner to target
117+
const dxFinalLeg = to.x - corner.x;
118+
const dyFinalLeg = to.y - corner.y;
119+
const finalLegLength = squareWidth; // Always one square for knight moves
120+
121+
// Shorten the final leg so the arrow stops before the target center
123122
const end = {
124-
// Calculate new end x coordinate by:
125-
// 1. Taking the mid->end x direction (dxEnd)
126-
// 2. Scaling it by (rEnd - ARROW_LENGTH_REDUCER) / rEnd to shorten it
127-
// 3. Adding to the mid x coordinate
128-
x: mid.x + (dxEnd * (rEnd - ARROW_LENGTH_REDUCER)) / rEnd,
129-
// Same calculation for y coordinate
130-
y: mid.y + (dyEnd * (rEnd - ARROW_LENGTH_REDUCER)) / rEnd,
123+
x:
124+
corner.x +
125+
(dxFinalLeg * (finalLegLength - ARROW_LENGTH_REDUCER)) /
126+
finalLegLength,
127+
y:
128+
corner.y +
129+
(dyFinalLeg * (finalLegLength - ARROW_LENGTH_REDUCER)) /
130+
finalLegLength,
131131
};
132132

133-
pathD = `M${from.x},${from.y} L${mid.x},${mid.y} L${end.x},${end.y}`;
133+
pathD = `M${start.x},${start.y} L${corner.x},${corner.y} L${end.x},${end.y}`;
134134
} else {
135-
// Calculate the new end point for the arrow
136-
// We subtract ARROW_LENGTH_REDUCER from the total distance to make the arrow
137-
// stop before reaching the center of the target square
135+
// Straight arrow - offset start point toward the target
136+
const start = {
137+
x: from.x + (dx * startOffset) / r,
138+
y: from.y + (dy * startOffset) / r,
139+
};
140+
141+
// Shorten the arrow so it stops before the target center
138142
const end = {
139-
// Calculate new end x coordinate by:
140-
// 1. Taking the original x direction (dx)
141-
// 2. Scaling it by (r - ARROW_LENGTH_REDUCER) / r to shorten it
142-
// 3. Adding to the starting x coordinate
143143
x: from.x + (dx * (r - ARROW_LENGTH_REDUCER)) / r,
144-
// Same calculation for y coordinate
145144
y: from.y + (dy * (r - ARROW_LENGTH_REDUCER)) / r,
146145
};
147146

148-
pathD = `M${from.x},${from.y} L${end.x},${end.y}`;
147+
pathD = `M${start.x},${start.y} L${end.x},${end.y}`;
149148
}
150149

151150
return (

src/defaults.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,5 @@ export const defaultArrowOptions = {
7373
activeArrowWidthMultiplier: 0.9, // the multiplier for the arrow width when it is being drawn
7474
opacity: 0.65, // opacity of arrow when not being drawn
7575
activeOpacity: 0.5, // opacity of arrow when it is being drawn
76+
arrowStartOffset: 0, // how far from the center of the start square the arrow begins, as a fraction of square width (0 = center, 0.5 = edge). Values between 0.3-0.4 give a chess.com-like look where the arrow starts near the base of the piece. Values above 0.5 will start outside the square.
7677
};

0 commit comments

Comments
 (0)