【问题标题】:How to make a draggable div resize-able on all 4 corners with pure javascript?如何使用纯 JavaScript 在所有 4 个角上调整可拖动 div 的大小?
【发布时间】:2021-02-16 20:00:51
【问题描述】:

我在 css 规范中注意到了这些调整大小的指针...

https://drafts.csswg.org/css-ui-3/#valdef-cursor-se-resize

是否有类似于一个角('resize: both')方法的 4 个角可调整大小的 CSS 快捷方式? 如果没有,将可调整大小与可拖动 div 组合时是否存在已知冲突?

我的出发点在这里......

https://www.w3schools.com/howto/howto_js_draggable.asp

感谢导航 posX, posY 的任何帮助。

getBoundingClient() 的注意事项 —————— | | |____ | div.getBoundingClientRect()

SE(右下): 高度和宽度/顶部和左侧是固定的

SW(左下): 高度和宽度以及左/上是固定的

西北(左上): 上下左右高度和宽度

NE(右上角): 高度和宽度以及顶部/左侧是固定的

编辑:删除填充和边框。

const myDiv = document.getElementById('mydiv')
let isResizing = false;
//Make the DIV element draggable:
dragElement(myDiv);
   

function dragElement(elmnt) {
  if (!isResizing) {
  let pos1 = 0,
    pos2 = 0,
    pos3 = 0,
    pos4 = 0;
  if (document.getElementById(elmnt.id + "header")) {
    //if present, the header is where you move the DIV from:
    document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
  } else {
    //otherwise, move the DIV from anywhere inside the DIV:
    elmnt.onmousedown = dragMouseDown;
  }

  function dragMouseDown(e) {
    
      e = e || window.event;
      e.preventDefault();
      // get the mouse cursor position at startup:
      pos3 = e.clientX;
      pos4 = e.clientY;
      document.onmouseup = closeDragElement;
      // call a function whenever the cursor moves:
      document.onmousemove = elementDrag;
    }
  

  function elementDrag(e) {
    
      e = e || window.event;
      e.preventDefault();
      // calculate the new cursor position:
      pos1 = pos3 - e.clientX;
      pos2 = pos4 - e.clientY;
      pos3 = e.clientX;
      pos4 = e.clientY;
      // set the element's new position:
      elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
      elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
    }
  
  function closeDragElement() {
    //stop moving when mouse button is released:
    document.onmouseup = null;
    document.onmousemove = null;
  }
 }
}
// Resize
(function fourCorners() {

const resizers = document.querySelectorAll('.resizer')
let currentResizer

for (let resizer of resizers) {
  resizer.addEventListener('mousedown', mouseDown)

  function mouseDown(e) {
    currentResizer = e.target
    e.preventDefault()
    isResizing = true;

    let posX = e.clientX;
    let posY = e.clientY;
    myDiv.addEventListener('mousemove', mouseMove)
    myDiv.addEventListener('mouseup', mouseUp)

    function mouseMove(e) {
       e.preventDefault()
      const rect = myDiv.getBoundingClientRect()
      if (currentResizer.classList.contains('se')) {
        //console.log(currentResizer.classList.value)

        myDiv.style.width = rect.width - (posX - e.clientX) + 'px';
        myDiv.style.height = rect.height - (posY - e.clientY) + 'px';
      } else if (currentResizer.classList.contains('sw')) {
        //console.log(currentResizer.classList.value)

        myDiv.style.width = rect.width + (posX - e.clientX) + 'px';
        myDiv.style.height = rect.height - (posY - e.clientY) + 'px';
        myDiv.style.left = rect.left - (posX - e.clientX) + 'px';

      } else if (currentResizer.classList.contains('ne')) {
        //console.log(currentResizer.classList.value)

        myDiv.style.width = rect.width - (posX - e.clientX) + 'px';
        myDiv.style.height = rect.height + (posY - e.clientY) + 'px';
        myDiv.style.top = rect.top - (posY - e.clientY) + 'px';

      } else {
        //console.log(currentResizer.classList.value)

        myDiv.style.width = rect.width + (posX - e.clientX) + 'px';
        myDiv.style.height = rect.height + (posY - e.clientY) + 'px';
        myDiv.style.top = rect.top - (posY - e.clientY) + 'px';
        myDiv.style.left = rect.left - (posX - e.clientX) + 'px';

      }
      posX = e.clientX;
      posY = e.clientY;
    }

    function mouseUp(e) {
      myDiv.removeEventListener('mousemove', mouseMove)
      myDiv.removeEventListener('mouseup', mouseUp)
      isResizing = false
    }
  }
}
})()
* {
  margin: 0;
  padding : 0;
}

