最近写需求,遇到要给图片加水印的需求。 刚开始想的方案是给图片上覆盖一层水印照片,但是这样的话用户直接下载图片水印也会消失。
后来查资料发现用 canvas 就可以给图片加水印,下面是处理过程。
首先我们要确认图片的格式,我们通过 input 上传的图片格式一般是 File (File 对象是特殊类型的 Blob)即 Blob 格式。
这样的话,我们需要先把 Blob 文件转成 img 标签,先通过 FileReader 读取文件,通过 reader.readAsDataURL 获得文件 Base 64 编码 URL 地址,拿到 URL 后,生成 img 标签。
// blob 文件格式转成 img 标签
const blobToImg = (blob) => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(blob)
reader.onload = () => {
let img = new Image()
img.src = reader.result
img.addEventListener('load', () => resolve(img))
}
})
}
这里要注意,如果使用 addEventListener 需要先注册事件监听。
const blobToImg = (blob) => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.addEventListener('load', () => {
let img = new Image()
img.src = reader.result
img.addEventListener('load', () => resolve(img))
})
reader.readAsDataURL(blob)
})
}
// 将img内容绘制到canvas画布
imgToCanvas(img) {
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
const context = canvas.getContext('2d')
context.drawImage(img, 0, 0)
return canvas
}
水印通过 ctx.createPattern(image, repetition) 方法来进行重复绘制,由于 image 参数要是是 img 标签格式或者 canvas 文件格式,所以我们可以让 UI 老师生成一个水印的图片,或者我们用 canvas 自己画一个水印的图片进行复制。
如果我们需要直接生成图片来进行展示,那我们可以用 canvas.toDataURL() 直接从 canvas 生成图片地址。
如果需要生成 Blob 格式传给后端,那我们可以用 canvas.toBlob() 方法从 canvas 生成 Blob 文件格式,传给后端。
// canvas画布上绘制水印
waterMark(canvas) {
return new Promise((resolve, reject) => {
const ctx = canvas.getContext('2d')
// 绘制水印 canvas
const canvasWater = this.drawWaterCanvas('已失效')
// 绘制重复的水印
ctx.fillStyle = ctx.createPattern(canvasWater, 'repeat')
ctx.fillRect(0, 0, canvas.width, canvas.height)
// 这里我需要直接展示,所以就直接生成图片地址
resolve(canvas.toDataURL())
})
},
drawWaterCanvas(str) {
const canvasWater = document.createElement('canvas')
canvasWater.width = 400
canvasWater.height = 400
const ctxWater = canvasWater.getContext('2d')
ctxWater.textAlign = 'left'
ctxWater.textBaseline = 'middle'
ctxWater.font = '32px Microsoft Yahei'
ctxWater.fillStyle = 'rgba(0, 0, 0, 0.3)'
ctxWater.rotate((-20 * Math.PI) / 180)
ctxWater.fillText(str, 10, 80)
return canvasWater
}
<template>
<div>
<img :src="imgUrl" alt="" />
div>
template>
<script>
import loppy from './assets/loppy.jpg'
export default {
data() {
return {
imgUrl: ''
}
},
created() {
const img = new Image()
img.src = loppy
img.onload = async () => {
const canvas = this.imgToCanvas(img)
const url = await this.waterMark(canvas)
this.imgUrl = url
}
},
methods: {
// 将img内容绘制到canvas画布
imgToCanvas(img) {
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
const context = canvas.getContext('2d')
context.drawImage(img, 0, 0)
return canvas
},
// canvas画布上绘制水印并转换为blob对象
waterMark(canvas) {
return new Promise((resolve) => {
const ctx = canvas.getContext('2d')
// 绘制水印 canvas
const canvasWater = this.drawWaterCanvas('图片已失效')
// 绘制重复的水印
ctx.fillStyle = ctx.createPattern(canvasWater, 'repeat')
ctx.fillRect(0, 0, canvas.width, canvas.height)
resolve(canvas.toDataURL())
})
},
drawWaterCanvas(str) {
const canvasWater = document.createElement('canvas')
canvasWater.width = 500
canvasWater.height = 500
const ctxWater = canvasWater.getContext('2d')
ctxWater.textAlign = 'left'
ctxWater.textBaseline = 'middle'
ctxWater.font = '32px Microsoft Yahei'
ctxWater.fillStyle = 'rgba(0, 0, 0, 0.3)'
ctxWater.rotate((-20 * Math.PI) / 180)
ctxWater.fillText(str, 10, 200)
ctxWater.fillText(new Date().toLocaleString(), 10, 300)
return canvasWater
}
}
}
script>
<style lang="scss" scoped>
img {
width: 200px;
height: 200px;
}
style>