前端笔记之截图选择

纯原生实现截图选择

最近在写自己的ui组件(没错,我已经不满足做一个只会cv的程序猿!!!),目前想写一个上传图片的组件,之前笔记里也记载了如何上传图片。但是我想做一个可以实现裁剪的图片上传,那么图片上传已经实现了,剩下的就是裁剪图片了。这里主要记录原生js实现一个图片裁剪。代码主要体现实现思路,所以会有代码冗余繁琐等问题。

实现思路

首先是文件上传预览,因为要实现裁剪上传,所以就不能传统input type=file 选择文件后就交互后端,应该使用FileReader的readAsDataURL直接把图片读取出来加载,然后交给裁剪容器去裁剪。
那么裁剪容器真的能做到裁剪吗?当然是不行了,它只是获取选中的左上和右下边界点的坐标,裁剪这个事情还是要交给canvas去做的。
本次笔记主要记录如何呈现一个裁剪容器。
首先你得有一个遮罩层和一个选中区,这里一开始真的是难倒我了,一开始就去死磕blend-mode,但是在选中区背景色为透明的时候,这个方法不能满足我的要求。所以人不能喜新厌旧,不能学了点新东西就老想用上,最后实现遮罩层和选中区的方法很简单就是用border,让border足够的粗大,颜色设置为半透明的黑色不就是遮罩层吗?那么只需要控制选中区的大小就可以实现裁剪选中了。

代码实现

<!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">
  <title>截图选中</title>
  <link rel="stylesheet" type="text/css" href="./css/jietu.css">
  </link>
</head>

