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
浙公网安备 33010602011771号