Skip to content

Instantly share code, notes, and snippets.

@steveruizok
Last active May 13, 2022 06:30
Show Gist options
  • Save steveruizok/3f8033ea9e9e655633dc8d21d78e5686 to your computer and use it in GitHub Desktop.
Save steveruizok/3f8033ea9e9e655633dc8d21d78e5686 to your computer and use it in GitHub Desktop.
Resizing boxes by corner.
interface Box {
x: number
y: number
w: number
h: number
maxX: number
maxY: number
}
interface BoxWithId extends Box {
id: string
}
export function getCornerResizer(boxes: BoxWithId[], corner: number) {
// Get bounding box for all boxes
let { x: minX, y: minY, maxX, maxY } = { ...boxes[0] }
for (let box of boxes) {
minX = Math.min(minX, box.x)
minY = Math.min(minY, box.y)
maxX = Math.max(maxX, box.maxX)
maxY = Math.max(maxY, box.maxY)
}
const bounds = {
x: minX,
y: minY,
maxX,
maxY,
w: maxX - minX,
h: maxY - minY
}
// Get normalized boxes, where each property is a normal of the bounding box
// For example, an x: .5, y: .75 would be at half of the bounds width, 3/4 its height.
const normalizedBoxes: Record<string, Box> = {}
for (let box of boxes) {
normalizedBoxes[box.id] = {
x: (box.x - minX) / bounds.w,
y: (box.y - minY) / bounds.h,
maxX: 1 - (box.maxX - minX) / bounds.w,
maxY: 1 - (box.maxY - minY) / bounds.h,
w: box.w / bounds.w,
h: box.h / bounds.h
}
}
const aspectRatio = bounds.w / bounds.h
let flipX: boolean,
flipY: boolean,
isRightCorner: boolean,
isTopCorner: boolean
// Resizing function
return (point: { x: number; y: number }, lockAspect = false) => {
isTopCorner = corner < 2 // 0TL or 1TR
isRightCorner = corner % 3 > 0 // 1TR or 2BR
// Update dragging corners
if (isTopCorner) minY = point.y
else maxY = point.y
if (isRightCorner) maxX = point.x
else minX = point.x
// Are we flipped?
flipX = maxX < minX
flipY = maxY < minY
bounds.w = Math.abs(maxX - minX)
bounds.h = Math.abs(maxY - minY)
if (lockAspect) {
if (bounds.w / bounds.h > aspectRatio) {
// Scale bounds height by aspect ratio
// and move points to fit new height
bounds.h = bounds.w / aspectRatio
if (isTopCorner) {
minY = flipY ? maxY + bounds.h : maxY - bounds.h
} else {
maxY = flipY ? minY - bounds.h : minY + bounds.h
}
} else {
// Scale bounds width by aspect ratio
// and move points to fit new width
bounds.w = bounds.h * aspectRatio
if (isRightCorner) {
maxX = flipX ? minX - bounds.w : minX + bounds.w
} else {
minX = flipX ? maxX + bounds.w : maxX - bounds.w
}
}
}
bounds.x = flipX ? maxX : minX
bounds.y = flipY ? maxY : minY
bounds.maxX = bounds.x + bounds.w
bounds.maxY = bounds.y + bounds.h
for (let box of boxes) {
// Use the box's initial size normals to calculate
// its size in the new bounds.
const nBox = normalizedBoxes[box.id]
box.x = bounds.x + (flipX ? nBox.maxX : nBox.x) * bounds.w
box.y = bounds.y + (flipY ? nBox.maxY : nBox.y) * bounds.h
box.w = nBox.w * bounds.w
box.h = nBox.h * bounds.h
}
return { boxes, bounds }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment