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 阅读(423) 评论(0) 收藏 举报
浙公网安备 33010602011771号