vue.js3: 裁剪图片的一部分并保存(vue@3.2.37)

一,js代码:

<template>
<div>
  <div style="width: 800px;margin: auto;">
  <div id="wrapper" style="display:none;position: relative;overflow: hidden;" >
    <img id="img" :src="imgSrc" style="width:800px;display: block;" />

    <div id="mask" class="mask" @mousedown="maskDown" >

      <div id="coor_tl" class="coor_tl" @mousedown="coormousedown_tl"  ></div>
      <div id="coor_tm" class="coor_tm" @mousedown="coormousedown_tm"   ></div>
      <div id="coor_tr" class="coor_tr" @mousedown="coormousedown_tr"   ></div>

      <div id="coor_ml" class="coor_ml" @mousedown="coormousedown_ml"  ></div>
      <div id="coor_mr" class="coor_mr" @mousedown="coormousedown_mr"  ></div>

      <div id="coor_bl" class="coor_bl" @mousedown="coormousedown_bl"  ></div>
      <div id="coor_bm" class="coor_bm" @mousedown="coormousedown_bm"   ></div>
      <div id="coor_br" class="coor_br" @mousedown="coormousedown_br"   ></div>
    </div>

  </div>

  <div>
    选择图片:
    <input type="file" ref="hiddenfile" accept="image/*" @change="readImg" class="hiddenInput" />
  </div>

  <div>
    <el-button :disabled="buttonEnable === true  ?  false : true" type="info" plain
               @click="down" style="width:400px;margin-top: 10px;">截取图片并下载</el-button>
  </div>

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

<script>
import {ref,onMounted} from "vue";

