웹 사이트에서 드래그 및 크기 조정이 가능한 창

데스크톱 OS에 있는 창처럼 보이고 느껴지는 드래그 가능하고 크기 조정 가능한 창을 만드세요.
전체 코드는 아래에 있습니다. 이제 HTML을 간단히 살펴보고 JS 부분을 설명하겠습니다.

HTML



<div class="window">
  <div class="resizer corner tl"></div>
  <div class="resizer corner tr"></div>
  <div class="resizer corner bl"></div>
  <div class="resizer corner br"></div>
  <div class="resizer t"></div>
  <div class="resizer b"></div>
  <div class="resizer l"></div>
  <div class="resizer r"></div>

  <div class="body">
    <div class="topbar">
      <div class="btns">
        <div></div>
        <div></div>
        <div></div>
      </div>
    </div>

    <!-- your content here -->
  </div>
</div>

여기 모퉁이에 4개의 리사이저가 있고 창의 측면에 4개가 있습니다. 창의 상단 표시줄을 잡고 드래그할 수 있습니다.

JS



창과 상단 표시줄의 변수 선택자를 선언해 봅시다.

const xwindow = document.querySelector(".window")
const topbar = document.querySelector(".topbar")


질질 끄는



마우스 버튼을 누른 상태에서 창을 드래그할 수 있습니다. mousemove 수신기는 mousedown가 트리거될 때만 추가됩니다. 따라서 마우스 버튼을 놓을 때 mouseup를 제거하려면 mousemove도 필요합니다.
이제 mousedown 수신기를 상단 표시줄에 추가하고 mousedown() 함수를 생성해 보겠습니다.

//topbar can be whatever you want to hold while dragging
topbar.addEventListener("mousedown", mousedown)

function mousedown(){
  window.addEventListener("mousemove", mousemove)
  window.addEventListener("mouseup", mouseup)
  function mousemove(){}
  function mouseup(){
    window.removeEventListener("mousemove", mousemove)
    window.removeEventListener("mouseup", mouseup)
  }
}


자, 이제 JS가 커서의 위치를 ​​추적하고 커서를 움직일 때 창을 이동하기를 원합니다. 그렇게 하려면 e (또는 event )가 필요합니다. 다른 것 외에도 커서 위치에 대한 정보를 저장합니다. 함수 인수로 전달합시다.

function mousedown(e){
  ...
  function mousemove(e){}
  ...
}


창의 위치를 ​​변경하기 위해 커서의 새 위치와 이전 위치의 차이를 추가하고 이 차이를 창의 왼쪽 및 위쪽 값에 추가할 수 있습니다(창은 절대 위치 지정됨). 커서 좌표의 이전 값을 가져옵니다. 커서를 이동하기 전에 이러한 값은 현재 값일 뿐입니다.

function mousedown(e){
  ...
  let prevX = e.clientX
  let prevY = e.clientY
  function mousemove(e){}
  ...
}


이제 새 위치와 이전 위치의 차이점을 찾아보겠습니다.

...
let prevX = e.clientX
let prevY = e.clientY
function mousemove(e){
  let newX = e.clientX - prevX
  let newY = e.clientY - prevY
}
...


JavaScript 메소드getBoundingClientRect()는 요소의 크기 및 뷰포트에 상대적인 위치에 대한 정보를 제공하는 DOMRect 객체를 반환합니다. 이를 통해 top 및 left 속성의 값을 가져오고 새 값을 설정할 수 있습니다.

function mousemove(e){
  let newX = e.clientX - prevX
  let newY = e.clientY - prevY
  const rect = xwindow.getBoundingClientRect()
  xwindow.style.left = rect.left + newX + "px"
  xwindow.style.top = rect.top + newY + "px"
  prevX = e.clientX
  prevY = e.clientY
}


축하해요! 이제 창을 끌 수 있습니다.

크기 조정



모든 리사이저를 얻자.

const resizers = document.querySelectorAll(".resizer")


상단 표시줄과 마찬가지로 리사이저를 잡고 있을 때만 창 크기를 조정하려고 합니다. 비슷한 일을 하지만 for 루프에서 모든 리사이저에 적용해 봅시다.

for (let resizer of resizers){
  resizer.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){
      const rect = xwindow.getBoundingClientRect()
      prevX = e.clientX
      prevY = e.clientY
    }
    function mouseup(){
      window.removeEventListener("mousemove", mousemove)
      window.removeEventListener("mouseup", mouseup)
    }
  }
}


이제 우리는 JS에게 우리가 들고 있는 resizer를 알려줘야 합니다. 클래스별로 쉽게 확인할 수 있습니다. 예를 들어 tl는 "왼쪽 위"를 의미합니다. 현재 리사이저에 이러한 클래스가 있는지 먼저 확인하려면 currentResiser를 선언하고 e.target(우리가 잡고 있는 요소)에 할당해야 합니다.

...
let prevY = e.clientY
let currentResizer = e.target
...


크기 조정기의 클래스 목록에 클래스가 있는지 확인하기 위해 currentResizer.classList.contains("tl") 문에 if를 사용합니다. 그렇다면 창의 새 크기를 계산해야 합니다. 또한 상단, 왼쪽 또는 상단 왼쪽 리사이저인 경우 topleft 도 변경해야 합니다.