<body>
  <div class="container">
    <img src="./img/皮卡丘01.jpg" alt="上传图片" />
    <div class="select-area s-move-content-outer">
      <div class="s-move-content-direction n"></div>
      <div class="s-move-content-direction ne"></div>
      <div class="s-move-content-direction e"></div>
      <div class="s-move-content-direction se"></div>
      <div class="s-move-content-direction s"></div>
      <div class="s-move-content-direction sw"></div>
      <div class="s-move-content-direction w"></div>
      <div class="s-move-content-direction nw"></div>
    </div>
  </div>
  <script>
    // 获取展示的元素
    const selectArea = document.querySelector(".select-area");
    // 获取各个拖动点
    const directionN = document.querySelector(".n");
    const directionE = document.querySelector(".e");
    const directionS = document.querySelector(".s");
    const directionW = document.querySelector(".w");
    const directionNE = document.querySelector(".ne");
    const directionSE = document.querySelector(".se");
    const directionSW = document.querySelector(".sw");
    const directionNW = document.querySelector(".nw");

    // 记录每次点击拖动点的坐标位置原始移动位置
    let initialPointCoordinates = [0, 0];
    // 记录拖动前展示区的对应border的宽度,当拖动斜角上的点时需要两个边框一起改变
    let currentBorderWidth = [0, 0];
    // 记录拖动前展示区的宽高
    let sizeBeforeChange = [0, 0];
    // 绑定向上拖动的函数
    directionN.addEventListener("mousedown", (event) => {
      // 记录初始点的坐标
      initialPointCoordinates = [event.x, event.y];
      sizeBeforeChange = [selectArea.clientWidth, selectArea.clientHeight];
      currentBorderWidth[0] = parseFloat(getComputedStyle(selectArea)["border-top-width"]);
      selectArea.addEventListener("mousemove", moveUp);
    });
    // 绑定向下拖动的函数
    directionS.addEventListener("mousedown", (event) => {
      initialPointCoordinates = [event.x, event.y];
      sizeBeforeChange = [selectArea.clientWidth, selectArea.clientHeight];
      currentBorderWidth[0] = parseFloat(getComputedStyle(selectArea)["border-bottom-width"]);
      selectArea.addEventListener("mousemove", moveDown);
    });
    // 绑定向左拖动的函数
    directionW.addEventListener("mousedown", (event) => {
      initialPointCoordinates = [event.x, event.y];
      sizeBeforeChange = [selectArea.clientWidth, selectArea.clientHeight];
      currentBorderWidth[0] = parseFloat(getComputedStyle(selectArea)["border-left-width"]);
      selectArea.addEventListener("mousemove", moveLeft);
    });
    // 绑定向右拖动的函数
    directionE.addEventListener("mousedown", (event) => {
      initialPointCoordinates = [event.x, event.y];
      sizeBeforeChange = [selectArea.clientWidth, selectArea.clientHeight];
      currentBorderWidth[0] = parseFloat(getComputedStyle(selectArea)["border-right-width"]);
      selectArea.addEventListener("mousemove", moveRight);
    });
    // 绑定向右上拖动的函数
    directionNE.addEventListener("mousedown", (event) => {
      initialPointCoordinates = [event.x, event.y];
      sizeBeforeChange = [selectArea.clientWidth, selectArea.clientHeight];
      currentBorderWidth[0] = parseFloat(getComputedStyle(selectArea)["border-top-width"]);
      currentBorderWidth[1] = parseFloat(getComputedStyle(selectArea)["border-right-width"]);
      selectArea.addEventListener("mousemove", moveUpRight);
    });
    // 绑定向右下拖动的函数
    directionSE.addEventListener("mousedown", (event) => {
      initialPointCoordinates = [event.x, event.y];
      sizeBeforeChange = [selectArea.clientWidth, selectArea.clientHeight];
      currentBorderWidth[0] = parseFloat(getComputedStyle(selectArea)["border-bottom-width"]);
      currentBorderWidth[1] = parseFloat(getComputedStyle(selectArea)["border-right-width"]);
      selectArea.addEventListener("mousemove", moveDownRight);
    });

    // 绑定向左上拖动的函数
    directionNW.addEventListener("mousedown", (event) => {
      initialPointCoordinates = [event.x, event.y];
      sizeBeforeChange = [selectArea.clientWidth, selectArea.clientHeight];
      currentBorderWidth[0] = parseFloat(getComputedStyle(selectArea)["border-top-width"]);
      currentBorderWidth[1] = parseFloat(getComputedStyle(selectArea)["border-left-width"]);
      selectArea.addEventListener("mousemove", moveUpLeft);
    });

    directionSW.addEventListener("mousedown", (event) => {
      initialPointCoordinates = [event.x, event.y];
      sizeBeforeChange = [selectArea.clientWidth, selectArea.clientHeight];
      currentBorderWidth[0] = parseFloat(getComputedStyle(selectArea)["border-bottom-width"]);
      currentBorderWidth[1] = parseFloat(getComputedStyle(selectArea)["border-left-width"]);
      selectArea.addEventListener("mousemove", moveDownLeft);
    });

    /**
     * 向上移动函数
     */
    const moveUp = (event) => {
      // 用当前位置减去原始位置算出移动距离
      const movingDistance = event.y - initialPointCoordinates[1];
      if (movingDistance > sizeBeforeChange[1]) return null;
      // 把计算得出距离加到选中元素高度上面去,注意向下拖动时缩减高度,所以用减号
      selectArea.style.height = sizeBeforeChange[1] - movingDistance + "px"
      // 同时根据移动距离减去对应的boder的宽度
      selectArea.style.borderTopWidth = currentBorderWidth[0] + movingDistance + "px";
      document.addEventListener("mouseup", () => {
        selectArea.removeEventListener("mousemove", moveUp);
      })
    }

    /**
     * 向下移动的函数
     */
    const moveDown = (event) => {
      // 用当前位置减去原始位置算出移动距离
      const movingDistance = event.y - initialPointCoordinates[1];
      // 设置向上拖动的极限,也就是上边框的底部
      if (movingDistance < -sizeBeforeChange[1]) return null;
      // 把计算得出距离加到选中元素高度上面去
      selectArea.style.height = sizeBeforeChange[1] + movingDistance + "px";
      // 同时根据移动距离减去对应的boder的宽度, 注意向下拖动时缩减border宽度,所以要做减法
      selectArea.style.borderBottomWidth = currentBorderWidth[0] - movingDistance + "px";
      document.addEventListener("mouseup", () => {
        selectArea.removeEventListener("mousemove", moveDown);
      })
    }

    /**
     * 向左移动的函数
     */
    const moveLeft = (event) => {
      // 用当前位置减去原始位置算出移动距离,这里减去的是宽度
      const movingDistance = event.x - initialPointCoordinates[0];
      if (movingDistance > sizeBeforeChange[0]) return null;
      // 把计算得出距离加到选中元素宽度上面去
      selectArea.style.width = sizeBeforeChange[0] - movingDistance + "px"
      // 同时根据移动距离减去对应的boder的宽度
      selectArea.style.borderLeftWidth = currentBorderWidth[0] + movingDistance + "px";
      document.addEventListener("mouseup", () => {
        selectArea.removeEventListener("mousemove", moveLeft);
      })
    }

    /**
     * 向右移动的函数
     */
    const moveRight = (event) => {
      // 用当前位置减去原始位置算出移动距离,这里减去的是宽度
      const movingDistance = event.x - initialPointCoordinates[0];
      if (movingDistance < -sizeBeforeChange[0]) return null;
      // 把计算得出距离加到选中元素宽度上面去
      selectArea.style.width = sizeBeforeChange[0] + movingDistance + "px"
      // 同时根据移动距离减去对应的boder的宽度
      selectArea.style.borderRightWidth = currentBorderWidth[0] - movingDistance + "px";
      document.addEventListener("mouseup", () => {
        selectArea.removeEventListener("mousemove", moveRight);
      })
    }

    /**
     * 向右上移动的函数
     */
    const moveUpRight = (event) => {
      const verticalDistance = event.y - initialPointCoordinates[1];
      const horizontalDistance = event.x - initialPointCoordinates[0];
      // 当垂直角度拖拽已经超过极限,则高度始终为0
      selectArea.style.height = ((verticalDistance > sizeBeforeChange[1]) ? 0 : (sizeBeforeChange[1] - verticalDistance)) + "px";
      // 同时对应方向的border宽度侵占了所有的高度
      selectArea.style.borderTopWidth = ((verticalDistance > sizeBeforeChange[1]) ? (currentBorderWidth[0] + sizeBeforeChange[1]) : (currentBorderWidth[0] + verticalDistance)) + "px";
      // 同理水平角度拖拽超过极限,则宽度始终为0
      selectArea.style.width = ((horizontalDistance < -sizeBeforeChange[0]) ? 0 : (sizeBeforeChange[0] + horizontalDistance)) + "px";
      // 相对应的border侵占了所有的宽度
      selectArea.style.borderRightWidth = (horizontalDistance < -sizeBeforeChange[0]) ? (currentBorderWidth[1] - sizeBeforeChange[0]) : (currentBorderWidth[1] - horizontalDistance) + "px";
      document.addEventListener("mouseup", () => {
        selectArea.removeEventListener("mousemove", moveUpRight);
      })
    }

    /**
     * 向右下移动的函数
     */
    const moveDownRight = (event) => {
      const verticalDistance = event.y - initialPointCoordinates[1];
      const horizontalDistance = event.x - initialPointCoordinates[0];
      selectArea.style.height = ((verticalDistance < -sizeBeforeChange[1]) ? 0 : (sizeBeforeChange[1] + verticalDistance)) + "px";
      selectArea.style.borderBottomWidth = ((verticalDistance < -sizeBeforeChange[1]) ? (currentBorderWidth[0] + sizeBeforeChange[1]) : (currentBorderWidth[0] - verticalDistance)) + "px";
      selectArea.style.width = ((horizontalDistance < -sizeBeforeChange[0]) ? 0 : (sizeBeforeChange[0] + horizontalDistance)) + "px";
      selectArea.style.borderRightWidth = (horizontalDistance < -sizeBeforeChange[0]) ? (currentBorderWidth[1] - sizeBeforeChange[0]) : (currentBorderWidth[1] - horizontalDistance) + "px";
      document.addEventListener("mouseup", () => {
        selectArea.removeEventListener("mousemove", moveDownRight);
      })
    }
    /**
    * 向左上移动的函数
    */
    const moveUpLeft = (event) => {
      const verticalDistance = event.y - initialPointCoordinates[1];
      const horizontalDistance = event.x - initialPointCoordinates[0];
      selectArea.style.height = ((verticalDistance > sizeBeforeChange[1]) ? 0 : (sizeBeforeChange[1] - verticalDistance)) + "px";
      selectArea.style.borderTopWidth = ((verticalDistance > sizeBeforeChange[1]) ? (currentBorderWidth[0] + sizeBeforeChange[1]) : (currentBorderWidth[0] + verticalDistance)) + "px";
      selectArea.style.width = ((horizontalDistance > sizeBeforeChange[0]) ? 0 : (sizeBeforeChange[0] - horizontalDistance)) + "px";
      selectArea.style.borderLeftWidth = (horizontalDistance > sizeBeforeChange[0]) ? (currentBorderWidth[1] + sizeBeforeChange[0]) : (currentBorderWidth[1] + horizontalDistance) + "px";
      document.addEventListener("mouseup", () => {
        selectArea.removeEventListener("mousemove", moveUpLeft);
      })
    }

    /**
    * 向左下移动的函数
    */
    const moveDownLeft = (event) => {
      const verticalDistance = event.y - initialPointCoordinates[1];
      const horizontalDistance = event.x - initialPointCoordinates[0];
      selectArea.style.height = ((verticalDistance < -sizeBeforeChange[1]) ? 0 : (sizeBeforeChange[1] + verticalDistance)) + "px";
      selectArea.style.borderBottomWidth = ((verticalDistance < -sizeBeforeChange[1]) ? (currentBorderWidth[0] + sizeBeforeChange[1]) : (currentBorderWidth[0] - verticalDistance)) + "px";
      selectArea.style.width = ((horizontalDistance > sizeBeforeChange[0]) ? 0 : (sizeBeforeChange[0] - horizontalDistance)) + "px";
      selectArea.style.borderLeftWidth = (horizontalDistance > sizeBeforeChange[0]) ? (currentBorderWidth[1] + sizeBeforeChange[0]) : (currentBorderWidth[1] + horizontalDistance) + "px";
      document.addEventListener("mouseup", () => {
        selectArea.removeEventListener("mousemove", moveDownLeft);
      })
    } 
  </script>
