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
- Put your fabric.js canvas in a wrapper div.
- 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.
- 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. - When the wrapper's size changes, set new fabric.js canvas dimensions via
setWidth()
andsetHeight()
.
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
What the Difference of This.State and This.Setstate in Reactjs
How to Check If Page Exists Using JavaScript
How to Remove One Specific Selected File from Input File Control
Why Array.Indexof Doesn't Find Identical Looking Objects
How to Loop Through All the Elements Returned from Getelementsbytagname
Console.Log Showing Only the Updated Version of the Object Printed
Should an Async API Ever Throw Synchronously
Differences Between JavaScript Regexp Literal and Constructor
Completely Removing Duplicate Items from an Array
Difference Between the 'Controller', 'Link' and 'Compile' Functions When Defining a Directive
How to Set a JavaScript Breakpoint from Code in Chrome
Http Get Request in Node.Js Express
How to Run Function in Angularjs Controller on Document Ready
Regex to Check Whether a String Contains Only Numbers