A draggable and resizable div using Angular (Part 2: Resizing)

Note: This tutorial builds off of previous code that can be found here.

The repository for this project can be found on Github.

Now that we have a div that can be dragged around, let’s build in the elements to resize it. We want the div to be resizable on all sides, as well as each corner. To do that, we introduce 8 new elements (one for each side, and one for each corner).

frame.component.html

...<div class="resizers"
[style.width.px]="size.x"
[style.height.px]="size.y">
<div class="resizer right"></div>
<div class="resizer left"></div>
<div class="resizer bottom"></div>
<div class="resizer top"></div>
<div class="resizer top-right"></div>
<div class="resizer top-left"></div>
<div class="resizer bottom-left"></div>
<div #resizeCorner class="resizer bottom-right"></div>
</div>
</div>
...

frame.component.scss

.resizers {
box-sizing: border-box;

.resizer {
opacity: 1;
width: 6px;
height: 6px;
border-radius: 1px;
background: white;
border: 1px solid #d50000;
position: absolute;
}

.right {
top: 0;
right: -3px;
height: 100%;
cursor: ew-resize;
}

.bottom {
bottom: -3px;
left: 0;
width: 100%;
cursor: ns-resize;
}

.top {
top: -3px;
left: 0;
width: 100%;
cursor: ns-resize;
}

.left {
top: 0;
left: -3px;
height: 100%;
cursor: ew-resize;
}

.bottom-right {
right: -3px;
bottom: -3px;
cursor: nwse-resize;
}

.top-right {
right: -3px;
top: -3px;
cursor: nesw-resize;
}

.top-left {
left: -3px;
top: -3px;
cursor: nwse-resize;
}

.bottom-left {
left: -3px;
bottom: -3px;
cursor: nesw-resize;
}
}
}

In order to see what we’re building, we give our resizers a background color and outline. When we’re finished, we could choose to hide this by setting the opacity to zero.

We specify each anchor position and cursor display individually. By setting the cursor display, the user can intuitively know what to do when they hover over each resizer.

We add a reference to the final resizer as we’ll be using the position of this corner to help compute the resizing.

Our div should now look like this:

Note the box around the div showing each individual resizer

Now that we’ve placed our resizers where we want them to go, we now need to assign functionality to them. Let’s create a function to handle similar to the function from the previous tutorial, only we’ll be calling it (drum roll) .

Our will need to know the resizing anchor and direction in order to properly compute the resize. We’ll define two simple types to handle those:

export type ResizeAnchorType =
| 'top'
| 'left'
| 'bottom'
| 'right'

export type ResizeDirectionType =
| 'x'
| 'y'
| 'xy';

The defines the anchor location of the resizer, and the defines the resize axis.

We can now define our function:

startResize($event, anchors: ResizeAnchorType[], direction: ResizeDirectionType): void {  $event.preventDefault();  const mouseX = $event.clientX;
const mouseY = $event.clientY;
const lastX = this.position.x;
const lastY = this.position.y;
const dimensionWidth = this.resizeCornerRef.nativeElement.parentNode.offsetWidth;
const dimensionHeight = this.resizeCornerRef.nativeElement.parentNode.offsetHeight;

const duringResize = (e) => {
let dw = dimensionWidth;
let dh = dimensionHeight;
if (direction === 'x' || direction === 'xy') {
if (anchors.includes('left')) {
dw += ( mouseX - e.clientX );
} else if (anchors.includes('right')) {
dw -= ( mouseX - e.clientX );
}
}
if (direction === 'y' || direction === 'xy') {
if (anchors.includes('top')) {
dh += ( mouseY - e.clientY );
} else if (anchors.includes('bottom')) {
dh -= ( mouseY - e.clientY );
}
}

if (anchors.includes('left')) {
this.position.x = lastX + e.clientX - mouseX;
this.size.w = Math.max(dw, this.minSize.w);
}

if (anchors.includes('top')) {
this.position.y = lastY + e.clientY - mouseY;
this.size.h = Math.max(dh, this.minSize.h);
}

if (anchors.includes('bottom') || anchors.includes('right')) {
this.size.w = Math.max(dw, this.minSize.w);
this.size.h = Math.max(dh, this.minSize.h);
}

this.lastSize = { ...this.size };
};

const finishResize = (e) => {
this._document.removeEventListener('mousemove', duringResize);
this._document.removeEventListener('mouseup', finishResize);
};

this._document.addEventListener('mousemove', duringResize);
this._document.addEventListener('mouseup', finishResize);
}

Let’s break this down:

  • Upon starting to resize, we must capture the div’s resting position and size, as well as the mouse’s beginning position.
  • In the same way we and in the previous tutorial, we similarly define and functions.
  • Within the function, we provide all the logic to adjust the size and position of the . This requires handling each size based on which anchor is being activated, as well as which direction we are resizing. This involves using some simple equations. I won’t go into each one in this tutorial, although I’m more than happy to follow up with more explanation if need be.
  • We then make sure to remove the event listeners when the user releases the mouse button.

Finally, we bind to this function for each resizer in the template:

frame.component.html

...<div class="resizers"
[style.width.px]="size.w"
[style.height.px]="size.h">
<div (mousedown)="startResize($event, ['right'], 'x')" class="resizer right"></div>
<div (mousedown)="startResize($event, ['left'], 'x')" class="resizer left"></div>
<div (mousedown)="startResize($event, ['bottom'],'y')" class="resizer bottom"></div>
<div (mousedown)="startResize($event, ['top'], 'y')" class="resizer top"></div>
<div (mousedown)="startResize($event, ['top','right'], 'xy')" class="resizer top-right"></div>
<div (mousedown)="startResize($event, ['top','left'], 'xy')" class="resizer top-left"></div>
<div (mousedown)="startResize($event, ['bottom','left'], 'xy')" class="resizer bottom-left"></div>
<div #resizeCorner (mousedown)="startResize($event, ['bottom','right'], 'xy')" class="resizer bottom-right"></div>

</div>
...

Running this application we can now find that our div is fully resizable and draggable!

A resizable and draggable div

Happy Coding!

Geophysicist, Software Engineer, Web Developer, Data Scientist

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store