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 Echarts柱状图label优化中问题针对讲解

    JavaScript Echarts柱状图label优化中问题针对讲解

    这篇文章主要介绍了JavaScript Echarts柱状图label优化中问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2022-12-12
  • JS Canvas接口和动画效果大全

    JS Canvas接口和动画效果大全

    这篇文章主要介绍了JS Canvas接口和动画效果大全,对Canvas感兴趣的同学,可以参考下
    2021-04-04
  • js控制div弹出层实现方法

    js控制div弹出层实现方法

    这篇文章主要介绍了js控制div弹出层实现方法,可实现点击链接弹出div浮动层,且背景色变暗的效果,是一款非常实用的特效源码,需要的朋友可以参考下
    2015-05-05
  • JavaScript实现选择框按比例拖拉缩放的方法

    JavaScript实现选择框按比例拖拉缩放的方法

    这篇文章主要介绍了JavaScript实现选择框按比例拖拉缩放的方法,具有等比例缩放及设置最小范围等功能,涉及javascript事件的添加、监听、页面元素动态操作及鼠标事件的响应等技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-08-08
  • js实现汉字排序的方法

    js实现汉字排序的方法

    这篇文章主要介绍了js实现汉字排序的方法,涉及javascript针对中文编码的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-07-07
  • 使用JavaScript脚本判断页面是否在微信中被打开

    使用JavaScript脚本判断页面是否在微信中被打开

    这篇文章主要介绍了使用JavaScript脚本判断网页是否在微信中被打开的方法,在各种使用微信参加活动的移动版页面上经常可以用到,需要的朋友可以参考下
    2016-03-03
  • weUI应用之JS常用信息提示弹层的封装

    weUI应用之JS常用信息提示弹层的封装

    这篇文章主要为大家详细介绍了weUI应用中利用JS封装几个常用的信息提示弹层的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-11-11
  • js随机生成26个大小写字母

    js随机生成26个大小写字母

    这篇文章主要为大家详细介绍了javascript随机生成26个大小写字母,感兴趣的朋友可以参考一下
    2016-02-02
  • 浅谈JavaScript超时调用和间歇调用

    浅谈JavaScript超时调用和间歇调用

    JavaScript是单线程语言,但它允许通过设置超时值和间歇时间值来调度代码在特定的时刻执行。前者是在指定的时间过后执行代码,而后者则是每隔指定的时间就执行一次代码。
    2015-08-08
  • js为数字添加逗号并格式化数字的代码

    js为数字添加逗号并格式化数字的代码

    数字添加逗号的方法有很多,在本将为大家介绍下使用js来实现,具体如下,感兴趣的朋友可以参考下,希望对大家有所帮助
    2013-08-08

最新评论