export default {
  name: "ImageCrop",
  setup() {
    //图片的src
    const imgSrc = ref("");
    //图片的原宽高
    const imgWidth = ref(0);
    const imgHeight = ref(0);
    //读取图片的信息
    const readImg = (e) => {
      let file = e.target.files[0];
      let reader = new FileReader();
      reader.readAsDataURL(file);

      document.getElementById("wrapper").style.display="";

      reader.onload = () =>{
        //显示图片
        imgSrc.value = reader.result;

        let img = new Image();
        img.src= reader.result;
        img.onload = () => {
          //保存宽高
          imgWidth.value = img.width;
          imgHeight.value = img.height;
          //设置图片和wrapper的宽高
          let imgStyle = document.getElementById('img').getAttributeNode('style');
          let wrapper = document.getElementById('wrapper');
          if (img.width >= img.height) {
            imgStyle.value = 'width:800px;display: block;';
            wrapper.style.width = "800px";
            let wHeight = (800*img.height) / img.width;
            wrapper.style.height = wHeight+"px";
            wrapper.style.marginLeft = "0px";
          } else {
              //document.getElementById('img').style.height = '800px';
            imgStyle.value = 'height:800px;display: block;';

            wrapper.style.height = "800px";
            let wWidth = (800*img.width) / img.height;
            wrapper.style.width = wWidth+"px";
            wrapper.style.marginLeft = (800 - wWidth) / 2 +"px";
          }

          //重置选区的位置
          let mask = document.getElementById('mask');
          mask.style.width = "100px";
          mask.style.height = "100px";
          mask.style.left = "20px";
          mask.style.top = "20px";
        }
      }
    }
    
    // 当前操作的类型,标记当前要拖动的区域
    const operateType = ref("");

    //记录点击时的x位置
    const clickX = ref(0);
    //记录点击时的y位置
    const clickY = ref(0);

    //从event中得到位置
    const getLocation = (e) => {
      return {
        x: e.x || e.clientX,
        y: e.y || e.clientY
      }
    }

    //容器的位置x
    const wrapper_x = ref(0);
    //容器的位置y
    const wrapper_y = ref(0);
    //容器的宽度
    const wrapper_w = ref(0);
    //容器的高度
    const wrapper_h = ref(0);

    //鼠标在coor上面按下时的处理函数
    const onDragDown = (e, type) => {
      e.stopPropagation();
      console.log("begin onDragDown");

      origin_w.value = document.getElementById('mask').getBoundingClientRect().width;
      origin_h.value = document.getElementById('mask').getBoundingClientRect().height;

      origin_x.value = document.getElementById('mask').getBoundingClientRect().left;
      origin_y.value = document.getElementById('mask').getBoundingClientRect().top;

      wrapper_x.value = document.getElementById('wrapper').getBoundingClientRect().left;
      wrapper_y.value = document.getElementById('wrapper').getBoundingClientRect().top;

      wrapper_w.value = document.getElementById('wrapper').getBoundingClientRect().width;
      wrapper_h.value = document.getElementById('wrapper').getBoundingClientRect().height;

      var location = getLocation(e);
      clickY.value = location.y;
      clickX.value = location.x;
      operateType.value = type;
      return false;
    };

    //鼠标松开时的事件
    const onDragUp = () => {
      document.body.style.cursor = "auto";
      operateType.value = null;
    };
    //coor鼠标按下:底部左侧
    const coormousedown_bl = (e) => {
      onDragDown(e, "bl");
    };
    //coor鼠标按下:底部中间
    const coormousedown_bm = (e) => {
      onDragDown(e, "b");
    };
    //coor鼠标按下:底部右侧
    const coormousedown_br = (e) => {
      onDragDown(e, "br");
    };
    //coor鼠标按下:顶部左侧
    const coormousedown_tl = (e) => {
      onDragDown(e, "tl");
    };
    //coor鼠标按下:顶部中间
    const coormousedown_tm = (e) => {
      onDragDown(e, "t");
    };
    //coor鼠标按下:顶部右侧
    const coormousedown_tr = (e) => {
      onDragDown(e, "tr");
    };
    //coor鼠标按下:中部左侧
    const coormousedown_ml = (e) => {
      onDragDown(e, "l");
    };
    //coor鼠标按下:中部右侧
    const coormousedown_mr = (e) => {
      onDragDown(e, "r");
    };

    //coor,向右侧拖动
    const setMoveR = (location) => {
      let mask = document.getElementById('mask');
      var disHe = location.x - clickX.value;
      var widthe = origin_w.value+disHe;
      if (((origin_x.value-wrapper_x.value)+widthe) > wrapper_w.value) {
        widthe = wrapper_w.value-(origin_x.value-wrapper_x.value);
      }

      mask.style.width =  widthe+ 'px';
    }
    //coor,向底部拖动
    const setMoveB = (location) => {
      let mask = document.getElementById('mask');
      var disVs = location.y - clickY.value;
      var heights = (origin_h.value+disVs);
      if (((origin_y.value-wrapper_y.value)+heights) > wrapper_h.value) {
        heights = wrapper_h.value-(origin_y.value-wrapper_y.value);
      }

      mask.style.height = heights + 'px';
    }
    //coor,向左侧拖动
    const setMoveL = (location,disX) => {
      let mask = document.getElementById('mask');
      if (disX >= 0) {
        var disH = location.x - clickX.value;
        var widthw = origin_w.value-disH;
        mask.style.width =  widthw+ 'px';
      }

      if (disX >= 0) {
        mask.style.left = disX + 'px';
      } else {
        mask.style.left =  '0px';
      }
    }

    //coor,向顶部拖动
    const setMoveT = (location,disY) => {
      let mask = document.getElementById('mask');
      if (disY >= 0) {
        var disV = location.y - clickY.value;
        var heightn = origin_h.value - disV;
        mask.style.height = heightn + 'px';
      }
      if (disY >= 0) {
        mask.style.top = disY + 'px';
      } else {
        mask.style.top =  '0px';
      }
    }

    //mask可以移动到的最右位置
    const rightMax = ref(0);
    //mask可以移动到的最下位置
    const bottomMax = ref(0);

    //移动mask的方法
    const setMoveMask = (location) => {

      var nx = location.x;
      var ny = location.y;
      //计算移动后的左偏移量和顶部的偏移量
      var nl = nx - (clickX.value - origin_x.value) - wrapper_x.value;
      var nt = ny - (clickY.value - origin_y.value) - wrapper_y.value;

      if (nl < 0) {
        nl = 0;
      }
      if (nl > rightMax.value) {
        nl = rightMax.value;
      }
      if (nt < 0) {
        nt = 0;
      }
      if (nt > bottomMax.value) {
        nt = bottomMax.value;
      }

      document.getElementById("mask").style.left = nl + 'px';
      document.getElementById("mask").style.top = nt + 'px';
    }

    //鼠标移动的通用处理方法
    const move = (operateType, location) => {
      let disY = (origin_y.value + (location.y - clickY.value))-wrapper_y.value;
      let disX = (origin_x.value + (location.x - clickX.value))-wrapper_x.value;

      switch (operateType) {
        case "r":
          setMoveR(location);
          break;
        case "b":
          setMoveB(location);
          break;
        case "l":
          setMoveL(location,disX);
          break;
        case "t":
          setMoveT(location,disY);
          break;
        case "br":
          setMoveB(location);
          setMoveR(location);
          break;
        case "tr":
          setMoveT(location,disY);
          setMoveR(location);
          break;
        case "tl":
          setMoveT(location,disY);
          setMoveL(location,disX);
          break;
        case "bl":
          setMoveB(location);
          setMoveL(location,disX);
          break;
        case "mask":
          setMoveMask(location);
          break;
      }
    };

    //当页面中鼠标移动时触发
    const onDragMove = (e) => {
      var location = getLocation(e);
      switch (operateType.value) {
        case "t":
          move("t", location);
          break;
        case "b":
          move("b", location);
          break;
        case "l":
          move("l", location);
          break;
        case "r":
          move("r", location);
          break;
        case "tl":
          move("tl", location);
          break;
        case "tr":
          move("tr", location);
          break;
        case "bl":
          move("bl", location);
          break;
        case "br":
          move("br", location);
          break;
        case "mask":
          move("mask", location);
          break;
      }
      return false;
    };

    //mask的宽度
    const origin_w = ref(0);
    //mask的高度
    const origin_h = ref(0);
    //mask的x
    const origin_x = ref(0);
    //mask的y
    const origin_y = ref(0);

    // mounted,页面加载成功的生命周期
    onMounted(()=>{
      document.onmousemove = onDragMove;
      document.onmouseup = onDragUp;
    })

    //当鼠标在mask区域中按下时触发
    const maskDown = (e) => {
      console.log("-------------------maskdown begin");
      var location = getLocation(e);
      clickY.value = location.y;
      clickX.value = location.x;

      operateType.value = 'mask';

      origin_w.value = document.getElementById('mask').getBoundingClientRect().width;
      origin_h.value = document.getElementById('mask').getBoundingClientRect().height;

      origin_x.value = document.getElementById('mask').getBoundingClientRect().left;
      origin_y.value = document.getElementById('mask').getBoundingClientRect().top;

      wrapper_x.value = document.getElementById('wrapper').getBoundingClientRect().left;
      wrapper_y.value = document.getElementById('wrapper').getBoundingClientRect().top;

      wrapper_w.value = document.getElementById('wrapper').getBoundingClientRect().width;
      wrapper_h.value = document.getElementById('wrapper').getBoundingClientRect().height;

      rightMax.value = wrapper_w.value-origin_w.value;
      bottomMax.value = wrapper_h.value-origin_h.value;
    }

    //截取选中的图片区域并下载文件
     const down = () => {
       const canvas = document.createElement('canvas')
       const ctx = canvas.getContext('2d')

       //计算,得到原始的位置和宽高
       let sourceImage = document.getElementById('img');

       //判断图片是横片还是竖片
       let ratio = 0;
       if (imgWidth.value >= imgHeight.value) {
         let naturalWidth = sourceImage.naturalWidth;
         ratio = naturalWidth / 800;
       } else {
         let naturalHeight = sourceImage.naturalHeight;
         ratio = naturalHeight / 800;
       }
       
       const x = parseInt(document.getElementById("mask").style.left) * ratio;
       const y = parseInt(document.getElementById("mask").style.top) * ratio;
       const width = parseInt(document.getElementById("mask").style.width) * ratio;
       const height = parseInt(document.getElementById("mask").style.height) * ratio;

       canvas.width = width;
       canvas.height = height;
       //保存截取部分到canvas
       ctx.drawImage(sourceImage, x, y, width, height, 0, 0, width, height);
       //保存:
       downJpgByCanvas(canvas);
     }

    //下载图片
    const downJpgByCanvas = (canvas) => {
      var oA = document.createElement("a");
      let time = timeFormat();
      oA.download = "img_"+time+'.jpg';// 设置下载的文件名,默认是'下载'
      oA.href = canvas.toDataURL("image/jpeg");
      document.body.appendChild(oA);
      oA.click();
      oA.remove(); // 下载之后把创建的元素删除
    }

    //补0
    const add0 = (m) => {
      return m<10?'0'+m:m
    }
    //格式化时间
    const timeFormat = ()=>{
      var time = new Date();
      var y = time.getFullYear();
      var m = time.getMonth()+1;
      var d = time.getDate();
      var h = time.getHours();
      var mm = time.getMinutes();
      var s = time.getSeconds();
      let res = y+add0(m)+add0(d)+add0(h)+add0(mm)+add0(s);
      return res;
    }
    
     const buttonEnable = ref(true);
    
      return {
        readImg,
        imgSrc,

        coormousedown_bl,
        coormousedown_bm,
        coormousedown_br,

        coormousedown_tl,
        coormousedown_tm,
        coormousedown_tr,

        coormousedown_ml,
        coormousedown_mr,
        onDragUp,

        maskDown,

        down,
        buttonEnable,
      }
  }
}
</script>

