Element-Ui结合canvas-select插件,实现上传图片类似PS钢笔工具的裁切
实现的功能就是图片上传,并且对上传的图片进行类似ps钢笔工具的裁切。裁切完成之后回显到el-upload并可传到后台。
<template> <div> <div class="img-upload-con"> <el-upload action="#" list-type="picture-card" :auto-upload="false" :limit="5" ref="imgupload" :on-change="handleEditChange" :file-list="fileList" > <i slot="default" class="el-icon-plus"></i> <div slot="file" slot-scope="{ file }"> <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" /> <span class="el-upload-list__item-actions"> <span class="el-upload-list__item-preview" @click="lassoImg(file)"> <i class="el-icon-lasso"></i> </span> <span class="el-upload-list__item-delete" @click="handleRemove(file)" > <i class="el-icon-delete"></i> </span> </span> </div> </el-upload> </div> <lasso-tool ref="LassoTool"></lasso-tool> </div> </template> <script> import LassoTool from "./LassoTool.vue"; export default { components: { LassoTool }, data() { return { fileList: [] }; }, methods: { handleEditChange(file, fileList) { // 图片改变 this.fileList = fileList; this.$forceUpdate(); }, lassoImg(file) { this.$refs.LassoTool.lassoImg(file); }, handleRemove(file) { // 图片删除 for (let i = 0; i < this.fileList.length; i++) { if (this.fileList[i].uid == file.uid) { this.fileList.splice(i, 1); } } this.$forceUpdate(); } } }; </script> <style scoped> .img-upload-con >>> .el-icon-lasso { width: 20px; height: 20px; background: url(../../static/icon/lasso.svg) no-repeat center; background-size: 100% 100%; } </style>
这个组件主要实现图片上传功能。
<template> <div> <el-dialog :visible.sync="lassoShow" :before-close="beforeCropperDialogClose" title="自由裁剪" custom-class="lasso-img-dialog" > <div> <div class="result-img" v-if="showResult"> <img :src="base64url" alt="" /> </div> <div class="lasso-canvas-container" v-else> <canvas id="lassoContainer1"></canvas> </div> <div class="operate-btn" style="margin-bottom: 0"> <span class="search-btn" @click="lassoOk">确定</span> <span class="empty" @click="beforeCropperDialogClose">取消</span> </div> </div> </el-dialog> <div class="shadow-canvas"> <canvas class="lasso-canvas" id="lassoContainer2"></canvas> </div> </div> </template> <script> // https://github.com/bookmarkbao/canvas-select 插件地址 import CanvasSelect from "canvas-select"; export default { props: [], data() { return { currentOperateFile: null, lassoShow: false, instance: null, base64url: null, showResult: false }; }, methods: { beforeCropperDialogClose() { this.instance.destroy(); this.lassoShow = false; },
getScale(imgWidth, imgHeight) {
const containerWidth = 500;
const containerHeight = 400;
const widthRatio = containerWidth / imgWidth;
const heightRatio = containerHeight / imgHeight;
const scale = Math.min(widthRatio, heightRatio);
return [imgWidth * scale, imgHeight * scale];
},
lassoImg(file) { // 初始化一些数据 this.showResult = false; this.base64url = null; if (this.instance) { this.instance.destroy(); } this.currentOperateFile = file; this.lassoShow = true; this.$nextTick(() => { // 这里需要使用$nextTick等待lassoContainer1渲染完成 const img = new Image(); img.onload = () => { let canvas = document.getElementById("lassoContainer1"); let width = img.naturalWidth; let height = img.naturalHeight; let arr = this.getScale(width, height); canvas.width = arr[0]; canvas.height = arr[1]; this.lassoAction(file); }; img.src = file.url; }); }, lassoAction(file) { this.instance = new CanvasSelect("#lassoContainer1", file.url); this.instance.createType = 2; this.instance.on("add", info => { const canvas = document.getElementById("lassoContainer2"); const ctx = canvas.getContext("2d"); // 加载图片并绘制到 Canvas 上 const img = new Image(); img.onload = () => { const selectedCoords = info.coor; const minX = Math.min(...selectedCoords.map(coor => coor[0])); const minY = Math.min(...selectedCoords.map(coor => coor[1])); const maxX = Math.max(...selectedCoords.map(coor => coor[0])); const maxY = Math.max(...selectedCoords.map(coor => coor[1])); const width = maxX - minX; const height = maxY - minY; canvas.width = width; canvas.height = height; // 绘制多边形路径并裁剪 ctx.beginPath(); ctx.moveTo(selectedCoords[0][0] - minX, selectedCoords[0][1] - minY); for (let i = 1; i < selectedCoords.length; i++) { ctx.lineTo( selectedCoords[i][0] - minX, selectedCoords[i][1] - minY ); } ctx.closePath(); ctx.clip(); ctx.drawImage(img, -minX, -minY); this.base64url = canvas.toDataURL("image/png", 1); this.showResult = true; this.$forceUpdate(); }; img.src = file.url; }); }, lassoOk() { // 将base64转换成file对象 let src = this.dataUrlToFile(this.base64url); this.currentOperateFile.raw = src; this.currentOperateFile.url = this.base64url; this.$forceUpdate(); this.$nextTick(() => { this.beforeCropperDialogClose(); }); }, dataUrlToFile(dataurl, filename = "file") { let arr = dataurl.split(","); let mime = arr[0].match(/:(.*?);/)[1]; let suffix = mime.split("/")[1]; let bstr = atob(arr[1]); let n = bstr.length; let u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new File([u8arr], `${filename}.${suffix}`, { type: mime }); } } }; </script> <style scoped> .operate-btn { width: 100%; max-width: 958px; overflow: hidden; margin-top: 21px; margin-bottom: 36px; } .search-btn { display: block; float: right; width: 85px; height: 32px; background: #1f4ebb; border-radius: 2px; font-size: 14px; color: #fff; text-align: center; line-height: 32px; cursor: pointer; } .empty { display: block; float: right; width: 85px; height: 32px; background: #fff; border-radius: 2px; font-size: 14px; color: #999; text-align: center; line-height: 30px; cursor: pointer; border: 1px solid #999; box-sizing: border-box; margin-right: 12px; } .lasso-canvas-container { width: 500px; height: 400px; display: flex; justify-content: center; align-items: center; background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC"); } .lasso-canvas { width: 500px; height: 400px; } .shadow-canvas { width: 500px; height: 400px; position: fixed; left: -999999px; top: -999999px; } .result-img { width: 500px; height: 400px; display: flex; justify-content: center; align-items: center; background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC"); } .result-img img { max-width: 500px; max-height: 400px; } </style> <style> .lasso-img-dialog { width: 544px; } .lasso-img-dialog .el-dialog__body { padding-top: 0; } </style>
posted on 2024-06-27 15:31 hanguahannibk 阅读(25) 评论(0) 编辑 收藏 举报