...
function mousemove(e){
  const rect = xwindow.getBoundingClientRect()
  if(currentResizer.classList.contains("br")){
    xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
    xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
  }
  else if(currentResizer.classList.contains("bl")){
    xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
    xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
    xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
  }
  else if(currentResizer.classList.contains("tr")){
    xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
    xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
    xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
  }
  else if(currentResizer.classList.contains("tl")){
    xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
    xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
    xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
    xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
  }
  else if(currentResizer.classList.contains("t")){
    xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
    xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
  }
  else if(currentResizer.classList.contains("b")){
    xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
  }
  else if(currentResizer.classList.contains("l")){
    xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
    xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
  }
  else if(currentResizer.classList.contains("r")){
    xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
  }
  prevX = e.clientX
  prevY = e.clientY
}
...


짜잔! 이제 크기를 조정할 수도 있습니다.

코드



HTML




<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
  <script defer src="index.js"></script>
</head>
<body>

  <div class="window">
    <div class="resizer corner tl"></div>
    <div class="resizer corner tr"></div>
    <div class="resizer corner bl"></div>
    <div class="resizer corner br"></div>
    <div class="resizer t"></div>
    <div class="resizer b"></div>
    <div class="resizer l"></div>
    <div class="resizer r"></div>

    <div class="body">
      <div class="topbar">
        <div class="btns">
          <div></div>
          <div></div>
          <div></div>
        </div>
      </div>

      <!-- your content here -->
    </div>
  </div>

</body>
</html>


SCSS




*{
  margin: 0;
  box-sizing: border-box;
}


.window{
  width: 600px;
  height: 400px;
  min-width: 300px;
  min-height: 200px;
  position: absolute;

  .resizer{
    position: absolute;
    z-index: 1;
    width: 22px;
    height: 22px;
    background: #41a94c80; //delete
    &.corner{
      z-index: 2;
      &.tl{ cursor: nw-resize; top: -5px; left: -5px; }
      &.tr{ cursor: ne-resize; top: -5px; right: -5px; }
      &.bl{ cursor: sw-resize; bottom: -5px; left: -5px; }
      &.br{ cursor: se-resize; bottom: -5px; right: -5px; }
    }
    &.t, &.b{
      width: 100%;
      height: 14px;
    }
    &.l, &.r{
      width: 14px;
      height: 100%;
    }
    &.t{ cursor: n-resize; top: -5px; }
    &.b{ cursor: s-resize; bottom: -5px; }
    &.l{ cursor: w-resize; left: -5px; }
    &.r{ cursor: e-resize; right: -5px; }
  }

  .body{
    border-radius: 15px;
    overflow: hidden;
    height: 100%;
    background: #f4f4f4;
    .topbar{
      width: 100%;
      height: 60px;
      background: #8c8c8c;
      display: flex;
      align-items: center;
      padding: 0 30px;
      .btns{
        display: flex;
        gap: 9px;
        div{
          height: 14px;
          width: 14px;
          border-radius: 50%;
          &:nth-child(1){ background:#FF5F58; }
          &:nth-child(2){ background:#FFBE2F; }
          &:nth-child(3){ background:#2AC940; }
        }
      }
    }
  }

}


JS




const xwindow = document.querySelector(".window")

const topbar = document.querySelector(".topbar")
topbar.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){
    let newX = e.clientX - prevX
    let newY = e.clientY - prevY
    const rect = xwindow.getBoundingClientRect()
    xwindow.style.left = rect.left + newX + "px"
    xwindow.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")

for (let resizer of resizers){
  resizer.addEventListener("mousedown", mousedown)
  function mousedown(e){
    let currentResizer = e.target
    let prevX = e.clientX
    let prevY = e.clientY
    window.addEventListener("mousemove", mousemove)
    window.addEventListener("mouseup", mouseup)
    function mousemove(e){
      const rect = xwindow.getBoundingClientRect()
      if(currentResizer.classList.contains("br")){
        xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
        xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
      }
      else if(currentResizer.classList.contains("bl")){
        xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
        xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
        xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
      }
      else if(currentResizer.classList.contains("tr")){
        xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
        xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
        xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
      }
      else if(currentResizer.classList.contains("tl")){
        xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
        xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
        xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
        xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
      }
      else if(currentResizer.classList.contains("t")){
        xwindow.style.height = rect.height + (prevY - e.clientY) + "px"
        xwindow.style.top = rect.top + (e.clientY - prevY) + "px"
      }
      else if(currentResizer.classList.contains("b")){
        xwindow.style.height = rect.height + (e.clientY - prevY) + "px"
      }
      else if(currentResizer.classList.contains("l")){
        xwindow.style.width = rect.width + (prevX - e.clientX) + "px"
        xwindow.style.left = rect.left + (e.clientX - prevX) + "px"
      }
      else if(currentResizer.classList.contains("r")){
        xwindow.style.width = rect.width + (e.clientX - prevX) + "px"
      }
      prevX = e.clientX
      prevY = e.clientY
    }
    function mouseup(){
      window.removeEventListener("mousemove", mousemove)
      window.removeEventListener("mouseup", mouseup)
    }
  }
}

좋은 웹페이지 즐겨찾기