JS VUE 用 canvas 给图片加水印

最近写需求,遇到要给图片加水印的需求。 刚开始想的方案是给图片上覆盖一层水印照片,但是这样的话用户直接下载图片水印也会消失。

后来查资料发现用 canvas 就可以给图片加水印,下面是处理过程。

首先我们要确认图片的格式,我们通过 input 上传的图片格式一般是 File (File 对象是特殊类型的 Blob)即 Blob 格式。

这样的话,我们需要先把 Blob 文件转成 img 标签,先通过 FileReader 读取文件,通过 reader.readAsDataURL 获得文件 Base 64 编码 URL 地址,拿到 URL 后,生成 img 标签。

1. Blob 文件转成 img 标签

js 复制代码
	// 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 需要先注册事件监听。

js 复制代码
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)
      })
    }

2. img 标签转成 canvas

js 复制代码
 // 将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
    }

3. 在 canvas 画布上绘制水印

水印通过 ctx.createPattern(image, repetition) 方法来进行重复绘制,由于 image 参数要是是 img 标签格式或者 canvas 文件格式,所以我们可以让 UI 老师生成一个水印的图片,或者我们用 canvas 自己画一个水印的图片进行复制。

如果我们需要直接生成图片来进行展示,那我们可以用 canvas.toDataURL() 直接从 canvas 生成图片地址。
如果需要生成 Blob 格式传给后端,那我们可以用 canvas.toBlob() 方法从 canvas 生成 Blob 文件格式,传给后端。

js 复制代码
    // 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
    }

4. VUE 中实际应用

html 复制代码
<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>
相关推荐
落花看风雪2 小时前
从零开始搭建后台系统(vue3+TypeScript+Vite)01
typescript·vue·js
行者全栈架构师6 小时前
我让 AI 独立开发了一款游戏:零依赖、188 个测试全绿、工期压到四分之一
typescript·canvas·vibecoding
一只游鱼2 天前
我用原生 JS 做了一个「会呼吸」的博客:Canvas 水境 + 合成音效 + 会破裂的留言气泡(vibe coding)
javascript·agent·canvas·github pages
那年那棵树4 天前
基于WebGis的人生如旅地图系统
前端·arcgis·vue·课程设计
长路 ㅤ   4 天前
从中国城市散点到 3D 全球访客地球:BlogLoom 访客地图的设计与实现
开源项目·数据可视化·canvas·blogloom·访客地图
贾伟康4 天前
【化学实验室|04】HarmonyOS ArkTS 场景选择器实战:让 13 个离线实验的入口、筛选和返回路径保持一致
harmonyos·arkts·canvas·arkui·化学实验
MicrosoftReactor5 天前
技术速递|当聊天框不再是合适的交互界面
ui·ai·copilot·canvas
冒险的豆子7 天前
Vue2 、Vue3 响应式对比及优劣
vue
NetX行者8 天前
用 Vite + Vue3 + UnoCSS 搭建企业级项目模板(一):基础骨架
vue·vite·unocss
贾伟康8 天前
【化学实验室|02】HarmonyOS ArkTS 实验结果载荷实战:用强类型模型隔离页面参数与结果展示
harmonyos·arkts·canvas·arkui·化学实验