最近有个需求是需要将本地上传的图片添加水印,然后需要在详情中打开预览 上网上搜了一下,加水印的方法大都是通过canvas实现,所以自己折腾了一下成功满足了需求! 首先要有一个上传的按钮,思路我先讲一下,就是你先上传一张图片,action上配置好了上传地址,一般成功后会返回这个图片的信息,比如有的会返回在服务器上的绝对地址,用于上传完成后的预览,这个项目只返回了 fileID 需要通过,也就是 res.obj(至于为啥是这样我也不晓得哪个大神定义的字段,我属于接盘维护项目).拿到这个 fileID 需要通过 this.getImgUrl(res.obj)这个方法(主要是这个图片需要token所以要包装一下)生成绝对地址,这个不用纠结,你完全可以要求后端大佬直接给你绝对地址!!!获取到地址了就可以生成一张图片了撒!
<img style="display:none" ref="images" :src="currentImg">
比如你用一个看不见的img盒子装这个图片用于后面的canvas来画图!当然可以 new image来实现,这样比较直观
然后来准备一个canvas
<canvas style="display:none" ref="canvas" id="canvas" width="800" height="800" ></canvas>
欧了,必要条件准备好了!开始着手画图了!下面这个 uploadSuccess 方法是图片上传成功的回调,不用介绍了吧?
uploadSuccess (res, file, fileList) {
// res 是成功后会返回 res.obj ,通过这个生成完整图片
if (res.code === "9999" && res.success) {
// 这两个是水印的内容,用户名 + 当前时间
let text = this.$store.getters.userInfo.userName
let textDate = this.currentTime
//通过canvas偷偷的加水印
// 当前上传图片的src
this.currentImg = this.getImgUrl(res.obj)
// 这是图片渲染完毕的一个回调
this.$refs.images.onload = () => {
// 开始canvas了 看不懂可以去补补canvas,这个实在没法解释了
var context = this.$refs.canvas.getContext('2d');
this.$refs.canvas.width = 800;
this.$refs.canvas.height = 800;
// 看,就是这个方法 字面意思 0, 0 , 这是图片的坐标, 后面的800 是宽高
context.drawImage(this.$refs.images, 0, 0, 800, 800);
// 设置文字倾斜角度为 -25 度以及样式
context.rotate(-25 * Math.PI / 180);
// 水印字体
context.font = "14px microsoft yahei"
// 水印颜色
context.fillStyle = "rgba(238, 36, 5, 0.1)";
// 水印样式
context.textAlign = 'center';
context.textBaseline = 'Middle';
// 这个地方是为了让水印平铺整个canvas,单条水印賊丑
for(let i = (document.body.offsetHeight*0.5)*-1; i<800; i+=160) {
for(let j = 0; j<document.body.offsetHeight*1.5; j+=60) {
// 填充文字,x 间距, y 间距
context.fillText(text, i, j)
context.fillText(textDate, i + 84, j + 150)
}
}
// 到这一步,canvas已经画好了,你打开canvas的display:block就可以看到是不是成功了
// 这是上传的文件名字
this.fileName = file.name
// 这个方法是 将canvas转blob文件流,用于偷偷的发给后端保存水印图片
this.$refs.canvas.toBlob((blob) => {
// 这个是将 blob转成file文件流
let file = this.blobToFile(blob, this.fileName)
// 原始方法,賊尴尬
var formData = new FormData();
formData.append("file", file);
formData.append("fileName", 'file');
// 原始方法,賊尴尬
var request = new XMLHttpRequest();
request.open("POST", this.uploadUrl);
request.send(formData);
到这一步水印图片已经成功发给后端了,其实就是跟你第一次上传图片干的事情一样
request.onreadystatechange = () => {
this.uploadLoading.close()
// 这个readyState看不懂的麻烦去看看原生状态码
if (request.readyState === 4) {
// 这个 status 看不懂的麻烦去看看原生状态码
if (request.status === 200) {
好了,大功告成,获取到返回值
this.waterFileId = JSON.parse(request.response).obj
this.fileId = res.obj
let imgItem = {
waterFileId: this.waterFileId,
srcUrl: this.getImgUrl(this.waterFileId),
fileId: this.waterFileId,
fileName: this.fileName.substring(0,this.fileName.indexOf(".")),
documentType: this.fileName.split(".").pop().toLowerCase(),
activityId: this.activityId,
activityIdName: this.activityIdName,
fileType: 2,
bizOwnerId: this.bizid,
tableName: this.tableName,
roleNo: this.$store.getters.current_role.roleNo,
zcgljb: this.$store.getters.mgtLevel
}
// 这个 srcList 就是上传好的带水印的图片数组了,用于当前预览
this.srcList.push(imgItem)
this.$emit("uploadSuccess", 'success')
} else {
this.$emit("uploadSuccess", 'fail')
console.log("Error", request.statusText);
}
}
};
})
}
} else {
this.$toast.show(res.msg, 2000);
}
},
// 这个方法也送给你们
blobToFile (newBlob, fileName) {
const files = new window.File(
[newBlob],
fileName, { type: 'image/png' }
)
return files
}
白送一个预览的html
<img v-for="item in srcList" :key="item.imgKey" @click="removeImg(item)" style="width: 80px; height: 80px;margin-right: 12px" :src="item.srcUrl" />
这个就是 上面srcList 生成的图片预览的地方,很清楚了吧?
加油!星空不问赶路人!!!