#mydiv {
  position: absolute; /* NECESSARY */
  background-color: whitesmoke;
  box-sizing: border-box;
  text-align: center;
  /* border: 1px solid #222; */ 
  height: 200px;
  width: 200px;
  /* resize: both; /* CSS RESIZE */
  overflow: hidden; /* CSS RESIZE */
}

#mydivheader {
  /* padding: 10px; */
  cursor: move;
  background-color: dodgerblue;
  color: #fff;
}

#content {
  color: #000;
  margin: 0px;
  background-color: whitesmoke;
}


/* ::-webkit-resizer {
  position: absolute;
  height: 20px;
  width: 20px;
  border-top-left-radius: 25px;
  background-color: #dd0;
  z-index: 2;
} */

.resizer {
  position: absolute;
  height: 20px;
  width: 20px;
  background-color: #dd0;
  z-index: 2;
}

.resizer.nw {
  top: -1px;
  left: -1px;
  cursor: nw-resize;
  border-bottom-right-radius: 25px;
}

.resizer.ne {
  top: -1px;
  right: -1px;
  cursor: ne-resize;
  border-bottom-left-radius: 25px;
}

.resizer.sw {
  bottom: -1px;
  left: -1px;
  cursor: sw-resize;
  border-top-right-radius: 25px;
}

.resizer.se {
  bottom: -1px;
  right: -1px;
  cursor: se-resize;
  border-top-left-radius: 25px;
}
<div id="mydiv">
  <div class='resizer nw'></div>
  <div class='resizer ne'></div>
  <div class='resizer sw'></div>
  <div class='resizer se'></div>

  <div id="mydivheader">Click here to move

    <div id='content'>

      <div id='image-container'><img height='auto' width='100%' src='https://picsum.photos/600' /></div>

    </div>
  </div>
</div>

【问题讨论】:

  • 我用当前进度更新问题。
  • 我明白你所说的 posX, posY 问题是什么意思。 ???一项“改进”是去掉我可以看到的滚动条:您可以将规则 #mydiv overflow: auto; 更改为 overflow: hidden;
  • 我喜欢你正在尝试做的事情,但此时我太忙了,无法真正仔细看看这个。它相当复杂。也许等我不那么忙并且没有其他人看过的时候再看看。
  • 它越来越近了,但我从 mouseup 移除事件侦听器未触发并且框继续调整大小时得到奇怪的行为。

标签: javascript css


【解决方案1】:

如果你想让它变得漂亮和简单,你可以使用 jQuery 来帮助它调整大小可能是最简单的方法。然后在你学会了如何用 jQuery 来做之后,你就可以用纯 js 来做。

【讨论】:

  • 我已经学习了 4 个月的 JavaScript 并且非常喜欢它。在进入图书馆之前,我想有一个更扎实的理解。这是一个练习。
  • 哦,很抱歉,我刚开始时也是这样做的。
  • 我几乎有这个工作......它不是很强大。事件监听器并不总是在鼠标抬起时移除,事情变得丑陋
  • 您可以尝试设置一个每毫秒检查一次的循环,以确保在鼠标抬起时移除事件侦听器。
【解决方案2】:

那么,您希望调整大小手柄出现在所有四个角上吗?这需要一些边界检查。

您可以修改drag 中的偏移检查以考虑任何角并将拖动作为调整大小事件处理。该代码在某种程度上适用于左上角和右下角的大小调整,但对对角效果不佳。这是一个开始。

这是一个开始:

const cursor = document.querySelector('#cursor');
const cornerThreshold = 4;

const dragStart = e => {
  const [ horz, vert ] = getDirection(e, cornerThreshold);
  const bounds = e.target.getBoundingClientRect();
  const cursor = getCursorType(e);
  
  if (cursor === 'grab') {
    e.target.dataset.isDragging = true;
  } else {
    e.target.dataset.isResizing = true;
  }

  e.target.dataset.startWidth = bounds.width;
  e.target.dataset.startHeight = bounds.height;
  e.target.dataset.originX = e.clientX;
  e.target.dataset.originY = e.clientY;
  e.target.dataset.offsetX = e.clientX - e.target.offsetLeft;
  e.target.dataset.offsetY = e.clientY - e.target.offsetTop;
  e.target.dataset.dirHorz = horz;
  e.target.dataset.dirVert = vert;
  e.target.style.zIndex = 999;
};

const dragEnd = e => {
  delete e.target.dataset.isDragging;
  delete e.target.dataset.offsetX;
  delete e.target.dataset.offsetY;
  delete e.target.dataset.originX;
  delete e.target.dataset.originY;
  delete e.target.dataset.startWidth;
  delete e.target.dataset.startHeight;
  delete e.target.dataset.dirHorz;
  delete e.target.dataset.dirVert;
  delete e.target.dataset.resizeDirection;
  
  e.target.style.removeProperty('z-index');
  e.target.style.removeProperty('cursor');
};

