JavaScript实现为图片添加水印的方法详解

 更新时间:2023年05月24日 09:24:02   作者:JYeontu  
在很多地方,我们都可以看到,上传图片的时候,图片都会被加上默认的水印,因此,我们在个人网站进行图片操作时,也可以给它加上自己独特的水印,本文就为大家整理了JS添加图片合文字水印的方法,需要的可以参考一下

背景

在很多地方,我们都可以看到,上传图片的时候,图片都会被加上默认的水印,水印的作用主要体现在以下几个方面:

  • 1.版权保护:在商业用途的照片中添加水印可以帮助保护作者的版权,防止他人未经授权使用照片。
  • 2.品牌推广:将商业品牌、商标或公司标志添加到照片中,可以帮助提高品牌知名度和曝光率。
  • 3.防止盗版:添加水印可以防止盗版和未经授权的使用,因为水印会明显表明该照片的版权归原作者所有。
  • 4.标识来源:在社交媒体平台上分享照片时,添加水印可以帮助其他用户识别出照片的来源和作者。
  • 5.识别真伪:对于一些重要的照片或证件,如证书或合同等,加上水印可以帮助识别真伪,防止伪造和篡改。

因此,我们在个人网站进行图片操作时,也可以给它加上自己独特的水印,那么作为一名前端开发,我们该如何实现给图片加上水印呢?

实现

对图片进行处理,我们的首选当然是canvas啦,使用canvas我们可以便捷地对图片进行操作,我们需要操作的图片主要分为以下两种:

  • 1、本地上传的图片
  • 2、线上链接图片

file 转 base64

对于本地上传的图片,我们需要先将其转换成 base64 再进行后续处理:

我们可以通过FileReader来获取图片的 base64,FileReader 是一种异步读取文件机制。

FileReader 提供了如下方法:

  • readAsArrayBuffer(file):按字节读取文件内容,结果用 ArrayBuffer 对象表示
  • readAsBinaryString(file):按字节读取文件内容,结果为文件的二进制串
  • readAsDataURL(file):读取文件内容,结果用 data:url 的字符串形式表示
  • readAsText(file,encoding):按字符读取文件内容,结果用字符串形式表示
  • abort():终止文件读取操作

readAsDataURL 方法会读取指定的 Blob 或 File 对象。并生成 data URl(base64 编码)。这里我们可以使用readAsDataURL来获取上传图片的 base64 编码:

function fileToBase64Async(file) {
  return new Promise((resolve, reject) => {
    let reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = (e) => {
      resolve(e.target.result);
    };
  });
}

使用 canvas 给图片加水印

使用在线图片链接的时候需要注意给图片设置crossOrigin属性(img.setAttribute("crossOrigin",'Anonymous')),不然会出现以下错误:

Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

大概意思是 canvas 无法执行 toDataURL 方法:污染的画布无法输出。受限于 CORS 策略,会存在跨域问题,虽然可以使用图像(比如 append 到页面上)但是绘制到画布上会污染画布,一旦一个画布被污染,就无法提取画布的数据,比如无法使用使用画布 toBlob(),toDataURL(),或 getImageData()方法;当使用这些方法的时候 会抛出一个安全错误。

我们这里可以分为文字水印和图片水印两种:

文字水印

添加文字水印的大致步骤如下:

1、生成一个新的 canvas 画布;

const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;

2、将现有需要添加水印的图片绘制到画布上;

const ctx = canvas.getContext("2d");
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

3、绘制需要添加的水印文本并设置样式。

我们可以使用fillText和strokeText这两个方法来绘制文字,fillText绘制的是默认的普通实线文本,strokeText绘制的是描边文本,这里我使用了strokeText来进行水印文本绘制。

完整代码如下:

const remFontSize = canvas.width / 35;
ctx.font = "bolder " + remFontSize + "px Verdana";
ctx.textAlign = "center";
ctx.strokeStyle = "#fff";
const name = "@JYeontu";
const spaceH = remFontSize * 0.3;
ctx.strokeText(name, canvas.width / 2, canvas.height - remFontSize - spaceH);
function fillTextToImg(base64) {
  const img = new Image();
  img.src = base64;
  img.setAttribute("crossOrigin", "Anonymous");
  return new Promise((resolve, reject) => {
    img.onload = () => {
      const canvas = document.createElement("canvas");
      canvas.width = img.width;
      canvas.height = img.height;
      const ctx = canvas.getContext("2d");
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      const remFontSize = canvas.width / 35;
      ctx.font = "bolder " + remFontSize + "px Verdana";
      ctx.textAlign = "center";
      /**
        ctx.textAlign = "center|end|left|right|start";
        start:默认,文本在指定的位置开始。
        end:文本在指定的位置结束。
        center:文本的中心在指定的位置。
        left:文本左对齐。
        right:文本右对齐。
        **/
      ctx.strokeStyle = "#fff";
      const name = "@JYeontu";
      const spaceH = remFontSize * 0.3;
      ctx.strokeText(
        name,
        canvas.width / 2,
        canvas.height - remFontSize - spaceH
      );
      resolve(canvas.toDataURL("image/jpeg"));
    };
  });
}

效果如下图,左边为原图,右边为加了文字水印的图片:

图片水印

//图片转为base64
async function getImgBase64(base64, width = 50) {
  const img = new Image();
  img.src = base64;
  img.setAttribute("crossOrigin", "Anonymous");
  return new Promise((resolve, reject) => {
    img.onload = () => {
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");
      canvas.width = width;
      canvas.height = (img.height * width) / img.width;
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      resolve(canvas.toDataURL("image/jpeg"));
    };
  });
}
function fillImgToImg(base64, waterMark = imgLink) {
  waterMark =
    "https://img2.baidu.com/it/u=2243573419,589412055&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1684861200&t=7bf0a17ca21ae8ec8aa77b0f98cb4c7e";
  const img = new Image();
  img.src = base64;
  img.setAttribute("crossOrigin", "Anonymous");
  return new Promise((resolve, reject) => {
    img.onload = async () => {
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");
      canvas.width = img.width;
      canvas.height = img.height;
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      const waterMarkSrc = await getImgBase64(waterMark, 100);
      const waterMarkImg = new Image();
      waterMarkImg.src = waterMarkSrc;
      waterMarkImg.setAttribute("crossOrigin", "Anonymous");
      waterMarkImg.onload = () => {
        ctx.drawImage(
          waterMarkImg,
          canvas.width / 2 - waterMarkImg.width / 2,
          canvas.height - waterMarkImg.height - 10,
          waterMarkImg.width,
          waterMarkImg.height
        );
        resolve(canvas.toDataURL("image/jpeg"));
      };
    };
  });
}

效果如下图,左边为原图,右边为加了图片水印的图片: 

完整代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      img {
        width: 500px;
      }
    </style>
  </head>
  <body>
    <input type="file" id="fileUplodBox" />
    <img alt="原图" id="originPic" />
    <img alt="水印图" id="waterMark" />
  </body>
  <script>
    const imgLink =
      "https://img2.baidu.com/it/u=2048195462,703560066&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1684861200&t=a0c977f68632303e7dac2196e8ad2866";
    document.getElementById("originPic").setAttribute("src", imgLink);
    const fileUplodBox = document.getElementById("fileUplodBox");
    fileUplodBox.addEventListener("change", (e) => {
      const file = e.target.files[0];
      dealFile(file);
    });
    test();
    async function test() {
      const img = await fillImgToImg(imgLink);
      document.getElementById("waterMark").setAttribute("src", img);
    }
    async function dealFile(file) {
      const base64 = await fileToBase64Async(file);
      document.getElementById("originPic").setAttribute("src", base64);
      const img = await fillTextToImg(base64);
      document.getElementById("waterMark").setAttribute("src", img);
    }
    function fileToBase64Async(file) {
      return new Promise((resolve, reject) => {
        let reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = (e) => {
          resolve(e.target.result);
        };
      });
    }
    // 给图片加文字水印
    function fillTextToImg(base64) {
      const img = new Image();
      img.src = base64;
      img.setAttribute("crossOrigin", "Anonymous");
      return new Promise((resolve, reject) => {
        img.onload = () => {
          const canvas = document.createElement("canvas");
          const ctx = canvas.getContext("2d");
          canvas.width = img.width;
          canvas.height = img.height;
          ctx.fillRect(0, 0, canvas.width, canvas.height);
          ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
          const remFontSize = canvas.width / 35;
          ctx.font = "bolder " + remFontSize + "px Verdana";
          ctx.textAlign = "center";
          ctx.strokeStyle = "#fff";
          const uploadTime = new Date();
          const name = "@JYeontu";
          const spaceH = remFontSize * 0.3;
          ctx.strokeText(
            name,
            canvas.width / 2,
            canvas.height - remFontSize - spaceH
          );
          resolve(canvas.toDataURL("image/jpeg"));
        };
      });
    }
    async function getImgBase64(base64, width = 50) {
      const img = new Image();
      img.src = base64;
      img.setAttribute("crossOrigin", "Anonymous");
      return new Promise((resolve, reject) => {
        img.onload = () => {
          const canvas = document.createElement("canvas");
          const ctx = canvas.getContext("2d");
          canvas.width = width;
          canvas.height = (img.height * width) / img.width;
          ctx.fillRect(0, 0, canvas.width, canvas.height);
          ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
          resolve(canvas.toDataURL("image/jpeg"));
        };
      });
    }
    // 给图片加图片水印
    function fillImgToImg(base64, waterMark = imgLink) {
      waterMark =
        "https://img2.baidu.com/it/u=2243573419,589412055&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1684861200&t=7bf0a17ca21ae8ec8aa77b0f98cb4c7e";
      const img = new Image();
      img.src = base64;
      img.setAttribute("crossOrigin", "Anonymous");
      return new Promise((resolve, reject) => {
        img.onload = async () => {
          const canvas = document.createElement("canvas");
          const ctx = canvas.getContext("2d");
          canvas.width = img.width;
          canvas.height = img.height;
          ctx.fillRect(0, 0, canvas.width, canvas.height);
          ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
          const waterMarkSrc = await getImgBase64(waterMark, 100);
          const waterMarkImg = new Image();
          waterMarkImg.src = waterMarkSrc;
          waterMarkImg.setAttribute("crossOrigin", "Anonymous");
          waterMarkImg.onload = () => {
            ctx.drawImage(
              waterMarkImg,
              canvas.width / 2 - waterMarkImg.width / 2,
              canvas.height - waterMarkImg.height - 10,
              waterMarkImg.width,
              waterMarkImg.height
            );
            resolve(canvas.toDataURL("image/jpeg"));
          };
        };
      });
    }
  </script>
</html>

以上就是JavaScript实现为图片添加水印的方法详解的详细内容,更多关于JavaScript图片添加水印的资料请关注脚本之家其它相关文章!

相关文章

  • 深入了解JavaScript中的二进制操作及位掩码应用

    深入了解JavaScript中的二进制操作及位掩码应用

    在JavaScript中,二进制操作可以说是一项非常强大和有用的技能,尤其是在处理数据和位掩码时,它们是不可或缺的,本文将介绍JavaScript中的二进制操作,包括什么是二进制以及如何在JavaScript中进行二进制操作
    2023-06-06
  • JS中位置与大小的获取方法

    JS中位置与大小的获取方法

    下面小编就为大家带来一篇JS中位置与大小的获取方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • js读取json文件片段中的数据实例

    js读取json文件片段中的数据实例

    下面小编就为大家带来一篇js读取json文件片段中的数据实例。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • javascript闭包功能与用法实例分析

    javascript闭包功能与用法实例分析

    这篇文章主要介绍了javascript闭包功能与用法,结合具体实例形式深入浅出的分析了javascript中闭包的概念、功能、使用方法与相关注意事项,需要的朋友可以参考下
    2017-04-04
  • 不用typsescript如何使用类型增强功能

    不用typsescript如何使用类型增强功能

    这篇文章主要给大家介绍了关于不用typsescript如何使用类型增强功能的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • JavaScript 函数参数是传值(byVal)还是传址(byRef) 分享

    JavaScript 函数参数是传值(byVal)还是传址(byRef) 分享

    这篇文章主要介绍了在JS中函数参数是传值(byVal)还是传址(byRef)的误区我们通过实例说明一下,有需要的朋友可以参考
    2013-07-07
  • jstree创建无限分级树的方法【基于ajax动态创建子节点】

    jstree创建无限分级树的方法【基于ajax动态创建子节点】

    这篇文章主要介绍了jstree创建无限分级树的方法,结合实例形式分析了jstree基于ajax结合asp.net后台动态创建子节点实现无限分级树效果的相关步骤与操作技巧,需要的朋友可以参考下
    2016-10-10
  • BootstrapTable请求数据时设置超时(timeout)的方法

    BootstrapTable请求数据时设置超时(timeout)的方法

    使用bootstrapTable获取数据时,有时由于网络或者服务器的原因,无法及时获取到数据,页面显示一直处于等待状态。为了改善效果,考虑设置超时,请求发送后超时即显示无数据,过段时间重新发起请求
    2017-01-01
  • js给onclick赋值传参数的两种方法

    js给onclick赋值传参数的两种方法

    这篇文章主要介绍了js给onclick赋值传参数的两种方法,有需要的朋友可以参考一下
    2013-11-11
  • 10个经典的网页鼠标特效代码

    10个经典的网页鼠标特效代码

    小编为广大读者们整理了10个经典的网页鼠标特效代码,并对代码进行了编译和解释,需要的朋友收藏下吧。
    2018-01-01

最新评论