图片水印生成


近期项目需要做一个 定位打卡,拍照加上水印的功能,项目是用的react+Ant mobile

  选用了 ImagePicker 图片选择器,代码如下

1
2
3
4
5
6
7
8
9
10
<ImagePicker
length={4}
disableDelete={true}
files={this.state.attachmentUrl}
onChange={this.onChangeFiles}
onImageClick={(index, fs) => this.openViewer(index, fs)}
selectable={this.state.attachmentUrl.length < 20}
multiple={this.state.multiple}
capture="camera" // 只支持 相机拍照
/>

  在onChange 里面调用方法,就能上传图片显示了,这里不多说介绍,上传图片的原理都差不多。
接下来说下给图片加水印的操作,在激烈的碰撞中,发现可以通过用Canvas,给图片上水印(本次是通过后台接口生成的),之后慢慢的预研了一波。

前端图片加水印原理很简单,主要分为下面几步:

  1. 将需要添加水印的图片绘制到 canvas 上
  2. 将水印图片绘制到 canvas 上
  3. 将 canvas 的内容导出为图片

前期学习

 使用canvas在前端实现图片水印合成

  1.在底图上加上想要的水印,核心代码如下

1
123456789101112// 获取当前 canvas 的上下文环境,用来操作在 canvas 上绘制内容const ctx = canvas.getContext('2d')// 向 canvas 上绘制图片// image 为一个图片对象// x 为绘制图片的横向起始位置,y 为绘制图片的纵向起始位置// width 为要绘制在 canvas 上宽度, height 为高度// 该方法最多可接受 9 个参数,从而实现剪裁的效果,但是与本篇内容无关,感兴趣的小伙伴可以搜索该方法ctx.drawImage(image, x, y, width, height)// 将 canvas 上的内容导出为 base64 格式的字符串,导出后可以直接赋值给 Image 对象的 src 属性// 第一个参数为导出的图片格式,可接受第二个参数(小于或等于 1 的数,表示导出图片的压缩比率)canvas.toDataURL("image/png")context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);

drawImage接受参数示意

参数 描述
img 用来被绘制的图像、画布或视频
sx 可选。img被绘制区域的起始左上x坐标。
sy 可选。img被绘制区域的起始左上y坐标。
swidth 可选。img被绘制区域的宽度(如果没有后面的width或height参数,则可以伸展或缩小图像)。
sheight 可选。img被绘制区域的高度(如果没有后面的width或height参数,则可以伸展或缩小图像)。
x 画布上放置img的起始x坐标。
y 画布上放置img的起始y坐标。
width 可选。画布上放置img提供的宽度(可能会有图片剪裁效果)。
height 可选。画布上放置img提供的高度(可能会有图片剪裁效果)。

步骤

 1、本地图片转成Base64
 在onChange 上传图片 直接获取到的对象包含了base64格式的图片数据,如图:

 2、使用H5 FlieReader 读取base64格式的图片数据,将图片数据赋值给image 对象

1
2
3
4
5
6
const reader = new FileReader();
reader.readAsDataURL(files);
reader.onload = (e) => {
const img = document.createElement('img');
img.src = reader.result;
}

 3、调用canvas元素画布上下文对象的drawImage方法即可实现将img内容绘制到画布

1
2
3
4
5
6
const canvas = document.createElement('canvas');
// 设置画布高宽
canvas.width = img.width
canvas.height = img.height
let ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)

drawImage这个方法可以传入多个参数(楼上已经述说过了),以定义绘制的图像的范围,这里传入的0, 0定义从图像左上角开始绘制,后面可以继续传入两个参数来定义图像的绘制终点,不过这里整个图片都要绘制到canvas,所以采用默认值即可。注意点:要设置画布的高宽,到时候图片需要平铺到整个画布上,不加 的话 大图会被裁剪掉。

 4、在画布上添加水印并借助canvas的toDataURL()方法把我们的canvas画布转换成base64无损PNG地址,这里就可以按照canvas的Api进行水印的操作

1
2
3
4
5
6
7
8
// 设置填充字号和字体,样式
ctx.font = "24px 宋体"
ctx.fillStyle = "#FFC82C"
// 设置右对齐
ctx.textAlign = 'right'
// 在指定位置绘制文字,这里指定距离右下角20坐标的地方
ctx.fillText('HOSJOY', canvas.width - 20, canvas.height - 20)
canvas.toDataURL("image/png")

 5、Demo完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const reader = new FileReader();
reader.readAsDataURL(files);
reader.onload = (e) => {
const img = document.createElement('img');
img.src = reader.result;
const canvas = document.createElement('canvas');
// 如何获取图片高度 宽度

var size = 100;
img.onload = async () => {
console.log(22, img.width)
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// ctx.drawImage(img, 0, 0, size, size, 0,0, size, size); 图片水印
ctx.fillStyle = 'white';
ctx.textBaseline = 'middle';
ctx.font ="24px 宋体"
ctx.fillText('Hello HOSJOY', 20, 20);
// canvas.toBlob(resolve);
// this.setState({
// attachmentUrl: this.state.attachmentUrl.concat({
// url: canvas.toDataURL("image/png"), // 生成base64的图片地址
// })
// })
const fileNew = canvas.toDataURL("image/png")
/这里执行后台上传接口
}
}

 这样就完整地给图片添加了水印效果,下面看一下实际效果,最终结果:

总结

前端给一个图片做简单的水印,canvas提供的一些API其实很有用,前端利用canvas的drawImage()可以实现对图片进行压缩,不妨大家可以试试,既然已经看到了最后,就请你伸出有魔力的手指点个关注吧!谢谢啦


Author: Sky
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Sky !
  TOC