移动端圈画选中单元格demo

118 阅读1分钟

今天PD提出了一个这样的需求,移动端中,在一个单元格内,可手滑圈中某些单元格,然后把圈中的列举出来给予相应的事件处理。这里我是通过touchstart、touchmove、touchend实现的,废话不多说,直接上代码!

1662111149096.gif

<!DOCTYPE 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" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
    />
    <title>Document</title>
  </head>
  
  
  <style>
    body {
      width: 100%;
      height: 100%;
      margin: 0;
    }
    .box {
      width: 100%;
      height: 100%;
      position: relative;
    }
    ul {
      display: flex;
      flex-wrap: wrap;
      margin: 34px;
      padding: 0;
    }
    li {
      width: 100px;
      height: 100px;
      list-style: none;
      text-align: center;
      line-height: 100px;
    }
    li:nth-child(2n) {
      background-color: bisque;
    }
    li:nth-child(2n + 1) {
      background-color: antiquewhite;
    }
    #event {
      /* 覆盖掉ul,定位在ul上面*/
      width: 300px; /* 宽为ul的和 */
      height: 300px; /* 高为ul的和 */
      background-color: none;
      position: absolute;
      top: 34px; /* top为ul的margin-top */
      left: 34px; /* let为ul的margin-left */
      z-index: 10;
    }
  </style>
  
  
  <body>
    <div id="box">
      <ul id="teble-wrapper">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
      </ul>
      <div id="event"></div>
    </div>
  </body>
  
  
  <script>
    const eventDom = document.getElementById("event");
    const box = document.getElementById("box");
    const ul = document.getElementById("teble-wrapper");
    let placementX = 0; //画圈落点X坐标
    let placementY = 0; //画圈落点Y坐标

    //落点开始
    eventDom.addEventListener(
      "touchstart",
      function (e) {
        //清除上次所选的
        if (document.getElementById("draw-dom")) {
          const drawDom = document.getElementById("draw-dom");
          drawDom.remove();
        }

        placementX = e.touches[0].clientX;
        placementY = e.touches[0].clientY;

        //创建一个div,覆盖被选中的单元格(阴影部分)
        let div = document.createElement("div");
        let divattr = document.createAttribute("id");
        divattr.value = "draw-dom";
        div.setAttributeNode(divattr);
        div.style.position = "absolute";
        div.style.top = e.touches[0].clientY + "px";
        div.style.left = e.touches[0].clientX + "px";
        div.style.background = "#000";
        div.style.opacity = "0.3";
        div.style.width = "0px";
        div.style.height = "0px";
        div.style.zIndex = "11";
        box.appendChild(div);
      },
      false
    );

    //开始移动
    eventDom.addEventListener(
      "touchmove",
      function (e) {
        e.preventDefault();
        const drawDom = document.getElementById("draw-dom");
        
        //判断圈画的宽度不能超出单元格的宽,及ul的宽
        if (e.touches[0].clientX < eventDom.offsetLeft + eventDom.clientWidth) {
          drawDom.style.width = e.touches[0].clientX - placementX + "px";
        } else {
          drawDom.style.width =
            eventDom.offsetLeft + eventDom.style.width - placementX + "px";
        }
        
         //判断圈画的高度度不能超出单元格的宽,及ul的高
        if (e.touches[0].clientY < eventDom.offsetTop + eventDom.clientHeight) {
          drawDom.style.height = e.touches[0].clientY - placementY + "px";
        } else {
          drawDom.style.height =
            eventDom.offsetTop + eventDom.style.height - placementY + "px";
        }
      },
      false
    );

    //停止移动
    eventDom.addEventListener(
      "touchend",
      function (e) {
        const drawDom = document.getElementById("draw-dom");
        const liLists = Array.from(ul.children); //因为ul.children获取的元素是伪数组,使用Array.from转化为真正数组
        let selectLis = [];//被选中的li

        //停止移动之后,判断哪些单元格被选中;
        //这里我的思路是判断每个li是否与圈画的矩形是否相交或重叠,若相交或重叠及被选中
        //这样问题实际就转化为在一个坐标中判断两个矩形是否相交或重叠,那么判断相交或重叠我这边的一个思路就是判断两个矩形的中心坐标的水平和垂直距离,只要这两个值满足某种条件就可以相交。

        //开始移动落点 X,Y 的坐标
        let startDrawDomCoordinate = {
          x: drawDom.offsetLeft,
          y: drawDom.offsetTop,
        };

        //结束移动落点 X,Y 的坐标
        let endDrawDomCoordinate = {
          x: drawDom.offsetLeft + Number(drawDom.style.width.split("px")[0]),
          y: drawDom.offsetTop + Number(drawDom.style.height.split("px")[0]),
        };

        let drawDomWidth = Number(drawDom.style.width.split("px")[0]); //drawDom的宽
        let drawDomHeight = Number(drawDom.style.height.split("px")[0]); //drawDom的高
        let drawDomMiddleX = (startDrawDomCoordinate.x + endDrawDomCoordinate.x) / 2; //drawDom中心X坐标
        let drawDomMiddleY = (startDrawDomCoordinate.y + endDrawDomCoordinate.y) / 2; //drawDom中心Y坐标

        liLists.forEach((item, index) => {
          //li左上角 X,Y 坐标
          let liLeftUpperCorner = {
            x: item.offsetLeft,
            y: item.offsetTop,
          };
           //li右下角角 X,Y 坐标
          let liRightQuarter = {
            x: item.offsetLeft + item.offsetWidth,
            y: item.offsetTop + item.offsetHeight,
          };

          let liWidth = item.offsetWidth; //li的宽
          let liHeight = item.offsetHeight; // li的高

          let liMiddelX = (liLeftUpperCorner.x + liRightQuarter.x) / 2; //li中心X坐标
          let liMiddelY = (liLeftUpperCorner.y + liRightQuarter.y) / 2; //li中心Y坐标

          
          if (
            Math.abs(liMiddelX - drawDomMiddleX) <= drawDomWidth / 2 + liWidth / 2 &&
            Math.abs(liMiddelY - drawDomMiddleY) <= drawDomHeight / 2 + liHeight / 2
          ) {
          //相交或重叠了,及被选中
            selectLis.push(index + 1);
          }
        });
        console.log(selectLis);
      },
      false
    );
  </script>
</html>