在前端中, 如何巧用 canvas(画布) 为图片加水印? 下面本篇文章就来给大家介绍一下使用 canvas 给图片添加水印的方法. 有一定的参考价值, 有需要的朋友可以参考一下, 希望对大家有所帮助.
在前端中给网页图片添加水印是个常见的功能, 也是互联网内容作者保护自己版权的方法之一. 本文简单记录一下借助 canvas 在前端实现图片添加水印的实现方法.
canvas 元素其实就是一个画布, 我们可以很方便地绘制一些文字, 线条, 图形等, 它也可以将一个 img 标签里渲染的图片画在画布上.
在上传文件到后端的时候, 使用 input 标签读取用户本地文件后得到的其实是一个对象 (更精确的说是 File 对象, 特殊的 Blob 对象); 而在页面上展示一个图片使用的是 img 标签; 绘制功能用 canvas 实现. 添加水印的功能需要在 img 标签, canvas 画布, Blob 对象这三者之间相互转换, 通过一些 API 可以完成这个工作:
我们可以从本地读取图片 Blob, 然后渲染到 img 标签, 使用 canvas 绘制 img 内容并且绘制水印内容到画布, 再将 canvas 内容转为 Blob 对象上传服务器, 这样就完整实现了图片 + 水印的功能.
一, 本地读取图像文件渲染到 img 标签
本地读取图片文件将会得到一个 Blob 对象, 我们可以借助 FileReader.readAsDataURL 方法读取 Blob 的内容, 并得到一个 Base64 编码的文件内容, 可以将该内容赋值给 img.src 从而在浏览器上渲染出本地的图像. 当然, img 并非必须渲染到 DOM 树. 读取操作是个异步操作, 读取完成会触发 load 事件, 为了便于之后的调用, 我们可以用一个 Promise 包装这个操作, 最后返回一个 Promise 对象.
- function blobToImg (blob) {
- return new Promise((resolve, reject) => {
- let reader = new FileReader()
- reader.addEventListener('load', () => {
- let img = new Image()
- img.src = reader.result
- img.addEventListener('load', () => resolve(img))
- })
- reader.readAsDataURL(blob)
- })
- }
二, 将 img 标签内容绘制到 canvas 画布
调用 canvas 元素画布上下文对象的方法即可实现将 img 内容绘制到画布.
- function imgToCanvas (img) {
- let canvas = document.createElement('canvas')
- canvas.width = img.width
- canvas.height = img.height
- let ctx = canvas.getContext('2d')
- ctx.drawImage(img, 0, 0)
- return canvas
- }
这个方法可以传入多个参数, 以定义绘制的图像的范围, 这里传入的 0, 0 定义从图像左上角开始绘制, 后面可以继续传入两个参数来定义图像的绘制终点, 不过这里整个图片都要绘制到 canvas, 所以采用默认值即可.
三, canvas 画布上绘制水印并转换为 Blob 对象
在图片上传的时候, 我们通常采用 FormData, 图片文件以一个 Blob 对象的形式放到 FormData 中, 所以我们需要把 canvas 再转为 Blob 以便文件上传等操作. 利用 htmlCanvasElement.toBlob 方法:
- function watermark (canvas, text) {
- return new Promise((resolve, reject) => {
- let ctx = canvas.getContext('2d')
- // 设置填充字号和字体, 样式
- ctx.font = "24px 宋体"
- ctx.fillStyle = "#FFC82C"
- // 设置右对齐
- ctx.textAlign = 'right'
- // 在指定位置绘制文字, 这里指定距离右下角 20 坐标的地方
- ctx.fillText(text, canvas.width - 20, canvas.height - 20)
- canvas.toBlob(blob => resolve(blob))
- })
- }
四, 图片添加水印完整使用例子
将以上三个步骤结合起来, 就完整地实现了从图片添加水印, 下面是一个简单的使用示例: 从本地选择一个图片文件, 然后添加水印后, 在传入的 dom 元素下预览添加水印后的图片.
- function imgWatermark (dom, text) {
- let input = document.createElement('input')
- input.setAttribute('type', 'file')
- input.setAttribute('accept', 'image/*')
- input.onchange = async () => {
- let img = await blobToImg(input.files[0])
- let canvas = imgToCanvas(img)
- let blob = await watermark(canvas, text)
- // 此处将 Blob 读取到 img 标签, 并在 dom 内渲染出来; 如果是上传文件, 可以将 blob 添加到 FormData 中
- let newImage = await blobToImg(blob)
- dom.appendChild(newImage)
- }
- input.click()
- }
给页面加一个 id 为 container 的 div 元素, 然后如下调用:
- let dom = document.querySelector('#container')
- imgWatermark(dom, '水印文字')
这样就完整地给图片添加了水印效果, 下面看一下实际效果, 你也可以在线体验 http://wintc.top/laboratory/#/watermark .
添加水印前:
添加水印后 (水印内容:"腾冲. 清凉山"):
五, 总结
本文仅仅介绍了图像 + 水印文字的简单实现, 只是做了一些示例性的介绍, 其实可以实现更复杂的水印效果, 比如图像, 半透明或渐变色文字等, 但是原理都是一样的. 另外文中涉及的一些接口其实很有用, 比如一个常见功能是头像上传的预览和剪裁, 这时候你可以利用 FileReader 来读取文件内容预览, 利用 CanvasRenderingContext2D.drawImage 来实现剪裁功能. 关于本文涉及接口的更多详细用法, 可以参照 MDN 文档, 文章中的 API 都使用了链接的形式, 你可以快速查看它们的文档.
本文转载自: https://segmentfault.com/a/1190000021570950
来源: http://www.css88.com/web/html/16103.html