Resize a Div on Border Drag and Drop Without Adding Extra Markup

Resize a div on border drag and drop without adding extra markup

It is certainly possible to do this without an extra div. Use css and ::after to create the border and change the cursor. Use MouseEvent.offsetX to determine whether to process a click in the element.

In your example, you want a click on the main div, but only on the first 4 pixels. You can do that by checking for e.offsetX < 4 in your click handler:

const BORDER_SIZE = 4;
const panel = document.getElementById("right_panel");

let m_pos;
function resize(e){
const dx = m_pos - e.x;
m_pos = e.x;
panel.style.width = (parseInt(getComputedStyle(panel, '').width) + dx) + "px";
}

panel.addEventListener("mousedown", function(e){
if (e.offsetX < BORDER_SIZE) {
m_pos = e.x;
document.addEventListener("mousemove", resize, false);
}
}, false);

document.addEventListener("mouseup", function(){
document.removeEventListener("mousemove", resize, false);
}, false);
#right_panel {
position: absolute;
width: 96px;
padding-left: 4px;
height: 100%;
right: 0;
background-color: #f0f0ff;
}

#right_panel::after {
content: '';
background-color: #ccc;
position: absolute;
left: 0;
width: 4px;
height: 100%;
cursor: ew-resize;
}
<body>
<div id="right_panel"></div>
</body>

How to limit the direction of resize of a container with mouse

You may add additional condition in to your resize function, something like this:

const minWidth = 150
const maxWidth = 500
const minHeight = 150
const maxHeight = 500

function resize(e) {
const dx = m_pos - e.x;
const dy = m_pos1 - e.y;
m_pos = e.x;
m_pos1 = e.y;

const newWidth = parseInt(getComputedStyle(panel).width) - dx
const newHeight = parseInt(getComputedStyle(panel).height - dy

if (newWidth > minWidth && newWidth < maxWidth) {
panel.style.width = newWidth + "px";
}
if (newHeight > minHeight && newHeight < maxHeight) {
panel.style.height = newHeight + "px";
}
}

Detect if user trying resize a draggable element

You could wrap the mouse-target (.draggable) inside a resizeable container element.

This way, the UI for the CSS-resize will get hit before the draggable element and you can handle only the dragging nicely:

!function(){
"use strict";
let x, y, drag;
document.addEventListener("mousedown", function(e) {
if (e.target.parentNode.lastChild !== e.target && e.target.parentNode.classList.contains("main")) {
//bring element to the front and dispatch mousedown event again otherwise resize doesn't work
e.target.parentNode.appendChild(e.target);
return e.target.dispatchEvent(new MouseEvent(e.type, e));
}

if (!e.target.classList.contains("draggable"))
return;

e.preventDefault();
drag = e.target.parentNode;
x = e.x - drag.offsetLeft;
y = e.y - drag.offsetTop;
document.body.classList.add("drag");
drag.classList.add("drag");
});

document.addEventListener("mouseup", function(e) {
document.body.classList.remove("drag");
drag = drag && drag.classList.remove("drag");
});

document.addEventListener("mousemove", function(e) {
if (!drag || e.x - drag.offsetLeft == x || e.y - drag.offsetTop == y)
return;

drag.style.left = (e.x - x) + "px";
drag.style.top = (e.y - y) + "px";
});

/*init*/
for (let i = 0, c, d = document.getElementsByClassName("main")[0].children; i < d.length; i++) {
c = (0x1000000 + Math.random() * 0xffffff).toString(16).substr(1, 6);
d[i].style.backgroundColor = '#' + c;
d[i].classList.toggle("dark", ((parseInt(c.substr(0, 2), 16) * 299) + (parseInt(c.substr(2, 2), 16) * 587) + (parseInt(c.substr(4, 2), 16) * 114)) / 1000 < 128);
d[i].style.left = document.documentElement.scrollWidth / 8 + Math.random() * (document.documentElement.scrollWidth / 1.33 - d[i].offsetWidth) + "px";
d[i].style.top = document.documentElement.scrollHeight / 8 + Math.random() * (document.documentElement.scrollHeight / 1.33 - d[i].offsetHeight) + "px";
}
}()
.resizeable {
width: 5em;
height: 5em;
border: 1px solid black;
position: absolute;
resize: both;
overflow: hidden;
mix-blend-mode: hard-light;
border-radius: 0.3em;
}

div.main>div:hover {
box-shadow: 0 0 5px black;
}

div.main>div:last-child {
box-shadow: 0 0 10px black;
}

div.draggable {
cursor: grab;
width: 100%;
height: 100%;
display: flex;
}
div.no-drag {
display: flex;
}
div.main div.no-drag:before {
content: "can't move me";
}
div.draggable:before {
content: "move me";
}
div.main div:before {
color: black;
text-shadow: 0 0 1em black;
margin: auto;
text-align: center;
}

div.main>div.dark:before {
color: white;
text-shadow: 0 0 1em white;
}

body.drag {
user-select: none;
}

body.drag div.draggable {
cursor: grabbing;
}
<div class="main">
<div class="resizeable no-drag"></div>
<div class="resizeable"><div class="draggable"></div></div>
<div class="resizeable"><div class="draggable"></div></div>
<div class="resizeable"><div class="draggable"></div></div>
</div>

Resizing a canvas element via dragging the edges

  1. Put your fabric.js canvas in a wrapper div.
  2. Make the wrapper resizable. Here I'm using CSS resize. It only adds a bottom-left corner as a resize control but it's good enough for the sake of the demo. To have all the edges as controls you can try something like this.
  3. Detect the wrapper's size change. Ideally, you would use something like ResizeObserver. However, since browser support is still about 80% at the time of posting this, you might feel the need to use a polyfill or write something specific to your case. A simple setInterval with size check might prove to be sufficient.
  4. When the wrapper's size changes, set new fabric.js canvas dimensions via setWidth() and setHeight().

const canvas = new fabric.Canvas('c')

canvas.add(new fabric.Rect({
width: 100,
height: 100,
fill: 'red',
left: 100,
top: 50,
}))

const canvasWrapper = document.getElementById('wrapper')
// initial dimensions
canvasWrapper.style.width = '300px'
canvasWrapper.style.height = '150px'

let width
let height
setInterval(() => {
const newWidth = canvasWrapper.clientWidth
const newHeight = canvasWrapper.clientHeight
if (newWidth !== width || newHeight !== height) {
width = newWidth
height = newHeight
canvas.setWidth(newWidth)
canvas.setHeight(newHeight)
}
}, 100)
#wrapper {
border: solid 1px black;
resize: both;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.2/fabric.min.js"></script>
<div id="wrapper">
<canvas id="c"></canvas>
</div>

How can I resize a DIV by dragging just ONE side of it?

There is a much simpler way to achieve this. CSS 3 has a resize property to make an HTML element resizeable, while following other CSS properties like min/max widths etc.

.resizeable {
resize: both;
overflow: auto;
border: 2px solid black;
}

For more info, here are the MDN Docs on the resize CSS 3 property.



Related Topics



Leave a reply



Submit