</body>

</html>

上面引用的css如下:

body {
  padding: 0;
  margin: 0;
  border: 0;
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.container {
  width: 50vh;
  height: 50vh;
  position: relative;
}

.container>img {
  width: 100%;
  height: 100%;
  user-select: none;
}

.select-area {
  width: 40%;
  height: 40%;
  border: 15vh solid rgba(0, 0, 0, 0.3);
  position: absolute;
  top: 0;
  left: 0;
}

.s-move-content-direction {
  width: 7px;
  height: 7px;
  position: absolute;
  background-color: #070707;
}

.n {
  cursor: n-resize;
  left: 50%;
  top: 0;
  transform: translateY(-90%) translateX(-50%);
}

.ne {
  cursor: ne-resize;
  top: 0;
  right: 0;
  transform: translateY(-90%) translateX(90%);
}

.e {
  cursor: e-resize;
  top: 50%;
  right: 0;
  transform: translateY(-50%) translateX(90%);
}

.se {
  cursor: se-resize;
  bottom: 0;
  right: 0;
  transform: translateY(90%) translateX(90%);
}

.s {
  cursor: s-resize;
  bottom: 0;
  left: 50%;
  transform: translateY(90%) translateX(-50%);
}

.sw {
  cursor: sw-resize;
  bottom: 0;
  left: 0;
  transform: translateY(90%) translateX(-90%);
}

.w {
  cursor: w-resize;
  top: 50%;
  left: 0;
  transform: translateY(-50%) translateX(-90%);
}

.nw {
  cursor: nw-resize;
  top: 0;
  left: 0;
  transform: translateY(-90%) translateX(-90%);
}

这样截图选中效果就实现了,那么接下来就是交给canvas坐标实现上传了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值