diff --git a/src/canvas/spatial-geometry.ts b/src/canvas/spatial-geometry.ts index 56bca03..06c5f0b 100644 --- a/src/canvas/spatial-geometry.ts +++ b/src/canvas/spatial-geometry.ts @@ -152,7 +152,10 @@ export class SpatialGeometry extends HTMLElement { this.addEventListener('pointerdown', this); - const shadowRoot = this.attachShadow({ mode: 'open', delegatesFocus: true }); + const shadowRoot = this.attachShadow({ + mode: 'open', + delegatesFocus: true, + }); shadowRoot.adoptedStyleSheets.push(styles); // Ideally we would creating these lazily on first focus, but the resize handlers need to be around for delegate focus to work. // Maybe can add the first resize handler here, and lazily instantiate the rest when needed? @@ -260,6 +263,21 @@ export class SpatialGeometry extends HTMLElement { const target = event.composedPath()[0] as HTMLElement; + // Store initial angle on rotation start + if (target.getAttribute('part') === 'rotate') { + // We need to store initial rotation/angle somewhere. + // This is a little awkward as we'll want to do *quite a lot* of this kind of thing. + // Might be an argument for making elements dumber (i.e. not have them manage their own state) and do this from the outside. + // But we also want to preserve the self-sufficient nature of elements' behaviour... + // Maybe some kind of shared utility, used by both the element and the outside environment? + target.dataset.initialRotation = String(this.#rotate); + const centerX = this.#x + this.#width / 2; + const centerY = this.#y + this.#height / 2; + target.dataset.startAngle = String( + Math.atan2(event.clientY - centerY, event.clientX - centerX) + ); + } + // ignore interactions from slotted elements. if (target !== this && !target.hasAttribute('part')) return; @@ -311,10 +329,16 @@ export class SpatialGeometry extends HTMLElement { } if (part === 'rotate') { - const centerX = (this.#x + this.#width) / 2; - const centerY = (this.#y + this.#height) / 2; - var newAngle = ((Math.atan2(event.clientY - centerY, event.clientX - centerX) + Math.PI / 2) * 180) / Math.PI; - this.rotate = newAngle; + const centerX = this.#x + this.#width / 2; + const centerY = this.#y + this.#height / 2; + const currentAngle = Math.atan2( + event.clientY - centerY, + event.clientX - centerX + ); + const startAngle = Number(target.dataset.startAngle); + const initialRotation = Number(target.dataset.initialRotation); + const deltaAngle = currentAngle - startAngle; + this.rotate = initialRotation + (deltaAngle * 180) / Math.PI; return; } @@ -407,7 +431,9 @@ export class SpatialGeometry extends HTMLElement { if (updatedProperties.has('rotate')) { // Although the change in resize isn't useful inside this component, the outside world might find it helpful to calculate acceleration and other physics - const notCancelled = this.dispatchEvent(new RotateEvent({ rotate: this.#rotate - this.#previousRotate })); + const notCancelled = this.dispatchEvent( + new RotateEvent({ rotate: this.#rotate - this.#previousRotate }) + ); if (notCancelled) { if (updatedProperties.has('rotate')) {