<style scoped>
.coor_ml {
  width: 10px;
  height: 10px;
  overflow: hidden;
  position: absolute;
  left: 0; top: calc(50% - 5px);
  background-color: #ffFFFF;
  opacity: 0.2;
  z-index: 5000;
  cursor:w-resize;
}

.coor_mr {
  width: 10px;
  height: 10px;
  overflow: hidden;
  position: absolute;
  right: 0; top: calc(50% - 5px);
  background-color: #ffFFFF;
  opacity: 0.2;
  z-index: 5000;
  cursor:e-resize;
}

.coor_tl {
  width: 10px;
  height: 10px;
  overflow: hidden;
  position: absolute;
  left: 0; top: 0;
  background-color: #ffFFFF;
  opacity: 0.2;
  z-index: 5000;
  cursor:nw-resize;
}

.coor_tm {
  width: 10px;
  height: 10px;
  overflow: hidden;
  position: absolute;
  left: calc(50% - 5px); top: 0;
  background-color: #ffFFFF;
  opacity: 0.2;
  z-index: 5000;
  cursor:n-resize;
}

.coor_tr {
  width: 10px;
  height: 10px;
  overflow: hidden;
  position: absolute;
  right: 0; top: 0;
  background-color: #ffFFFF;
  opacity: 0.2;
  z-index: 5000;
  cursor:ne-resize;
}

