If anyone could help me figure out how to make scaled draggable elements, I'd really appreciate any guidance.
Drag works fine when transform: scale(1), but doesn't work correctly when something like transform: scale(1.1).
I tried dividing left and top by the scaler while dragging but didn't work.
const el = document.querySelector(".item");
let isResizing = false;
el.addEventListener("mousedown", mousedown);
function mousedown(e) {
window.addEventListener("mousemove", mousemove);
window.addEventListener("mouseup", mouseup);
let prevX = e.clientX;
let prevY = e.clientY;
function mousemove(e) {
if (!isResizing) {
let newX = prevX - e.clientX;
let newY = prevY - e.clientY;
const rect = el.getBoundingClientRect();
el.style.left = rect.left - newX + "px";
el.style.top = rect.top - newY + "px";
prevX = e.clientX;
prevY = e.clientY;
}
}
function mouseup() {
window.removeEventListener("mousemove", mousemove);
window.removeEventListener("mouseup", mouseup);
}
}
const resizers = document.querySelectorAll(".resizer");
let currentResizer;
for (let resizer of resizers) {
resizer.addEventListener("mousedown", mousedown);
function mousedown(e) {
currentResizer = e.target;
isResizing = true;
let prevX = e.clientX;
let prevY = e.clientY;
window.addEventListener("mousemove", mousemove);
window.addEventListener("mouseup", mouseup);
function mousemove(e) {
const rect = el.getBoundingClientRect();
if (currentResizer.classList.contains("se")) {
el.style.width = rect.width - (prevX - e.clientX) + "px";
el.style.height = rect.height - (prevY - e.clientY) + "px";
} else if (currentResizer.classList.contains("sw")) {
el.style.width = rect.width + (prevX - e.clientX) + "px";
el.style.height = rect.height - (prevY - e.clientY) + "px";
el.style.left = rect.left - (prevX - e.clientX) + "px";
} else if (currentResizer.classList.contains("ne")) {
el.style.width = rect.width - (prevX - e.clientX) + "px";
el.style.height = rect.height + (prevY - e.clientY) + "px";
el.style.top = rect.top - (prevY - e.clientY) + "px";
} else {
el.style.width = rect.width + (prevX - e.clientX) + "px";
el.style.height = rect.height + (prevY - e.clientY) + "px";
el.style.top = rect.top - (prevY - e.clientY) + "px";
el.style.left = rect.left - (prevX - e.clientX) + "px";
}
prevX = e.clientX;
prevY = e.clientY;
}
function mouseup() {
window.removeEventListener("mousemove", mousemove);
window.removeEventListener("mouseup", mouseup);
isResizing = false;
}
}
}
.item {
height: 50px;
width: 50px;
position: absolute;
background: pink;
transform: scale(1.1);
}
.resizer {
position: absolute;
width: 10px;
height: 10px;
border-radius: 5px;
background-color: #333;
z-index: 2;
}
.resizer.nw {
top: -1px;
left: -1px;
cursor: nw-resize;
}
.resizer.ne {
top: -1px;
right: -1px;
cursor: nw-resize;
}
.resizer.sw {
bottom: -1px;
left: -1px;
cursor: sw-resize;
}
.resizer.se {
bottom: -1px;
right: -1px;
cursor: se-resize;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./style.css">
<title>Scaled Div</title>
</head>
<body>
<div class="item">
<div class="resizer ne"></div>
<div class="resizer nw"></div>
<div class="resizer sw"></div>
<div class="resizer se"></div>
</div>
<script src="./main.js"></script>
</body>
</html>
Problem seems to be the mouse-down, conflicting between the x/y-values of the surrounding .item-div and the 4 Resize-Handlers, so the browser doesn't know which are the right resulting values ...
So a solution of this problem may be:
add a CSS border to the .item {...; border:1px solid grey ;}
.item {position:absolute; height:50px; width:50px; border:1px solid grey; background:pink}
and substract these (both-side) borders on the 4 resize-handlers:
...
el.style.width = rect.width +2+ (prevX - e.clientX) + "px"; //adding 2px
el.style.height = rect.height -2- (prevY - e.clientY) + "px"; //substr. 2px
... etc.
...
or instead of these 4 div-handlers using the default browser css-handlers:
.item { ...; resize:both; overflow:hidden}
...My error: "adding" 2px nevertheless should always substract 2px on all sides, so a mouse-down is not conflicting betw. surrounding div and handler-divs:
function mousemove(e) {
const rect = el.getBoundingClientRect(); //"rect" has no border, so add 2px for each side
if (currentResizer.classList.contains("se")) {
el.style.width = rect.width -2- (prevX - e.clientX) + "px"; //2 = border-width
el.style.height = rect.height -2- (prevY - e.clientY) + "px";
} else if (currentResizer.classList.contains("sw")) {
el.style.width = rect.width -2+ (prevX - e.clientX) + "px";
el.style.height = rect.height -2- (prevY - e.clientY) + "px";
el.style.left = rect.left - (prevX - e.clientX) + "px";
} else if (currentResizer.classList.contains("ne")) {
el.style.width = rect.width -2- (prevX - e.clientX) + "px";
el.style.height = rect.height -2+ (prevY - e.clientY) + "px";
el.style.top = rect.top - (prevY - e.clientY) + "px";
} else { //nw
el.style.width = rect.width -2+ (prevX - e.clientX) + "px";
el.style.height = rect.height -2+ (prevY - e.clientY) + "px";
el.style.top = rect.top - (prevY - e.clientY) + "px";
el.style.left = rect.left - (prevX - e.clientX) + "px";
}
Related
I made a text box and boxes on the corners for resizing. The first time I drag, it works perfectly fine, but after the 1st time, the dragging amount seems to multiply. (Don't mind the position of the dragging nodes). If you drag once, it scales fine with the mouse. It seems as if every time after that, when you drag, it scales farther and farther from the user's mouse, causing an 'uncontrollable' scale. I reset the values of the prevX and prevY values at the end of the move function. I'm not sure if that's the issue though. The issue, I believe, is with the scaling with getBoundingClientRect() in the resize portion. Moving the textbox, however, works perfectly fine. Here is my code:
var textBox = document.querySelector(".text-box");
var textArea = document.querySelector('.text-field');
textArea.focused = false;
textBox.addEventListener('mousedown', mousedown);
let isResizing = false;
let isTyping = false;
textArea.addEventListener('dblclick', type)
function type(){
if (!isTyping){
isTyping = true;
textArea.style.cursor = 'text';
}else {
isTyping = false;
textArea.style.cursor = 'default';
}
}
document.body.onclick = function(e){
if (e.target != textArea){
isTyping = false;
e.target.style.cursor = 'default';
}
}
function mousedown(e){
if (e.target == textArea){
e.target.style.cursor = 'default';
}
window.addEventListener('mousemove', mousemove);
window.addEventListener('mouseup', mouseup);
let prevX = e.clientX;
let prevY = e.clientY;
function mousemove(e){
if(!isResizing && !isTyping){
let newX = prevX - e.clientX;
let newY = prevY - e.clientY;
const rect = textBox.getBoundingClientRect();
textBox.style.left = rect.left - newX + "px";
textBox.style.top = rect.top - newY + "px";
prevX = e.clientX;
prevY = e.clientY;
}
}
function mouseup(){
window.removeEventListener('mousemove', mousemove);
window.removeEventListener('mouseup', mouseup);
}
const handles = document.querySelectorAll('.handle');
let currentHandle;
for(let handle of handles){
handle.addEventListener('mousedown', mousedown);
function mousedown(e){
currentHandle = e.target;
isResizing = true;
let prevX = e.clientX;
let prevY = e.clientY;
window.addEventListener('mousemove', mousemove);
window.addEventListener('mouseup', mouseup);
function mousemove(e){
const rect = textBox.getBoundingClientRect();
if (currentHandle.classList.contains("se")) {
textBox.style.width = rect.width - (prevX - e.clientX) + "px";
textBox.style.height = rect.height - (prevY - e.clientY) + "px";
} else if (currentHandle.classList.contains("sw")) {
textBox.style.width = rect.width + (prevX - e.clientX) + "px";
textBox.style.height = rect.height - (prevY - e.clientY) + "px";
textBox.style.left = rect.left - (prevX - e.clientX) + "px";
} else if (currentHandle.classList.contains("ne")) {
textBox.style.width = rect.width - (prevX - e.clientX) + "px";
textBox.style.height = rect.height + (prevY - e.clientY) + "px";
textBox.style.top = rect.top - (prevY - e.clientY) + "px";
} else {
textBox.style.width = rect.width + (prevX - e.clientX) + "px";
textBox.style.height = rect.height + (prevY - e.clientY) + "px";
textBox.style.top = rect.top - (prevY - e.clientY) + "px";
textBox.style.left = rect.left - (prevX - e.clientX) + "px";
}
prevX = e.clientX;
prevY = e.clientY;
}
function mouseup(){
window.removeEventListener('mousemove', mousemove);
window.removeEventListener('mouseup', mouseup);
textArea.focused = false;
isResizing = false;
}
}
}
}
.text-box{
height: 50px;
width: 150px;
position: absolute;
}
.handle{
position: absolute;
width: 5px;
height: 5px;
background-color: blue;
z-index: 2;
}
.ne{
top: -1px;
right: -1px;
cursor: ne-resize;
}
.nw{
top: -1px;
left: -1px;
cursor: nw-resize;
}
.sw{
bottom: -1px;
left: -1px;
cursor: sw-resize;
}
.se{
bottom: -1px;
right: -1px;
cursor: se-resize;
}
.text-field {
width: 100%;
height: 100%;
resize: none;
cursor: default;
}
<div class="text-box">
<textarea class="text-field">Click to add text...</textarea>
<div class="handle ne"></div>
<div class="handle nw"></div>
<div class="handle sw"></div>
<div class="handle se"></div>
</div>
It is just as Chris said on the comment, (However I tried many things before understanding the issue...)
One way very simple to solve it is to take out your for loop from inside your mousedown function.
var textBox = document.querySelector(".text-box");
var textArea = document.querySelector('.text-field');
textArea.focused = false;
textBox.addEventListener('mousedown', mousedown);
let isResizing = false;
let isTyping = false;
textArea.addEventListener('dblclick', type)
function type(){
if (!isTyping){
isTyping = true;
textArea.style.cursor = 'text';
}else {
isTyping = false;
textArea.style.cursor = 'default';
}
}
document.body.onclick = function(e){
if (e.target != textArea){
isTyping = false;
e.target.style.cursor = 'default';
}
}
function mousedown(e){
if (e.target == textArea){
e.target.style.cursor = 'default';
}
window.addEventListener('mousemove', mousemove);
window.addEventListener('mouseup', mouseup);
let prevX = e.clientX;
let prevY = e.clientY;
function mousemove(e){
if(!isResizing && !isTyping){
let newX = prevX - e.clientX;
let newY = prevY - e.clientY;
const rect = textBox.getBoundingClientRect();
textBox.style.left = rect.left - newX + "px";
textBox.style.top = rect.top - newY + "px";
prevX = e.clientX;
prevY = e.clientY;
}
}
function mouseup(){
window.removeEventListener('mousemove', mousemove);
window.removeEventListener('mouseup', mouseup);
}
}
const handles = document.querySelectorAll('.handle');
let currentHandle;
for(let handle of handles){
handle.addEventListener('mousedown', mousedown);
function mousedown(e){
currentHandle = e.target;
isResizing = true;
let prevX = e.clientX;
let prevY = e.clientY;
window.addEventListener('mousemove', mousemove);
window.addEventListener('mouseup', mouseup);
function mousemove(e){
const rect = textBox.getBoundingClientRect();
if (currentHandle.classList.contains("se")) {
textBox.style.width = rect.width - (prevX - e.clientX) + "px";
textBox.style.height = rect.height - (prevY - e.clientY) + "px";
} else if (currentHandle.classList.contains("sw")) {
textBox.style.width = rect.width + (prevX - e.clientX) + "px";
textBox.style.height = rect.height - (prevY - e.clientY) + "px";
textBox.style.left = rect.left - (prevX - e.clientX) + "px";
} else if (currentHandle.classList.contains("ne")) {
textBox.style.width = rect.width - (prevX - e.clientX) + "px";
textBox.style.height = rect.height + (prevY - e.clientY) + "px";
textBox.style.top = rect.top - (prevY - e.clientY) + "px";
} else {
textBox.style.width = rect.width + (prevX - e.clientX) + "px";
textBox.style.height = rect.height + (prevY - e.clientY) + "px";
textBox.style.top = rect.top - (prevY - e.clientY) + "px";
textBox.style.left = rect.left - (prevX - e.clientX) + "px";
}
prevX = e.clientX;
prevY = e.clientY;
}
function mouseup(){
window.removeEventListener('mousemove', mousemove);
window.removeEventListener('mouseup', mouseup);
textArea.focused = false;
isResizing = false;
}
}
}
Just one more thing, I added these lines to your code, to not remove the resize cursor when clicking the handler:
document.body.onclick = function(e){
if (e.target != textArea){
if(e.target.classList.contains('handle')){
isTyping = false
}
else{
isTyping = false;
e.target.style.cursor = 'default';
}
}
}
I have made a code pen example for complete understanding please checkout this link
https://codepen.io/hasan-irshad/pen/bGpPZzB
this is the code which i am using for bottom right corner
if (currentResizer.classList.contains('bottom-right')) {
const width = original_width + (e.pageX - original_mouse_x);
const height = original_height + (e.pageY - original_mouse_y)
if (width > minimum_size) {
element.style.width = width + 'px'
element.style.height = 'max-content';
let Textdiv = document.getElementById('Text');
let relFontsizew = Textdiv.offsetWidth * 0.25;
Textdiv.style.fontSize = relFontsizew + 'px';
}
}
if you resize div from bottom right corner it works fine as I want it to work, from bottom right corner it increases/decreases width of div according to the mouse-X, and height of div according to the inner content height. This is the behavior which I want to implement on all the corners.
for example if I drag from bottom right corner so the top left corner stays at it's position
just like that if I resize it from top right corner so bottom left corner should maintain it's position.
expected behavior
1 width of outer element should depend on mouse-X
2 font size should be dependent on width of outer div
3 height of outer div should depend on inner content height
4 div element should not lose its position
below is a link of video for demonstration of end result which I want to implement.
drive.google.com/file/d/1Wgh3GhEOhv1syk_WFmcCGp0UJUyGzS5u/view
can anybody please help me complete this task?
Thanks a lot in advance!!!!!
I comment the things out you dont need, and inserted the things you should add, it was mainly that you dont need to set the realtive position and you needed to set the height to "max-content", and the only time you add a script to change your font-size was in the right-bottom corner.
let Textdiv = document.getElementById('Text');
let relFontsizew = Textdiv.offsetWidth * 0.25;
Textdiv.style.fontSize = relFontsizew + 'px';
/*Make resizable div by Hung Nguyen*/
function makeResizableDiv(div) {
const element = document.querySelector(div);
const resizers = document.querySelectorAll(div + ' .resizer')
const minimum_size = 20;
let original_width = 0;
let original_height = 0;
let original_x = 0;
let original_y = 0;
let original_mouse_x = 0;
let original_mouse_y = 0;
for (let i = 0;i < resizers.length; i++) {
const currentResizer = resizers[i];
currentResizer.addEventListener('mousedown', function(e) {
e.preventDefault()
original_width = parseFloat(getComputedStyle(element, null).getPropertyValue('width').replace('px', ''));
original_height = parseFloat(getComputedStyle(element, null).getPropertyValue('height').replace('px', ''));
original_x = element.getBoundingClientRect().left;
original_y = element.getBoundingClientRect().top;
original_mouse_x = e.pageX;
original_mouse_y = e.pageY;
window.addEventListener('mousemove', resize)
window.addEventListener('mouseup', stopResize)
})
function resize(e) {
if (currentResizer.classList.contains('bottom-right')) {
const width = original_width + (e.pageX - original_mouse_x);
const height = original_height + (e.pageY - original_mouse_y)
if (width > minimum_size) {
element.style.width = width + 'px'
element.style.height = 'max-content';
let Textdiv = document.getElementById('Text');
let relFontsizew = Textdiv.offsetWidth * 0.25;
Textdiv.style.fontSize = relFontsizew + 'px';
}
}
else if (currentResizer.classList.contains('bottom-left')) {
const height = original_height + (e.pageY - original_mouse_y)
const width = original_width - (e.pageX - original_mouse_x)
if (height > minimum_size) {
element.style.height = "max-content"
}
if (width > minimum_size) {
element.style.width = width + 'px'
element.style.left = original_x + (e.pageX - original_mouse_x) + 'px'
let Textdiv = document.getElementById('Text');
let relFontsizew = Textdiv.offsetWidth * 0.25;
Textdiv.style.fontSize = relFontsizew + 'px';
}
}
else if (currentResizer.classList.contains('top-right')) {
const width = original_width + (e.pageX - original_mouse_x)
const height = original_height - (e.pageY - original_mouse_y)
if (width > minimum_size) {
element.style.width = width + 'px'
element.style.height = "max-content"
//element.style.top = original_y + (e.pageY - original_mouse_y) + 'px'
let Textdiv = document.getElementById('Text');
let relFontsizew = Textdiv.offsetWidth * 0.25;
Textdiv.style.fontSize = relFontsizew + 'px';
}
}
else {
const width = original_width - (e.pageX - original_mouse_x)
const height = original_height - (e.pageY - original_mouse_y)
if (width > minimum_size) {
element.style.width = width + 'px'
element.style.left = original_x + (e.pageX - original_mouse_x) + 'px'
}
if (height > minimum_size) {
element.style.height = "max-content"
//element.style.top = original_y + (e.pageY - original_mouse_y) + 'px'
}
let Textdiv = document.getElementById('Text');
let relFontsizew = Textdiv.offsetWidth * 0.25;
Textdiv.style.fontSize = relFontsizew + 'px';
}
}
function stopResize() {
window.removeEventListener('mousemove', resize)
}
}
}
makeResizableDiv('.resizable')
body,
html {
background: black;
}
.resizable {
background: white;
width: 100px;
height: 100px;
position: absolute;
top: 100px;
left: 100px;
}
.resizable .resizers{
width: 100%;
height: 100%;
border: 3px solid #4286f4;
box-sizing: border-box;
}
.resizable .resizers .resizer{
width: 10px;
height: 10px;
border-radius: 50%; /*magic to turn square into circle*/
background: white;
border: 3px solid #4286f4;
position: absolute;
}
.resizable .resizers .resizer.top-left {
left: -5px;
top: -5px;
cursor: nwse-resize; /*resizer cursor*/
}
.resizable .resizers .resizer.top-right {
right: -5px;
top: -5px;
cursor: nesw-resize;
}
.resizable .resizers .resizer.bottom-left {
left: -5px;
bottom: -5px;
cursor: nesw-resize;
}
.resizable .resizers .resizer.bottom-right {
right: -5px;
bottom: -5px;
cursor: nwse-resize;
}
<div class='resizable'>
<div class='resizers'>
<div class='resizer top-left'></div>
<div class='resizer top-right'></div>
<div class='resizer bottom-left'></div>
<div class='resizer bottom-right'></div>
<article id="Text" style=" width:100%; font-size:22px; color:black">Any Text</article>
</div>
</div>
I have a resizable DIV that is resizable in the 4 sides. How ever, in some cases the content of this DIV may exceed its size. So I've added the option overflow: auto, but when I use this option, the component responsible to resize the DIV moves with the scroll.
Here's the code:
https://codesandbox.io/s/23kt1?file=/index.html
How can I fix the component always on the edges of the DIV in such a way that it's not moved when the user scroll the div?
You need an extra container for the dots to align to.
function resizeable() {
var resizers = document.querySelectorAll('.n, .s, .w, .e, .nw, .ne, .se, .sw');
const min = 40;
for (let i = 0; i < resizers.length; i++) {
const currentResizer = resizers[i];
const element = currentResizer.parentElement;
const parent = currentResizer.parentElement.parentElement;
let p;
let c;
let original_w = 0;
let original_h = 0;
let parent_x = 0;
let parent_y = 0;
let parent_sx = 0;
let parent_sy = 0;
let child_x = 0;
let child_y = 0;
let mouse_x = 0;
let mouse_y = 0;
let scale_x = 0;
let scale_y = 0;
let scroll_x = 0;
let scroll_y = 0;
// Mouse events
currentResizer.addEventListener('mousedown', function(e) {
first(e);
document.addEventListener('mousemove', resize);
document.addEventListener('mouseup', stopResize);
e.preventDefault();
});
// First location & width
function first(e) {
c = element.getBoundingClientRect();
child_y = c.top;
child_x = c.left;
p = parent.getBoundingClientRect();
parent_y = p.top;
parent_x = p.left;
parent_sy = parent.scrollTop;
parent_sx = parent.scrollLeft;
scroll_y = window.scrollY;
scroll_x = window.scrollX;
original_w = parseFloat(c.width).toFixed(2);
original_h = parseFloat(c.height).toFixed(2);
scale_y = parseFloat(c.height / element.offsetHeight).toFixed(2);
scale_x = parseFloat(c.width / element.offsetWidth).toFixed(2);
mouse_y = e.pageY - scroll_y;
mouse_x = e.pageX - scroll_x;
}
// Resize process
function resize(e) {
element.style.position = "absolute";
if (currentResizer.classList.contains('se')) {
const width = e.pageX - scroll_x - child_x;
const height = e.pageY - scroll_y - child_y;
if (width > min) {
element.style.width = (width / scale_x) + 'px';
}
if (height > min) {
element.style.height = (height / scale_y) + 'px';
}
if (parent.scrollTop < parent.scrollHeight) {
parent.scrollTop = parent.scrollHeight;
}
if (parent.scrollLeft < parent.scrollWidth) {
parent.scrollLeft = parent.scrollWidth;
}
} else if (currentResizer.classList.contains('sw')) {
const width = original_w - (e.pageX - scroll_x - child_x);
const height = e.pageY - scroll_y - child_y;
if (height > min) {
element.style.height = (height / scale_y) + 'px';
}
if (width > min) {
element.style.left = e.pageX - scroll_x - parent_x + parent_sx + 'px';
element.style.width = (width / scale_x) + 'px';
}
if (parent.scrollTop < parent.scrollHeight) {
parent.scrollTop = parent.scrollHeight;
}
} else if (currentResizer.classList.contains('ne')) {
const width = e.pageX - child_x - scroll_x;
const height = original_h - (e.pageY - mouse_y - scroll_y);
if (width > min) {
element.style.width = (width / scale_x) + 'px';
}
if (height > min) {
element.style.height = (height / scale_y) + 'px';
element.style.top = e.pageY - parent_y - scroll_y + parent_sy + 'px';
}
if (parent.scrollLeft < parent.scrollWidth) {
parent.scrollLeft = parent.scrollWidth;
}
} else if (currentResizer.classList.contains('nw')) {
const width = original_w - (e.pageX - scroll_x - child_x);
const height = original_h - (e.pageY - scroll_y - mouse_y);
if (width > min) {
element.style.left = e.pageX - parent_x - scroll_x + parent_sx + 'px';
element.style.width = (width / scale_x) + 'px';
}
if (height > min) {
element.style.height = (height / scale_y) + 'px';
element.style.top = e.pageY - parent_y - scroll_y + parent_sy + 'px';
}
} else if (currentResizer.classList.contains('e')) {
const width = e.pageX - scroll_x - child_x;
if (width > min) {
element.style.width = (width / scale_x) + 'px';
}
if (parent.scrollLeft < parent.scrollWidth) {
parent.scrollLeft = parent.scrollWidth;
}
} else if (currentResizer.classList.contains('s')) {
const height = e.pageY - scroll_y - child_y;
if (height > min) {
element.style.height = (height / scale_y) + 'px';
}
if (parent.scrollTop < parent.scrollHeight) {
parent.scrollTop = parent.scrollHeight;
}
} else if (currentResizer.classList.contains('w')) {
const width = original_w - (e.pageX - scroll_x - child_x);
if (width > min) {
element.style.width = (width / scale_x) + 'px';
element.style.left = (e.pageX - scroll_x - parent_x + parent_sx) + 'px';
}
} else if (currentResizer.classList.contains('n')) {
const height = original_h - (e.pageY - scroll_y - mouse_y);
if (height > min) {
element.style.height = (height / scale_y) + 'px';
element.style.top = e.pageY - parent_y - scroll_y + parent_sy + 'px';
}
}
}
// When mouse released stop
function stopResize(e) {
first(e);
document.removeEventListener('mousemove', resize);
}
}
}
resizeable();
body {
width: 1200px;
}
div {
position: absolute;
background-color: grey;
}
.regular {
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: red;
overflow: auto;
position: absolute;
}
.n,
.s,
.w,
.e,
.nw,
.ne,
.se,
.sw {
position: absolute;
width: 18px;
height: 18px;
border: 1px solid grey;
border-radius: 20px;
background-color: #fff;
z-index: 1;
}
.n:hover,
.s:hover,
.w:hover,
.e:hover,
.nw:hover,
.ne:hover,
.se:hover,
.sw:hover {
background-color: red;
}
.nw {
top: -10px;
left: -10px;
cursor: nw-resize;
}
.ne {
top: -10px;
left: calc(100% - 10px);
cursor: ne-resize;
}
.sw {
top: calc(100% - 10px);
left: -10px;
cursor: sw-resize;
}
.se {
top: calc(100% - 10px);
left: calc(100% - 10px);
cursor: se-resize;
}
.n {
top: -10px;
left: calc(50% - 10px);
cursor: n-resize;
}
.w {
top: calc(50% - 10px);
left: -10px;
cursor: w-resize;
}
.e {
top: calc(50% - 10px);
left: calc(100% - 10px);
cursor: e-resize;
}
.s {
top: calc(100% - 10px);
left: calc(50% - 10px);
cursor: s-resize;
}
.container {
position: relative;
width: 300px;
height: 300px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="/src/styles.css" />
</head>
<body>
<div class="container">
<div class="nw"></div>
<div class="ne"></div>
<div class="sw"></div>
<div class="se"></div>
<div class="n"></div>
<div class="s"></div>
<div class="w"></div>
<div class="e"></div>
<div class="regular">
<p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
<p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
<p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
<p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
<p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
<p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
<p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
<p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
<p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
<p>SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS</p>
</div>
</div>
<script src="/src/index.js"></script>
</body>
</html>
In my project user can create as many objects as he wants and resize them. But there is some bug that causes resizing go super fast. When user creates one object, resize it, then create second object and resize it too, after resizing second object come back to first and resizing this first object is faster than mouse movement speed.
//create
function create() {
const workArea = document.querySelector('.work-area');
let divParent = document.createElement("div");
divParent.className = "divParent";
let div = document.createElement("div");
div.className = "div";
divParent.appendChild(div);
workArea.appendChild(divParent);
var resizerNE = document.createElement("div");
resizerNE.className = "resizerNE";
resizerNE.classList.add("resizer");
var resizerSE = document.createElement("div");
resizerSE.className = "resizerSE";
resizerSE.classList.add("resizer");
var resizerSW = document.createElement("div");
resizerSW.className = "resizerSW";
resizerSW.classList.add("resizer");
var resizerNW = document.createElement("div");
resizerNW.className = "resizerNW";
resizerNW.classList.add("resizer");
divParent.appendChild(resizerNE);
divParent.appendChild(resizerSE);
divParent.appendChild(resizerSW);
divParent.appendChild(resizerNW);
//resize
let el;
const resizer = document.querySelectorAll(".resizer");
resizer.forEach(function(thisresizer) {
thisresizer.addEventListener("mousedown", function() {
el = this.parentNode;
});
});
let isResizing = false;
const resizers = document.querySelectorAll(".resizer");
let currentResizer;
for (let resizer of resizers) {
resizer.addEventListener("mousedown", mousedown);
function mousedown(e) {
currentResizer = e.target;
isResizing = true;
//console.log(el);
let prevX = e.clientX;
let prevY = e.clientY;
window.addEventListener("mousemove", mousemove);
window.addEventListener("mouseup", mouseup);
function mousemove(e) {
const rect = el.getBoundingClientRect();
if (currentResizer.classList.contains("resizerSE")) {
el.style.width = rect.width - (prevX - e.clientX) + "px";
el.style.height = rect.height - (prevY - e.clientY) + "px";
} else if (currentResizer.classList.contains("resizerSW")) {
el.style.width = rect.width + (prevX - e.clientX) + "px";
el.style.height = rect.height - (prevY - e.clientY) + "px";
el.style.left = rect.left - (prevX - e.clientX) + "px";
} else if (currentResizer.classList.contains("resizerNE")) {
el.style.width = rect.width - (prevX - e.clientX) + "px";
el.style.height = rect.height + (prevY - e.clientY) + "px";
el.style.top = rect.top - (prevY - e.clientY) + "px";
} else if (currentResizer.classList.contains("resizerNW")) {
el.style.width = rect.width + (prevX - e.clientX) + "px";
el.style.height = rect.height + (prevY - e.clientY) + "px";
el.style.top = rect.top - (prevY - e.clientY) + "px";
el.style.left = rect.left - (prevX - e.clientX) + "px";
} else if (currentResizer.classList.contains("resizerN")) {
el.style.height = rect.height + (prevY - e.clientY) + "px";
el.style.top = rect.top - (prevY - e.clientY) + "px";
} else if (currentResizer.classList.contains("resizerE")) {
el.style.width = rect.width - (prevX - e.clientX) + "px";
} else if (currentResizer.classList.contains("resizerS")) {
el.style.height = rect.height - (prevY - e.clientY) + "px";
} else if (currentResizer.classList.contains("resizerW")) {
el.style.width = rect.width + (prevX - e.clientX) + "px";
el.style.left = rect.left - (prevX - e.clientX) + "px";
}
el.children[0].style.width = el.style.width;
el.children[0].style.height = el.style.height;
//console.log("prevX: " + prevX + " prevY: " + prevY);
prevX = e.clientX;
prevY = e.clientY;
}
function mouseup() {
window.removeEventListener("mousemove", mousemove);
window.removeEventListener("mouseup", mouseup);
isResizing = false;
}
}
}
}
.work-area{
width: 100%;
height: 100vh;
}
.divParent{
width: 100px;
height: 100px;
position: absolute;
background:black
}
.div{
background: #555555;
}
.resizer {
width: 12px;
height: 12px;
position: absolute;
z-index: 99999999;
border: 1px solid #2872c7;
border-radius: 10px;
background: #f2f2f2;
box-sizing: border-box;
display: block;
}
.resizerSE {
right: -6px;
bottom: -6px;
cursor: se-resize;
}
.resizerNE {
top: -6px;
right: -6px;
cursor: ne-resize;
}
.resizerSW {
left: -6px;
bottom: -6px;
cursor: sw-resize;
}
.resizerNW {
left: -6px;
top: -6px;
cursor: nw-resize;
}
<button onclick="create()">Create</button>
<div class="work-area">
</div>
finally 😌
i spend more than hour to find a way to overcome the problem🤣
Which is Every Time you create objects you add listeners to the previous resizers that's why every new object makes resizing the previous ones faster
in other words the browser moves more steps per mousemove event
only change is
const resizers = document.querySelectorAll(".resizer");
to
const resizers = divParent.querySelectorAll(".resizer");
also in my answer i rearranged your code making it simpler to read & smaller
//create
function create() {
const workArea = document.querySelector(".work-area");
let divParent = document.createElement("div");
divParent.className = "divParent";
workArea.appendChild(divParent);
NewDiv = cls => {
let elm = document.createElement("div");
elm.className = cls;
divParent.appendChild(elm);
};
let div = NewDiv("div");
let resizerNE = NewDiv("resizerNE resizer");
let resizerSE = NewDiv("resizerSE resizer");
let resizerSW = NewDiv("resizerSW resizer");
let resizerNW = NewDiv("resizerNW resizer");
//resize
let el;
let isResizing = false;
const resizers = divParent.querySelectorAll(".resizer");
let currentResizer;
let prevX;
let prevY;
function mousedown(e) {
el = this.parentNode;
currentResizer = e.target;
isResizing = true;
prevX = e.clientX;
prevY = e.clientY;
workArea.addEventListener("mousemove", mousemove);
workArea.addEventListener("mouseup", mouseup);
}
for (let resizer of resizers) {
resizer.addEventListener("mousedown", mousedown);
}
function mousemove(e) {
if (isResizing) {
const rect = el.getBoundingClientRect();
if (currentResizer.classList.contains("resizerSE")) {
el.style.width = rect.width - (prevX - e.clientX) + "px";
el.style.height = rect.height - (prevY - e.clientY) + "px";
} else if (currentResizer.classList.contains("resizerSW")) {
el.style.width = rect.width + (prevX - e.clientX) + "px";
el.style.height = rect.height - (prevY - e.clientY) + "px";
el.style.left = rect.left - (prevX - e.clientX) + "px";
} else if (currentResizer.classList.contains("resizerNE")) {
el.style.width = rect.width - (prevX - e.clientX) + "px";
el.style.height = rect.height + (prevY - e.clientY) + "px";
el.style.top = rect.top - (prevY - e.clientY) + "px";
} else if (currentResizer.classList.contains("resizerNW")) {
el.style.width = rect.width + (prevX - e.clientX) + "px";
el.style.height = rect.height + (prevY - e.clientY) + "px";
el.style.top = rect.top - (prevY - e.clientY) + "px";
el.style.left = rect.left - (prevX - e.clientX) + "px";
} else if (currentResizer.classList.contains("resizerN")) {
el.style.height = rect.height + (prevY - e.clientY) + "px";
el.style.top = rect.top - (prevY - e.clientY) + "px";
} else if (currentResizer.classList.contains("resizerE")) {
el.style.width = rect.width - (prevX - e.clientX) + "px";
} else if (currentResizer.classList.contains("resizerS")) {
el.style.height = rect.height - (prevY - e.clientY) + "px";
} else if (currentResizer.classList.contains("resizerW")) {
el.style.width = rect.width + (prevX - e.clientX) + "px";
el.style.left = rect.left - (prevX - e.clientX) + "px";
}
el.children[0].style.width = el.style.width;
el.children[0].style.height = el.style.height;
//console.log("prevX: " + prevX + " prevY: " + prevY);
prevX = e.clientX;
prevY = e.clientY;
}
}
function mouseup() {
isResizing = false;
workArea.removeEventListener("mouseup", mouseup);
workArea.removeEventListener("mousemove", mousemove);
}
}
.work-area {
width: 100%;
height: 100vh;
}
.divParent {
width: 100px;
height: 100px;
position: absolute;
background: black;
}
.div {
background: #555555;
}
.resizer {
width: 12px;
height: 12px;
position: absolute;
z-index: 99999999;
border: 1px solid #2872c7;
border-radius: 10px;
background: #f2f2f2;
box-sizing: border-box;
display: block;
}
.resizerSE {
right: -6px;
bottom: -6px;
cursor: se-resize;
}
.resizerNE {
top: -6px;
right: -6px;
cursor: ne-resize;
}
.resizerSW {
left: -6px;
bottom: -6px;
cursor: sw-resize;
}
.resizerNW {
left: -6px;
top: -6px;
cursor: nw-resize;
}
<body>
<button onclick="create()">Create</button>
<div class="work-area"></div>
</body>
There are so many examples to draw rectangles with mouse on canvas. For example, check this jsFiddle. Is it possible to draw parallelogram?
initDraw(document.getElementById('canvas'));
function initDraw(canvas) {
function setMousePosition(e) {
var ev = e || window.event; //Moz || IE
if (ev.pageX) { //Moz
mouse.x = ev.pageX + window.pageXOffset;
mouse.y = ev.pageY + window.pageYOffset;
} else if (ev.clientX) { //IE
mouse.x = ev.clientX + document.body.scrollLeft;
mouse.y = ev.clientY + document.body.scrollTop;
}
};
var mouse = {
x: 0,
y: 0,
startX: 0,
startY: 0
};
var element = null;
canvas.onmousemove = function(e) {
setMousePosition(e);
if (element !== null) {
element.style.width = Math.abs(mouse.x - mouse.startX) + 'px';
element.style.height = Math.abs(mouse.y - mouse.startY) + 'px';
element.style.left = (mouse.x - mouse.startX < 0) ? mouse.x + 'px' : mouse.startX + 'px';
element.style.top = (mouse.y - mouse.startY < 0) ? mouse.y + 'px' : mouse.startY + 'px';
}
}
canvas.onclick = function(e) {
if (element !== null) {
element = null;
canvas.style.cursor = "default";
console.log("finsihed.");
} else {
console.log("begun.");
mouse.startX = mouse.x;
mouse.startY = mouse.y;
element = document.createElement('div');
element.className = 'rectangle'
element.style.left = mouse.x + 'px';
element.style.top = mouse.y + 'px';
canvas.appendChild(element)
canvas.style.cursor = "crosshair";
}
}
}
#canvas {
width: 2000px;
height: 2000px;
border: 10px solid transparent;
}
.rectangle {
border: 1px solid #FF0000;
position: absolute;
}
<div id="canvas"></div>
I think this question is self explanatory.