add transformOrigin and rotateOrigin
This commit is contained in:
parent
d3ea50b37a
commit
e33b5fa404
|
|
@ -7,6 +7,8 @@ interface TransformDOMRectInit {
|
|||
x?: number;
|
||||
y?: number;
|
||||
rotation?: number;
|
||||
transformOrigin?: Point;
|
||||
rotateOrigin?: Point;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -20,13 +22,17 @@ interface TransformDOMRectInit {
|
|||
* - Rotation is **clockwise**, in **radians**, around the rectangle's **center**.
|
||||
*/
|
||||
export class TransformDOMRect implements DOMRect {
|
||||
// Private properties for position, size, and rotation
|
||||
// Private properties for position, size, rotation, and origins
|
||||
private _x: number; // X-coordinate of the top-left corner
|
||||
private _y: number; // Y-coordinate of the top-left corner
|
||||
private _width: number; // Width of the rectangle
|
||||
private _height: number; // Height of the rectangle
|
||||
private _rotation: number; // Rotation angle in radians, clockwise
|
||||
|
||||
// New properties for transform origin and rotation origin
|
||||
private _transformOrigin: Point; // Origin for transformations
|
||||
private _rotateOrigin: Point; // Origin for rotation
|
||||
|
||||
// Internal transformation matrices
|
||||
#transformMatrix: Matrix; // Transforms from local to parent space
|
||||
#inverseMatrix: Matrix; // Transforms from parent to local space
|
||||
|
|
@ -42,11 +48,14 @@ export class TransformDOMRect implements DOMRect {
|
|||
this._height = init.height ?? 0;
|
||||
this._rotation = init.rotation ?? 0;
|
||||
|
||||
// Initialize origins with relative values (0.5, 0.5 is center)
|
||||
this._transformOrigin = init.transformOrigin ?? { x: 0.5, y: 0.5 };
|
||||
this._rotateOrigin = init.rotateOrigin ?? { x: 0.5, y: 0.5 };
|
||||
|
||||
// Initialize transformation matrices
|
||||
this.#transformMatrix = Matrix.Identity();
|
||||
this.#inverseMatrix = Matrix.Identity();
|
||||
|
||||
// Update matrices based on current properties
|
||||
this.#updateMatrices();
|
||||
}
|
||||
|
||||
|
|
@ -97,6 +106,24 @@ export class TransformDOMRect implements DOMRect {
|
|||
this.#updateMatrices();
|
||||
}
|
||||
|
||||
/** Gets or sets the **transform origin** as relative values (0 to 1). */
|
||||
get transformOrigin(): Point {
|
||||
return this._transformOrigin;
|
||||
}
|
||||
set transformOrigin(value: Point) {
|
||||
this._transformOrigin = value;
|
||||
this.#updateMatrices();
|
||||
}
|
||||
|
||||
/** Gets or sets the **rotation origin** as relative values (0 to 1). */
|
||||
get rotateOrigin(): Point {
|
||||
return this._rotateOrigin;
|
||||
}
|
||||
set rotateOrigin(value: Point) {
|
||||
this._rotateOrigin = value;
|
||||
this.#updateMatrices();
|
||||
}
|
||||
|
||||
// DOMRect read-only properties
|
||||
|
||||
/** The **left** coordinate of the rectangle (same as `x`). */
|
||||
|
|
@ -121,35 +148,54 @@ export class TransformDOMRect implements DOMRect {
|
|||
|
||||
/**
|
||||
* Updates the transformation matrices based on the current position,
|
||||
* size, and rotation of the rectangle.
|
||||
* size, rotation, and origins of the rectangle.
|
||||
*
|
||||
* The transformation sequence is:
|
||||
* 1. **Translate** to the center of the rectangle.
|
||||
* 2. **Rotate** around the center.
|
||||
* 3. **Translate** back to the top-left corner.
|
||||
* 1. **Translate** to the global position.
|
||||
* 2. **Translate** to the transform origin.
|
||||
* 3. **Rotate** around the rotation origin.
|
||||
* 4. **Translate** back from the transform origin.
|
||||
*/
|
||||
#updateMatrices() {
|
||||
// Reset the transformMatrix to identity
|
||||
this.#transformMatrix.identity();
|
||||
|
||||
// Compute the center point of the rectangle
|
||||
const centerX = this._x + this._width / 2;
|
||||
const centerY = this._y + this._height / 2;
|
||||
// Get absolute positions for origins
|
||||
const transformOrigin = this.#getAbsoluteTransformOrigin();
|
||||
const rotateOrigin = this.#getAbsoluteRotateOrigin();
|
||||
|
||||
// Apply transformations in this order:
|
||||
// 1. Translate to center
|
||||
// 2. Rotate around center
|
||||
// 3. Translate back to position
|
||||
// Apply transformations
|
||||
this.#transformMatrix
|
||||
.translate(centerX, centerY)
|
||||
// Step 1: Translate to global position
|
||||
.translate(this._x, this._y)
|
||||
// Step 2: Translate to the transform origin
|
||||
.translate(transformOrigin.x, transformOrigin.y)
|
||||
// Step 3: Rotate around the rotation origin
|
||||
.translate(rotateOrigin.x - transformOrigin.x, rotateOrigin.y - transformOrigin.y)
|
||||
.rotate(this._rotation)
|
||||
.translate(-centerX, -centerY)
|
||||
.translate(this._x, this._y);
|
||||
.translate(-(rotateOrigin.x - transformOrigin.x), -(rotateOrigin.y - transformOrigin.y))
|
||||
// Step 4: Translate back from the transform origin
|
||||
.translate(-transformOrigin.x, -transformOrigin.y);
|
||||
|
||||
// Update inverseMatrix as the inverse of transformMatrix
|
||||
this.#inverseMatrix = this.#transformMatrix.clone().invert();
|
||||
}
|
||||
|
||||
// Convert relative origins to absolute points
|
||||
#getAbsoluteTransformOrigin(): Point {
|
||||
return {
|
||||
x: this._width * this._transformOrigin.x,
|
||||
y: this._height * this._transformOrigin.y,
|
||||
};
|
||||
}
|
||||
|
||||
#getAbsoluteRotateOrigin(): Point {
|
||||
return {
|
||||
x: this._width * this._rotateOrigin.x,
|
||||
y: this._height * this._rotateOrigin.y,
|
||||
};
|
||||
}
|
||||
|
||||
// Accessors for the transformation matrices
|
||||
get transformMatrix(): Matrix {
|
||||
return this.#transformMatrix;
|
||||
|
|
|
|||
|
|
@ -272,6 +272,10 @@ export class FolkShape extends HTMLElement {
|
|||
this.width = Number(this.getAttribute('width')) || 'auto';
|
||||
this.height = Number(this.getAttribute('height')) || 'auto';
|
||||
this.rotation = (Number(this.getAttribute('rotation')) || 0) * (Math.PI / 180);
|
||||
|
||||
this.#rect.transformOrigin = { x: 0, y: 0 };
|
||||
this.#rect.rotateOrigin = { x: 0.5, y: 0.5 };
|
||||
|
||||
this.#previousRect = new TransformDOMRect(this.#rect);
|
||||
}
|
||||
|
||||
|
|
@ -405,9 +409,9 @@ export class FolkShape extends HTMLElement {
|
|||
|
||||
// Store initial angle on rotation start
|
||||
if (target.getAttribute('part')?.startsWith('rotation')) {
|
||||
const center = this.#rect.center;
|
||||
this.#initialRotation = this.#rect.rotation;
|
||||
this.#startAngle = Vector.angleFromOrigin({ x: event.clientX, y: event.clientY }, center);
|
||||
const parentRotateOrigin = this.#rect.toParentSpace(this.#rect.rotateOrigin);
|
||||
this.#startAngle = Vector.angleFromOrigin({ x: event.clientX, y: event.clientY }, parentRotateOrigin);
|
||||
}
|
||||
|
||||
// ignore interactions from slotted elements.
|
||||
|
|
@ -443,8 +447,8 @@ export class FolkShape extends HTMLElement {
|
|||
}
|
||||
|
||||
if (handle.startsWith('rotation')) {
|
||||
const center = this.#rect.center;
|
||||
const currentAngle = Vector.angleFromOrigin({ x: event.clientX, y: event.clientY }, center);
|
||||
const parentRotateOrigin = this.#rect.toParentSpace(this.#rect.rotateOrigin);
|
||||
const currentAngle = Vector.angleFromOrigin({ x: event.clientX, y: event.clientY }, parentRotateOrigin);
|
||||
const rotation = this.#initialRotation + (currentAngle - this.#startAngle);
|
||||
|
||||
let degrees = (rotation * 180) / Math.PI;
|
||||
|
|
|
|||
Loading…
Reference in New Issue