.coor_bl {
  width: 10px;
  height: 10px;
  overflow: hidden;
  position: absolute;
  left: 0; bottom: 0;
  background-color: #ffFFFF;
  opacity: 0.2;
  z-index: 5000;
  cursor:sw-resize;
}

.coor_bm {
  width: 10px;
  height: 10px;
  overflow: hidden;
  position: absolute;
  left: calc(50% - 5px); bottom: 0;
  background-color: #ffFFFF;
  opacity: 0.2;
  z-index: 5000;
  cursor:s-resize;
}

.coor_br {
  width: 10px;
  height: 10px;
  overflow: hidden;
  position: absolute;
  right: 0; bottom: 0;
  background-color: #ffFFFF;
  opacity: 0.2;
  z-index: 5000;
  cursor:se-resize;
}

.mask {
  position: absolute;
  width:100px;
  height:100px;
  left:20px;
  top:20px;
  box-shadow: 0 0 0 999vw rgba(0, 0, 0, .5);
}
</style>

说明:刘宏缔的架构森林是一个专注架构的博客,

网站:https://blog.imgtouch.com
本文: https://blog.imgtouch.com/index.php/2023/06/02/vue-js3-cai-jian-tu-pian-de-yi-bu-fen-bing-bao-cun-vue-3-2/

         对应的源码可以访问这里获取: https://github.com/liuhongdi/
         或: https://gitee.com/liuhongdi

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,测试效果:

截取的部分:

 

 三,查看vue框架的版本:

liuhongdi@lhdpc:/data/vue/pdf/image2pdf$ npm list vue
image2pdf@0.1.0 /data/vue/pdf/image2pdf
├─┬ @vue/cli-plugin-babel@5.0.8
│ └─┬ @vue/babel-preset-app@5.0.8
│   ├─┬ @vue/babel-preset-jsx@1.3.0
│   │ └── vue@3.2.37 deduped invalid: "2.x" from node_modules/@vue/babel-preset-jsx
│   └── vue@3.2.37 deduped
└─┬ vue@3.2.37
  └─┬ @vue/server-renderer@3.2.37
    └── vue@3.2.37 deduped

 

posted @ 2022-09-07 14:30  刘宏缔的架构森林  阅读(567)  评论(0)    收藏  举报