Skip to content

Commit

Permalink
feat: ✨ finished rotation anchoring when resizing
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulRill00 committed Dec 8, 2023
1 parent 6b50e06 commit 89be65a
Showing 1 changed file with 88 additions and 31 deletions.
119 changes: 88 additions & 31 deletions src/utils/Resizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type RectDim = {
l: number;
w: number;
h: number;
r: number;
};

type BoundingRect = {
Expand All @@ -25,6 +26,8 @@ type CallbackOptions = {
resizer: Resizer;
};

type Coordinate = Pick<RectDim, 't'|'l'>;

export interface ResizerOptions {
/**
* Function which returns custom X and Y coordinates of the mouse.
Expand Down Expand Up @@ -443,6 +446,59 @@ export default class Resizer {
}
}

/**
* Get any of the 8 handlers from the rectangle,
* and get it's coordinates based on zero degrees rotation.
*/
private getRectCoordiante(handler: string, rect: RectDim): Coordinate {
switch (handler) {
case 'tl': return { t: rect.t, l: rect.l };
case 'tr': return { t: rect.t, l: rect.l + rect.w };
case 'bl': return { t: rect.t + rect.h, l: rect.l };
case 'br': return { t: rect.t + rect.h, l: rect.l + rect.w };
case 'tc': return { t: rect.t, l: rect.l + (rect.w / 2) };
case 'cr': return { t: rect.t + (rect.h / 2), l: rect.l + rect.w };
case 'bc': return { t: rect.t + rect.h, l: rect.l + (rect.w / 2) };
case 'cl': return { t: rect.t + (rect.h / 2), l: rect.l };
default: throw new Error('Invalid handler ' + handler);
}
}

/**
* Get opposite coordinate on rectangle based on distance to center
*/
private getOppositeRectCoordinate(coordinate: Coordinate, rect: RectDim): Coordinate {
const cx = rect.l + (rect.w / 2);
const cy = rect.t + (rect.h / 2);

const dx = cx - coordinate.l;
const dy = cy - coordinate.t;

const nx = cx + dx;
const ny = cy + dy;

return { l: nx, t: ny }
}

/**
* Rotate a rectangle coordinate around it's center and given rectangle rotation
*/
private rotateCoordinate(coordinate: Coordinate, rect: RectDim): Coordinate {
const cx = rect.l + (rect.w / 2);
const cy = rect.t + (rect.h / 2);

const a = ((rect.r));
const theta = a * (Math.PI / 180);

const x = coordinate.l;
const y = coordinate.t;

const rx = (x - cx) * Math.cos(theta) - (y - cy) * Math.sin(theta) + cx;
const ry = (x - cx) * Math.sin(theta) + (y - cy) * Math.cos(theta) + cy;

return { l: rx, t: ry }
}

/**
* Start resizing
* @param {Event} e
Expand All @@ -462,13 +518,18 @@ export default class Resizer {
const rect = this.getElementPos(el!, { avoidFrameZoom: true, avoidFrameOffset: true });
const parentRect = this.getElementPos(parentEl!);
const target = e.target as HTMLElement;
const _rotation = getComputedStyle(el).rotate;
const rotation = (Number((_rotation === 'none' ? '0deg' : _rotation).replace('deg', '')) + 360) % 360;

this.handlerAttr = target.getAttribute(attrName)!;
this.clickedHandler = target;

this.startDim = {
t: Number.parseFloat(el?.computedStyleMap().get('top')?.toString() ?? '0'),
l: Number.parseFloat(el?.computedStyleMap().get('left')?.toString() ?? '0'),
w: rect.width,
h: rect.height,
r: rotation
};
this.rectDim = {
...this.startDim,
Expand All @@ -484,12 +545,9 @@ export default class Resizer {
l: parentRect.left,
w: parentRect.width,
h: parentRect.height,
r: 0
};

console.table({ name: 'startPos', ...this.startPos });
console.table({ name: 'startDim', ...this.startDim });
console.table({ name: 'parentDim', ...this.parentDim });

// Listen events
const docs = this.getDocumentEl();
this.docs = docs;
Expand All @@ -516,9 +574,18 @@ export default class Resizer {
y: e.clientY,
};
this.currentPos = currentPos;

// Calculate delta based on rotation and x,y shift
const theta = (Math.PI / 180) * -this.startDim!.r;

const sx = this.startPos!.x * Math.cos(theta) - this.startPos!.y * Math.sin(theta);
const sy = this.startPos!.x * Math.sin(theta) + this.startPos!.y * Math.cos(theta);
const cx = currentPos.x * Math.cos(theta) - currentPos.y * Math.sin(theta);
const cy = currentPos.x * Math.sin(theta) + currentPos.y * Math.cos(theta);

this.delta = {
x: currentPos.x - this.startPos!.x,
y: currentPos.y - this.startPos!.y,
x: cx - sx,
y: cy - sy
};
this.keys = {
shift: e.shiftKey,
Expand Down Expand Up @@ -559,14 +626,26 @@ export default class Resizer {
const config = this.opts;
const rect = this.rectDim!;
const updateTarget = this.updateTarget;
const selectedHandler = this.getSelectedHandler();
const { unitHeight, unitWidth, keyWidth, keyHeight } = config;

// Calculate difference between locking point after new dimensions
const coordiantes = [this.startDim!, rect].map(rect => {
const handlerCoordinate = this.getRectCoordiante(this.handlerAttr!, rect);
const oppositeCoordinate = this.getOppositeRectCoordinate(handlerCoordinate, rect);
return this.rotateCoordinate(oppositeCoordinate, rect);
})

const diffX = coordiantes[0].l - coordiantes[1].l;
const diffY = coordiantes[0].t - coordiantes[1].t;

rect.t += diffY;
rect.l += diffX;

// Use custom updating strategy if requested
if (isFunction(updateTarget)) {
updateTarget(el, rect, {
store,
selectedHandler,
selectedHandler: this.handlerAttr,
resizer,
config,
});
Expand Down Expand Up @@ -604,22 +683,6 @@ export default class Resizer {
});
}

/**
* Get selected handler name
* @return {string}
*/
getSelectedHandler() {
var handlers = this.handlers;

if (!this.selectedHandler) {
return;
}

for (let n in handlers) {
if (handlers[n] === this.selectedHandler) return n;
}
}

/**
* Handle ESC key
* @param {Event} e
Expand Down Expand Up @@ -673,6 +736,7 @@ export default class Resizer {
l: startDim.l,
w: startW,
h: startH,
r: startDim.r
};

if (!data) return;
Expand Down Expand Up @@ -726,13 +790,6 @@ export default class Resizer {
}
}

if (~attr.indexOf('l')) {
box.l += startDim.w - box.w;
}
if (~attr.indexOf('t')) {
box.t += startDim.h - box.h;
}

for (const key in box) {
const i = key as keyof RectDim;
box[i] = parseInt(`${box[i]}`, 10);
Expand Down

0 comments on commit 89be65a

Please sign in to comment.