Constraint System
Constraints limit how interactive components can move, enabling precise control over user interactions.
Available Constraints
horizontal
Constrains movement to a horizontal line at a specific y-coordinate.
import { horizontal } from '@wangyaoshen/locus-interaction';
// Only move along y = 100
const constraint = horizontal(100);
vertical
Constrains movement to a vertical line at a specific x-coordinate.
import { vertical } from '@wangyaoshen/locus-interaction';
// Only move along x = 50
const constraint = vertical(50);
grid
Snaps positions to a grid.
import { grid } from '@wangyaoshen/locus-interaction';
// Snap to 10x10 grid
const constraint = grid({ size: 10 });
// Snap to different x/y sizes
const constraint = grid({ sizeX: 20, sizeY: 10 });
// Snap to grid with offset
const constraint = grid({ size: 10, offsetX: 5, offsetY: 5 });
circular
Constrains movement to a circle.
import { circular } from '@wangyaoshen/locus-interaction';
// Move on circle centered at (100, 100) with radius 50
const constraint = circular([100, 100], 50);
line
Constrains movement to a line segment between two points.
import { line } from '@wangyaoshen/locus-interaction';
// Move along line from (0, 0) to (100, 100)
const constraint = line([0, 0], [100, 100]);
bounds
Constrains movement within a rectangular boundary.
import { bounds } from '@wangyaoshen/locus-interaction';
// Stay within rectangle
const constraint = bounds({
xMin: 0,
xMax: 500,
yMin: 0,
yMax: 300,
});
Composing Constraints
Use compose to combine multiple constraints. They are applied in order.
import { compose, grid, bounds } from '@wangyaoshen/locus-interaction';
// Snap to grid AND stay within bounds
const constraint = compose(
grid({ size: 10 }),
bounds({ xMin: 0, xMax: 500, yMin: 0, yMax: 300 }),
);
Using Constraints
Apply constraints when creating interactive components:
import { InteractivePoint, horizontal, bounds, compose } from '@wangyaoshen/locus-interaction';
const point = new InteractivePoint({
position: [100, 100],
constrain: compose(
horizontal(100),
bounds({ xMin: 0, xMax: 500, yMin: 100, yMax: 100 }),
),
});
Custom Constraints
Create custom constraint functions:
import { ConstraintFunction, Vector2 } from '@wangyaoshen/locus-interaction';
// Custom constraint: only allow positions where x + y < 200
const customConstraint: ConstraintFunction = (pos: Vector2): Vector2 => {
const [x, y] = pos;
if (x + y >= 200) {
// Project to the line x + y = 200
const sum = x + y;
const ratio = 200 / sum;
return [x * ratio, y * ratio];
}
return pos;
};
const point = new InteractivePoint({
position: [50, 50],
constrain: customConstraint,
});
Constraint Function Signature
type ConstraintFunction = (
position: Vector2,
options?: ConstraintOptions
) => Vector2;
interface ConstraintOptions {
previous?: Vector2; // Previous position (for velocity-based constraints)
bounds?: ViewportBounds; // Viewport bounds
}