前端图片加水印

3,371 阅读4分钟

概述

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

选用了 ImagePicker 图片选择器

<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在前端实现图片水印合成,在底图上加上想要的水印,核心代码如下

// 获取当前 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 对象

const reader =newFileReader();
reader.readAsDataURL(files);
reader.onload =(e) =>{
    const img = document.createElement('img');
    img.src = reader.result;
}

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

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进行水印的操作

// 设置填充字号和字体,样式
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完成代码如下:

// 移动端REACT 加上水印
readers = (files) => {
  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 () => {
      const ctx = canvas.getContext('2d')
      canvas.width = img.width
      canvas.height = img.height
      ctx.drawImage(img, 0, 0)
      ctx.fillStyle = 'white'
      ctx.textBaseline = 'middle'
      ctx.font = "24px 宋体"
      ctx.fillText('Hello HOSJOY', 20, 20)
      const fileNew = canvas.toDataURL("image/png")
      const promises = await uploadFiles({ base64: fileNew, latitude: '123', longitude: '23' })
      this.setState({
        attachmentUrl: this.state.attachmentUrl.concat({
          url: promises.data.accessUrl  // 生成base64的图片地址
        })
      })
    }
  }
}

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

总结

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