Skip to content

Commit

Permalink
feat: add resizing
Browse files Browse the repository at this point in the history
  • Loading branch information
guiseek committed Aug 23, 2024
1 parent 3dd90f3 commit 87ba429
Show file tree
Hide file tree
Showing 7 changed files with 2,297 additions and 30 deletions.
2,152 changes: 2,152 additions & 0 deletions public/backgrounds/wall.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 60 additions & 9 deletions src/features/canvas/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class Canvas extends HTMLCanvasElement {
this.#layers
.filter((layer) => layer.active)
.sort((a, b) => a.order - b.order)
.map((layer) => {
.map(async (layer) => {
await layer.render()
const {width, height} = layer
const {x, y} = layer.position
this.context?.drawImage(layer, x, y, width, height)
Expand All @@ -58,25 +59,75 @@ class Canvas extends HTMLCanvasElement {
return current.order > highest.order ? current : highest
}, colliders[0])

this.#layer = layer
layer.startDrag(position)
const direction = this.#getResizeDirection(layer, position)

if (direction.x || direction.y) {
this.#layer = layer
layer.startResize(direction)
} else {
this.#layer = layer
layer.startDrag(position)
}
}
}

#onMouseMove = (event: MouseEvent) => {
if (this.#layer) {
const {offsetX, offsetY} = event
const newPosition = new Vector2(offsetX, offsetY)
this.#layer.dragTo(newPosition)
const {offsetX, offsetY} = event
const position = new Vector2(offsetX, offsetY)

if (this.#layer && this.#layer.dragging) {
this.#layer.dragTo(position)
this.render()
} else if (this.#layer && this.#layer.resizing) {
this.#layer.resizeTo(position)
this.render()
} else {
const colliders = this.#layers.filter(({rect}) =>
position.isCollision(rect)
)

const hoveredLayer = colliders.reduce((highest, current) => {
return current.order > highest.order ? current : highest
}, colliders[0])

if (hoveredLayer) {
const direction = this.#getResizeDirection(hoveredLayer, position)

if (direction.x || direction.y) {
this.style.cursor =
direction.x && direction.y
? 'nwse-resize'
: direction.x
? 'ew-resize'
: 'ns-resize'
} else {
this.style.cursor = 'default'
}
} else {
this.style.cursor = 'default'
}
}
}

#getResizeDirection(layer: Layer, position: Vector2) {
const cornerSize = 10
const rect = layer.rect

return {
x: position.x >= rect.x + rect.w - cornerSize,
y: position.y >= rect.y + rect.h - cornerSize,
}
}

#onMouseUp = () => {
if (this.#layer) {
this.#layer.stopDrag()
if (this.#layer.resizing) {
this.#layer.stopResize()
} else {
this.#layer.stopDrag()
}
this.#layer = null
this.render()
this.render().then()
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/features/layers/image-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ export class ImageLayer extends Layer {
async render() {
if (!this.context?.drawImage) return

this.context.clearRect(0, 0, this.width, this.height)

await this.image.decode()
const {x, y, w, h} = this.rect
this.context.drawImage(this.image, x, y, w, h)
const {w, h} = this.rect
this.context.drawImage(this.image, 0, 0, w, h)
}

setSrc(src: string) {
Expand Down
57 changes: 56 additions & 1 deletion src/features/layers/layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@ export abstract class Layer extends OffscreenCanvas {

protected _draggable = true

protected _resizable = true

protected _dragging = false

protected _resizing = false

protected _aspectRatio: number

protected _resizeDirection = {x: false, y: false}

protected _active = true

protected _order = 1
Expand All @@ -23,6 +31,10 @@ export abstract class Layer extends OffscreenCanvas {
return this._dragging
}

get resizing() {
return this._resizing
}

get offset() {
return this._offset
}
Expand All @@ -48,9 +60,9 @@ export abstract class Layer extends OffscreenCanvas {
super(w, h)
this._position = new Vector2(x, y)
this.context = this.getContext('2d')
this._aspectRatio = w / h
}


abstract render(): Promise<void>

setActive(value: boolean) {
Expand Down Expand Up @@ -87,4 +99,47 @@ export abstract class Layer extends OffscreenCanvas {
stopDrag() {
this.setDragging(false)
}

setResizable(resizable: boolean) {
this._resizable = resizable
return this
}

protected setResizing(resizing: boolean) {
this._resizing = resizing
return this
}

startResize(direction: Vector2Direction) {
this.setResizing(true)
this._resizeDirection = direction
}

resizeTo(point: Vector2) {
if (this._resizing) {
const {x, y} = this._resizeDirection

if (x && y) {
const dx = point.x - this.position.x
const dy = point.y - this.position.y
const scale = Math.max(dx / this.width, dy / this.height)

this.width = this.width * scale
this.height = this.height * scale
} else if (x) {
this.width = Math.max(10, point.x - this.position.x)
this.height = this.width / this._aspectRatio
} else if (y) {
this.height = Math.max(10, point.y - this.position.y)
this.width = this.height * this._aspectRatio
}

this.render()
}
}

stopResize() {
this.setResizing(false)
this._resizeDirection = {x: false, y: false}
}
}
2 changes: 2 additions & 0 deletions src/features/layers/text-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export class TextLayer extends Layer {
async render() {
if (!this.context?.drawImage) return

this.context.clearRect(0, 0, this.width, this.height)

this.context.fillStyle = this._color
this.context.font = this.font

Expand Down
36 changes: 18 additions & 18 deletions src/shared/utils/math/vector2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,46 @@ export class Vector2 {
constructor(public x = 0, public y = 0) {}

set(x: number, y: number) {
this.x = x;
this.y = y;
this.x = x
this.y = y
}

clone() {
return new Vector2(this.x, this.y);
return new Vector2(this.x, this.y)
}

copy(v: Vector2) {
this.x = v.x;
this.y = v.y;
this.x = v.x
this.y = v.y

return this;
return this
}

add(v: Vector2) {
this.x += v.x;
this.y += v.y;
this.x += v.x
this.y += v.y

return this;
return this
}

sub(v: Vector2) {
this.x -= v.x;
this.y -= v.y;
this.x -= v.x
this.y -= v.y

return this;
return this
}

isCollision(v2: RectLike) {
const topLeft = v2;
const clone = new Vector2(topLeft.x, topLeft.y);
const vector = new Vector2(v2.w, v2.h);
const bottomRight = clone.add(vector);
isCollision = (v2: RectLike) => {
const topLeft = v2
const clone = new Vector2(topLeft.x, topLeft.y)
const vector = new Vector2(v2.w, v2.h)
const bottomRight = clone.add(vector)

return (
this.x >= topLeft.x &&
this.y >= topLeft.y &&
this.x <= bottomRight.x &&
this.y <= bottomRight.y
);
)
}
}
5 changes: 5 additions & 0 deletions src/vite-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ interface Vector2Like {
y: number
}

interface Vector2Direction {
x: boolean
y: boolean
}

interface RectLike {
x: number
y: number
Expand Down

0 comments on commit 87ba429

Please sign in to comment.