引入canvas 和cropperjs
$ npm install canvas -s
$ npm install cropperjs -s
代码
<template>
<div class="hello">
<!-- 画布 -->
<div id="huabuDiv">
<div style="width:1200px;margin-top:60px" title="画图">
<!--操作 -->
字体大小:
<el-select
size="mini"
style="width:8%;back-ground:blue;height:28px"
v-model="fontSize"
placeholder="请选择字体大小"
>
<el-option label="12" value="12"></el-option>
<el-option label="16" value="16"></el-option>
<el-option label="20" value="20"></el-option>
<el-option label="24" value="24"></el-option>
<el-option label="28" value="28"></el-option>
<el-option label="32" value="32"></el-option>
<el-option label="48" value="48"></el-option>
<el-option label="64" value="64"></el-option> </el-select
> 每行字数:
<el-input
size="mini"
style="width:8%"
v-model="fontMount"
placeholder="请选择每行字数"
/>
<el-button type="primary" size="mini" @click="shuaxinhuabu"
>重置</el-button
>
<el-button
type="primary"
size="mini"
:disabled="this.canvasIndex == 0"
@click="changeCanvas(1)"
>上一步</el-button
>
<el-button
type="primary"
size="mini"
:disabled="this.canvasIndex >= this.canvasData.length"
@click="changeCanvas(2)"
>下一步</el-button
>
<el-button type="primary" size="mini" @click="getType(1)"
>文字</el-button
>
<el-button type="primary" size="mini" @click="getType(2)"
>椭圆</el-button
>
<el-button type="primary" size="mini" @click="getType(3)"
>方框</el-button
>
<el-button type="primary" size="mini" @click="xuanzhuan(1)"
>左旋</el-button
>
<el-button type="primary" size="mini" @click="xuanzhuan(2)"
>右旋</el-button
>
<el-button type="primary" size="mini" @click="initCropper()"
>裁剪</el-button
>
<el-button type="primary" size="mini" @click="saveCropImg()"
>确认</el-button
>
<el-button type="primary" size="mini" @click="saveHuabu()"
>保存</el-button
>
<br />
<!-- 画布 -->
<canvas
magin-top=""
@mousedown="canvasDown"
@mouseup="canvasUp"
@click="huoquzuobiao"
id="myCanvas"
ref="myCanvas"
style="z-index: 1;align-content:center;border: 1px solid red;background-color:#ead3d3;"
>您的浏览器不支持canvas标签。</canvas
>
<textarea
@keyup.enter="luruFont"
id="fontInput"
style="z-index: 2;height:30px;display:none;border:2px solid groove;position:absolute;"
></textarea>
</div>
</div>
</div>
</template>
<script>
import Cropper from "cropperjs";
export default {
name: "JcbgMiddle",
components: {},
data() {
return {
canvasIndex: 0,
canvasData: [],
fontMount: 10,
// 截图模型
cropper: "",
// 画布弹窗
dialogds: true,
// 图片地址
imgUrl: "http://lwww.baidu.com" //图片地址,
// 文字大小
fontSize: 16,
// 文字录入坐标
intputFontX: "",
intputFontY: "",
fontX: "",
fontY: "",
// 图片坐标
picX: "",
picY: "",
// 操作类型
caozuoType: "",
// 保存的画布
imgBase64: "",
imgBeginUrl: "",
// 画布对象
canvas: "",
jiaodu: 0,
// 截图所需
beginRec: {
x: "",
y: ""
},
// 鼠标移动事件
canvasMoveUse: false,
// 画布弹窗是否展示
huatuPic: false,
// 修改检查图片弹窗
dialogFormPic: false,
// 图片模型
img: ""
};
},
props: ["callbackdata"],
mounted() {
// 初始隐藏 dialog 第一次打开无法获取其内元素
this.dialogds = false;
this.$nextTick(function() {
this.shuaxinhuabu();
});
},
methods: {
// 弹窗关闭之前的确认
handleClose(done) {
this.$confirm("确认关闭?")
.then(_ => {
done(_);
})
.catch(_ => {
console.log(_);
});
},
// 画布的上一步
spliceCanvasDaata() {
let datalength = this.canvasData.length;
if (this.canvasIndex < datalength) {
this.canvasData = this.canvasData.splice(0, this.canvasIndex - 1);
console.log("is use method spliceCanvasData");
this.canvasIndex--;
}
},
// 画图操作上一步 下一步
changeCanvas(val) {
console.log(this.canvasIndex);
// 上一步
if (val == 1) {
if (this.canvasIndex == 1) {
this.shuaxinhuabu();
this.canvasIndex--;
}
if (this.canvasIndex > 1) {
this.canvasIndex--;
let canvasNow = this.canvasData[this.canvasIndex - 1];
let myCanvas = this.$refs.myCanvas;
myCanvas.width = canvasNow.width;
myCanvas.height = canvasNow.height;
let ctx = myCanvas.getContext("2d");
ctx.putImageData(canvasNow, 0, 0);
ctx.save();
}
}
// 下一步
if (val == 2) {
this.canvasIndex++;
let canvasNow = this.canvasData[this.canvasIndex - 1];
let myCanvas = this.$refs.myCanvas;
myCanvas.width = canvasNow.width;
myCanvas.height = canvasNow.height;
let ctx = myCanvas.getContext("2d");
ctx.putImageData(canvasNow, 0, 0);
ctx.save();
}
},
// 初始化截图对象
initCropper() {
if (this.cropper != "") {
this.cropper.destroy();
}
var image = this.$refs.myCanvas;
this.dialogVisible = true;
var cropper = new Cropper(image, {
background: false,
zoomable: false,
ready: function() {
self.croppable = true;
}
});
this.cropper = cropper;
},
// 保存截图到画布
saveCropImg() {
this.imgBeginUrl = this.cropper
.getCroppedCanvas()
.toDataURL("image/jpeg");
var canvas = this.$refs.myCanvas;
if (canvas.getContext) {
//获取对应的CanvasRenderingContext2D对象(画笔)
let ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
//创建新的图片对象
var img = new Image();
//指定图片的URL
//浏览器加载图片完毕后再绘制图片
img.src = this.cropper.getCroppedCanvas().toDataURL("image/jpeg");
img.crossOrigin = "anonymous";
img.onload = function() {
//以Canvas画布上的坐标(10,10)为起始点,绘制图像
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(
img,
canvas.width / 2 - img.width / 2,
canvas.height / 2 - img.height / 2
);
};
this.img = img;
this.canvas = canvas;
this.cropper.destroy();
let imgdata = ctx.getImageData(0, 0, canvas.width, canvas.height);
this.spliceCanvasDaata();
this.canvasData.push(imgdata);
this.canvasIndex++;
}
},
// 提交修改
saveHuabu() {
const image = document.getElementById("myCanvas");
image.setAttribute("crossOrigin", "anonymous");
var src = image.toDataURL("image/png");
this.imgBase64 = src;
var blob = this.dataURItoBlob(src);
let dataform = new FormData();
dataform.append("jcjgPicFile", blob);
dataform.append("xiangmuId", this.jcjgPicId);
this.$axios
.post("http://www.baidu.com", dataform, {
"Content-Type": "multipart/form-data"
})
.then(response => {
if (response.data.code == "000000") {
alert("修改成功");
this.dialogFormPic = false;
this.getJianchabaogao();
this.jcjgPicId = "";
this.dialogds = false;
}
});
},
// 转换画布对象为Blob 准备上传的数据
dataURItoBlob(base64Data) {
var byteString;
if (base64Data.split(",")[0].indexOf("base64") >= 0)
byteString = atob(base64Data.split(",")[1]);
else byteString = unescape(base64Data.split(",")[1]);
var mimeString = base64Data
.split(",")[0]
.split(":")[1]
.split(";")[0];
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], { type: mimeString });
},
//旋转图片的方法
xuanzhuan(val) {
let context = this.$refs.myCanvas.getContext("2d");
this.dialogVisible = true;
if (val == 1) {
this.jiaodu += 3;
}
if (val == 2) {
this.jiaodu += 1;
}
let canvas = this.$refs.myCanvas;
context.save();
let a = canvas.width;
canvas.width = canvas.height;
canvas.height = a;
context.clearRect(0, 0, canvas.width, canvas.height);
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate((this.jiaodu / 2) * Math.PI);
context.translate(-canvas.width / 2, -canvas.height / 2);
context.drawImage(
this.img,
canvas.width / 2 - this.img.width / 2,
canvas.height / 2 - this.img.height / 2
);
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate((-this.jiaodu / 2) * Math.PI);
context.translate(-canvas.width / 2, -canvas.height / 2);
this.canvas = canvas;
context.restore();
context.save();
let imgdata = context.getImageData(0, 0, canvas.width, canvas.height);
this.spliceCanvasDaata();
this.canvasData.push(imgdata);
this.canvasIndex++;
},
//鼠标点下的事件
canvasDown(e) {
console.log(e.offsetX, e.offsetY);
this.canvasMoveUse = true;
// client是基于整个页面的坐标,offset是cavas距离pictureDetail顶部以及左边的距离
const canvasX = e.offsetX;
const canvasY = e.offsetY;
// 记录起始点和起始状态
this.beginRec.x = canvasX;
this.beginRec.y = canvasY;
// this.beginRec.imageData = this.context.getImageData(0, 0, this.width, this.height)
// 存储本次绘制坐标信息
let context = this.$refs.myCanvas.getContext("2d");
context.beginPath();
context.save();
},
//鼠标抬起的事件
canvasUp(e) {
const canvasX = e.offsetX;
const canvasY = e.offsetY;
let a = (canvasX + this.beginRec.x) / 2;
let b = (canvasY + this.beginRec.y) / 2;
let c = (canvasX - this.beginRec.x) / 2;
if (c < 0) {
c = -c;
}
let d = c / 2;
let l = canvasX - this.beginRec.x;
let f = canvasY - this.beginRec.y;
if (l < 0) {
l = -l;
}
if (f < 0) {
f = -f;
}
let context = this.$refs.myCanvas.getContext("2d");
if (this.caozuoType == 2) {
context.ellipse(a, b, c, d, 0, 0, Math.PI * 2);
context.strokeStyle = "#FFFFFF";
context.lineWidth = 3;
context.stroke();
let imgdata = context.getImageData(
0,
0,
this.$refs.myCanvas.width,
this.$refs.myCanvas.height
);
this.spliceCanvasDaata();
this.canvasData.push(imgdata);
this.canvasIndex++;
}
if (this.caozuoType == 3) {
context.rect(this.beginRec.x, this.beginRec.y, l, f);
context.strokeStyle = "#FFFFFF";
context.lineWidth = 3;
context.stroke();
let imgdata = context.getImageData(
0,
0,
this.$refs.myCanvas.width,
this.$refs.myCanvas.height
);
this.spliceCanvasDaata();
this.canvasData.push(imgdata);
this.canvasIndex++;
}
context.save();
},
//获取画笔类型
getType(val) {
this.caozuoType = val;
},
//添加文字
luruFont() {
let ctmyCanvas = this.$refs.myCanvas;
var stringa = document.getElementById("fontInput").value;
console.log(stringa);
console.log(stringa.split("/s/g"));
document.getElementById("fontInput").style =
"z-index: 2;display:none;border:2px solid groove;position:absolute;";
var context = ctmyCanvas.getContext("2d");
let fontStr = "" + this.fontSize + "px bold 宋体";
context.font = fontStr;
context.fillStyle = "#FFFFFF";
let str = document.getElementById("fontInput").value;
let fontmount = parseInt(this.fontMount);
for (let i = 0; i < str.length; i += fontmount) {
context.fillText(
str.substring(i, i + fontmount),
this.beginRec.x,
this.beginRec.y + 15 + (i / fontmount) * (parseInt(this.fontSize) + 2)
);
}
// context.fillText(
// document.getElementById("fontInput").value,
// this.beginRec.x,
// this.beginRec.y + 15
// );
context.save();
let imgdata = context.getImageData(
0,
0,
ctmyCanvas.width,
ctmyCanvas.height
);
this.spliceCanvasDaata();
this.canvasData.push(imgdata);
this.canvasIndex++;
console.log(this.canvasIndex);
console.log(this.canvasData);
},
//点击画布事件
huoquzuobiao(e) {
if (this.caozuoType == 1) {
this.fontX = e.offsetX;
this.fontY = e.offsetY;
(this.intputFontX = e.offsetX), (this.intputFontY = e.offsetY);
document.getElementById("fontInput").style =
"z-index:11111;border:2px solid groove;position:absolute;left:" +
(e.offsetX + (1200 - this.canvas.width) / 2) +
"px;top:" +
(e.offsetY + 110) +
"px;height:+" +
this.fontSize * 3 +
"px; font-size:" +
this.fontSize +
"px;";
// document.getElementById("fontInput").style="float-left:"+this.fontX+";float-top:"+this.fontY+";border:2px solid groove;relative:absolute;left:"+this.fontX+";top:"+this.fontY+";opacity:.5"
}
},
//重置画布
shuaxinhuabu() {
this.jiaodu = 0;
var canvas = this.$refs.myCanvas;
if (canvas.getContext) {
//获取对应的CanvasRenderingContext2D对象(画笔)
let ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
//创建新的图片对象
var img = new Image();
//指定图片的URL
//浏览器加载图片完毕后再绘制图片
img.src = this.imgUrl + "?timeStamp=" + new Date().valueOf();
img.crossOrigin = "anonymous";
img.onload = function() {
//在Canvas画中间布绘制图像
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(
img,
canvas.width / 2 - img.width / 2,
canvas.height / 2 - img.height / 2
);
};
this.img = img;
this.canvas = canvas;
}
},
// 文件上传之选择文件并赋值
getFile(event) {
this.jianchajieguoPicBean.jcjgPicFile = event.target.files[0];
},
// 文件上传
formCommitPic() {
let dataform = new FormData();
dataform.append("jcjgPicFile", this.jianchajieguoPicBean.jcjgPicFile);
dataform.append("xiangmuId", this.jianchajieguoPicBean.jcjgPicId);
this.$axios
.post(
"http://www.baidu.com",//上传地址
dataform,
{ "Content-Type": "multipart/form-data" }
)
.then(response => {
if (response.data.code == "000000") {
alert("修改成功");
this.dialogFormPic = false;
this.getJianchabaogao();
this.jianchajieguoPicBean = {
jcjgPicFile: "",
jcjgPicId: ""
};
}
});
},
// 修改图片
updateJianchaPic(val, picID) {
this.canvasIndex = 0;
this.canvasData = [];
this.jcjgPicId = picID;
document.getElementById("huabuDiv").style = "display:block";
this.dialogds = true;
this.huatuPic = true;
this.imgUrl = this.zhuanhuanwangzhi(val);
this.shuaxinhuabu();
}
}
};
</script>
<style >
</style>
注意
需要解决文件的跨域问题,代码内目前只做了文字、椭圆、方形、图片裁剪和旋转的处理,并且可能存在bug,只是记录一下,有类似需求的同学可以在此基础上做自己的业务功能