const drag = e => {
  e.target.style.cursor = getCursorType(e);
  cursor.textContent = `(${e.clientX}, ${e.clientY})`;
  if (e.target.dataset.isDragging) {
    e.target.style.left = `${e.clientX - parseInt(e.target.dataset.offsetX, 10)}px`;
    e.target.style.top = `${e.clientY - parseInt(e.target.dataset.offsetY, 10)}px`;
  } else if (e.target.dataset.isResizing) {
    const bounds = e.target.getBoundingClientRect();
    
    const startWidth = parseInt(e.target.dataset.startWidth, 10);
    const startHeight = parseInt(e.target.dataset.startWidth, 10);
    const deltaX = e.clientX - parseInt(e.target.dataset.originX, 10);
    const deltaY = e.clientY - parseInt(e.target.dataset.originY, 10);
    const originX = parseInt(e.target.dataset.originX, 10);
    const originY = parseInt(e.target.dataset.originY, 10);
    const dirHorz = parseInt(e.target.dataset.dirHorz, 10);
    const dirVert = parseInt(e.target.dataset.dirVert, 10);
    
    if (dirHorz < 0) {
      e.target.style.left = `${originX + deltaX}px`;
      e.target.style.width = `${startWidth - deltaX}px`
    } else if (dirHorz > 0) {
      e.target.style.width = `${startWidth + deltaX}px`;
    }
    if (dirVert < 0) {
      e.target.style.top = `${originY + deltaY}px`;
      e.target.style.height = `${startHeight - deltaY}px`;
    } else if (dirVert > 0) {
      e.target.style.height = `${startHeight + deltaY}px`;
    }
  }
};

const focus = e => { };

const unfocus = e => { e.target.style.removeProperty('cursor'); };

const getDirection = (e, threshold) => {
  const bounds = e.target.getBoundingClientRect();
  const offsetX = e.clientX - e.target.offsetLeft;
  const offsetY = e.clientY - e.target.offsetTop;

  const isTop = offsetY <= threshold;
  const isLeft = offsetX <= threshold;
  const isBottom = offsetY > (bounds.height - threshold);
  const isRight = offsetX > (bounds.width - threshold);
  
  if      (isTop    && isLeft)  return [ -1, -1 ];
  else if (isTop    && isRight) return [ -1,  1 ];
  else if (isBottom && isLeft)  return [  1, -1 ];
  else if (isBottom && isRight) return [  1,  1 ];
  else                          return [  0,  0 ];
};

const getCursorType = (e) => {
  if (e.target.dataset.isDragging) {
    return 'grabbing';
  } else {
    const [ horz, vert ] = getDirection(e, cornerThreshold);

    const isTop = vert === -1;
    const isLeft = horz === -1;
    const isBottom = vert === 1;
    const isRight = horz === 1;
  
    if ((isTop && isLeft) || (isBottom && isRight)) return 'nwse-resize';
    if ((isTop && isRight) || (isBottom && isLeft)) return 'nesw-resize';
  }
  return 'grab';
};

document.querySelectorAll('.draggable').forEach(draggable => {
 draggable.addEventListener('mousedown', dragStart);
 draggable.addEventListener('mouseup', dragEnd);
 draggable.addEventListener('mousemove', drag);
 draggable.addEventListener('mouseenter', focus);
 draggable.addEventListener('mouseleave', unfocus);
});
html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}

.container {
  position: relative;
  width: 60%;
  height: 60%;
  border: thin solid grey;
}

.square {
  position: absolute;
  height: 2em;
  width: 2em;
}

.color-red { background: red; }
.color-blue { background: blue; }
.color-green { background: green; }

#square-1 { top:  10px; left:  10px; }
#square-2 { top:  50px; left:  50px; }
#square-3 { top: 100px; left: 100px; }
<div class="container">
  <div id="square-1" class="square color-red draggable resizable"></div>
  <div id="square-2" class="square color-blue draggable resizable"></div>
  <div id="square-3" class="square color-green draggable resizable"></div>
</div>
<br />
<div>Cursor: <span id="cursor">(0, 0)</span></div>

【讨论】:

  • 拖动功能本身就可以正常工作,我将调整大小视为单独的“插件”功能。但我喜欢你将两者结合起来以备将来尝试的想法。关于调整大小,您对 e.preventDefault() 了解多少?
  • 我发现我的问题或其中一个问题源于添加的边框和填充属性。它们似乎呈指数增长。 (偏移)
猜你喜欢
  • 2014-10-30
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
  • 2014-01-16
  • 2016-02-02
  • 2014-11-05
  • 1970-01-01
相关资源
最近更新 更多