给图片加水印纯js实现

概要

需求:后端返回的url图片,预览时需要加上水印

// 全局绘制水印参数
objmsg: {
	rotate: 20, //旋转角度  默认20
	fontcolor: "255, 255, 255, 0.8", //字体颜色  rgba类型  默认 255, 255, 255, 0.2(黑色)
	density: 3, //稠密度    数值越大,水印越多
	str: ["文印文字"], //水印文字 数组类型  最大三行(即length<=3)[必传]
}

一、创建XMLHttpRequest对象

源代码如下:

let that = this;
var xhr = new XMLHttpRequest();
xhr.open('get', file.url, true);
// 设置请求头(这一步得设置不然oss图片还是跨域)
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.responseType = 'blob';
xhr.onload = function () {
  if (this.status == 200) {
    let imgFile =  URL.createObjectURL(this.response);
    console.log('this.response',this.response)
    console.log('====================',imgFile)
    that.objmsg.imgpath = imgFile; //需要添加水印图片路径  [必传]
    that.objmsg.domid = "dialogImage"; //图片dom的id   用来更换添加水印后的图片
    drawWaterMark.init(that.objmsg);
  }
};

提示:使用XMLHttpRequest来异步地获取一个图片文件,并将其转换为Blob对象。之后,它创建一个表示该Blob对象的URL,并将其用于绘制水印。下面是对这段代码的详细解释

  1. 创建XMLHttpRequest对象,配置请求,设置请求头,设置响应类型为Blob:
    var xhr = new XMLHttpRequest();
    xhr.open('get', file.url, true);
    xhr.setRequestHeader("Cache-Control", "no-cache");
    xhr.responseType = 'blob';
    
  2. 处理响应:
    xhr.onload = function () {
    	if (this.status == 200) {
            let imgFile = URL.createObjectURL(this.response);
            console.log('this.response', this.response);
            console.log('====================', imgFile);
            that.objmsg.imgpath = imgFile; //需要添加水印图片路径  [必传]
            that.objmsg.domid = "dialogImage"; //图片dom的id   用来更换添加水印后的图片
            drawWaterMark.init(that.objmsg); // 添加水印
        }
    };
    
    当请求完成时,onload事件会被触发。如果响应状态码为200(表示成功),则执行以下操作:
    a. 使用URL.createObjectURL方法,将Blob对象转换为可以直接在浏览器中使用的URL。
    b. 将Blob的URL和响应的Blob对象打印到控制台。
    c. 将imgFile(图片的URL)赋值给that.objmsg.imgpath,这个值之后将用于绘制水印。
    d. 设置that.objmsg.domid为"dialogImage",这可能是用于将添加水印后的图片显示在指定DOM元素上的ID。
    c. 调用drawWaterMark.init(that.objmsg)来开始绘制水印。
  3. 发送请求:
    xhr.send();
    

二、添加水印

提示:这段JavaScript代码的主要功能是给图片添加水印。下面我将详细解释代码的每一部分

// 添加水印源码
var drawWaterMark = {};
drawWaterMark.init = function (objmsg) {
  console.log('--------------',objmsg)
  var canvas = document.createElement('canvas');
  var ctx = canvas.getContext('2d');
  var img = new Image();
  img.src = objmsg.imgpath;
  img.setAttribute("crossOrigin", 'Anonymous');
  img.onload = function () {
      //绘制和图片大小相同的canvas
      canvas.width = img.width;
      canvas.height = img.height;
      //canvas绘制图片,0 0  为左上角坐标原点
      ctx.drawImage(img, 0, 0);
      //绘制水印
      if (objmsg.rotate != undefined && objmsg.rotate != null) {//旋转角度[默认20]
          ctx.rotate((Math.PI / 120) * -objmsg.rotate);
      } else {
          ctx.rotate((Math.PI / 120) * -20);
      };
      var fontsize = 20; // 字体大小[默认20px]
      if (img.width >= 3456) { // 根据图片大小改变水印文案字体大小
          fontsize = 50;
      } else if (img.width >= 2700) {
          fontsize = 30;
      } else if (img.width >= 2000) {
          fontsize = 26;
      } else if (img.width >= 1436) {
          fontsize = 20;
      } else if (img.width >= 800) {
          fontsize = 12;
      } else if (img.width >= 500) {
          fontsize = 10;
      } else {
          fontsize = 8;
      };
      ctx.font = fontsize + "px Microsoft Yahei";
      var fontcolor = '255, 255, 255, 0.2';
      if (objmsg.fontcolor != undefined && objmsg.fontcolor != null) {//字体颜色透明度[默认白色]
          fontcolor = objmsg.fontcolor;
      };
      ctx.fillStyle = "rgba(" + fontcolor + ")";
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      var density = 3;
      if (objmsg.density != undefined && objmsg.density != null) {//稠密度[默认3]
          density = objmsg.density
      };
      let maxpixel;
      let minPixel;
      if(img.width > img.height) {
        maxpixel = img.width;
        minPixel = img.height;
      }else {
        maxpixel = img.height;
        minPixel = img.width
      };
      for (var i = -1000; i < maxpixel; i += minPixel / density) {
          for (var k = 0; k < maxpixel; k += minPixel / density) {
              var str = objmsg.str;
              if (str.length == 1) {
                  ctx.fillText(str[0], i, k);
              } else if(str.length==2){
                  ctx.fillText(str[0], i, k);
                  ctx.fillText(str[1], i, k + (fontsize-0+5));//多行
              } else if (str.length == 3 || str.length > 3) {
                  ctx.fillText(str[0], i, k);
                  ctx.fillText(str[1], i, k + (fontsize - 0 + 5));//多行
                  ctx.fillText(str[2], i, k + (fontsize*2 - 0 + 5));//多行
              }
          }
      };
      var base64 = canvas.toDataURL("image/jpeg");//添加过水印的base64图片
      if (objmsg.domid != undefined && objmsg.domid != null) {//id图片
        let url1 = that.getBase64URL(base64)
        that.dialogImageUrl = url1;
      };
      if (objmsg.cb != undefined && objmsg.cb != null) {//回调函数
          objmsg.cb(base64);//回调函数
      }
  }
};
  1. 变量定义,初始化函数

    let that = this;
    var drawWaterMark = {};
    drawWaterMark.init = function (objmsg) {
        ...
    }
    

    a. 定义了一个空对象drawWaterMark,用于存放与绘制水印相关的功能
    b. 在drawWaterMark对象上定义了一个init方法,它接受一个对象参数objmsg,该对象应该包含绘制水印所需的各种信息,如图片路径、旋转角度、字体大小、字体颜色等。

  2. 创建Canvas和Context,Image对象

    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');
    var img = new Image();
    img.src = objmsg.imgpath;
    img.setAttribute("crossOrigin", 'Anonymous');
    

    a. 使用document.createElement创建了一个新的元素,并通过getContext(‘2d’)获取其2D渲染上下文。
    b. 创建了一个新的Image对象,并设置其src属性为objmsg.imgpath(图片的路径)。crossOrigin属性被设置为’Anonymous’,这用于跨域图片加载。

  3. 当图片加载完成后,onload事件触发,绘制图片到Canvas

    // 绘制和图片大小相同的canvas
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);
    
  4. 设置水印选择角度

    if (objmsg.rotate != undefined && objmsg.rotate != null) {
        ctx.rotate((Math.PI / 120) * -objmsg.rotate);
    } else {
        ctx.rotate((Math.PI / 120) * -20);
    }
    

    根据objmsg.rotate的值(如果存在)旋转Canvas。如果不存在或为空,则默认旋转20度。

  5. 设置水印字体样式(大小,颜色,对齐方式)

    var fontsize = 20;
    if (img.width >= 3456) { // 根据图片大小改变水印文案字体大小
        fontsize = 50;
    } else if (img.width >= 2700) {
        fontsize = 30;
    } else if (img.width >= 2000) {
        fontsize = 26;
    } else if (img.width >= 1436) {
        fontsize = 20;
    } else if (img.width >= 800) {
        fontsize = 12;
    } else if (img.width >= 500) {
        fontsize = 10;
    } else {
        fontsize = 8;
    };
    var fontcolor = '255, 255, 255, 0.2';
    if (objmsg.fontcolor != undefined && objmsg.fontcolor != null) {
        fontcolor = objmsg.fontcolor;
    };
    ctx.font = fontsize + "px Microsoft Yahei";  // 字体大小[默认20px]
    ctx.fillStyle = "rgba(" + fontcolor + ")"; // 字体颜色透明度[默认白色]
    ctx.textAlign = "center"; // 水平对齐方式:居中对齐
    ctx.textBaseline = "middle"; // 垂直对齐方式:居中对齐
    
  6. 绘制水印

    var density = 3;
    if (objmsg.density != undefined && objmsg.density != null) {//稠密度[默认3]
        density = objmsg.density
    };
    let maxpixel;
    let minPixel;
    if(img.width > img.height) {
      maxpixel = img.width;
      minPixel = img.height;
    }else {
      maxpixel = img.height;
      minPixel = img.width
    };
    // 开始绘制水印文字
    for (var i = -1000; i < maxpixel; i += minPixel / density) {
        for (var k = 0; k < maxpixel; k += minPixel / density) {
            var str = objmsg.str;
            if (str.length == 1) {
                ctx.fillText(str[0], i, k);
            } else if(str.length==2){
                ctx.fillText(str[0], i, k);
                ctx.fillText(str[1], i, k + (fontsize-0+5));//多行
            } else if (str.length == 3 || str.length > 3) {
                ctx.fillText(str[0], i, k);
                ctx.fillText(str[1], i, k + (fontsize - 0 + 5));//多行
                ctx.fillText(str[2], i, k + (fontsize*2 - 0 + 5));//多行
            }
        }
    };
    

    a. density 设置水印稠密度,默认为3。值越大绘制的水印越多
    b. maxpixel;minPixel。获取图片的最大值像素值和最小值像素值,方便绘制水印时,确保不会遗落部分地方无水印情况
    c. 使用两层循环来遍历图片的像素区域,并在这个区域内绘制水印文字。

    • 外层循环的变量i从-1000开始,每次增加minPixel / density,直到maxpixel。这里从-1000开始为了确保水印文字不会完全贴着图片的边界绘制。
    • 内层循环的变量k从0开始,每次增加minPixel / density,直到maxpixel。
    • 在每次循环中,获取objmsg.str的值,根据其长度绘制水印文字。
    • 如果str的长度为1,则直接在(i, k)位置绘制这个字符。
    • 如果str的长度为2,则在(i, k)位置绘制第一个字符,在(i, k + (fontsize-0+5))位置绘制第二个字符。这里fontsize-0+5为了调整第二行文字与第一行文字的垂直间距。
    • 如果str的长度为3或更多,则按照类似的方式绘制前三个字符,每个字符都在不同的垂直位置。
  7. 拿到含有水印的base64图片

    var base64 = canvas.toDataURL("image/jpeg");//添加过水印的base64图片
    let url1 = that.getBase64URL(base64); // 获取url图片地址
    

三、将base64转化为url

getBase64URL(pic) {
    const blob = this.base64ImgtoFile(pic)
    const blobUrl = window.URL.createObjectURL(blob);
    return blobUrl
},
base64ImgtoFile (dataurl, filename = 'file') {
    //将base64格式分割:['data:image/png;base64','XXXX']
    const arr = dataurl.split(',')
    // .*? 表示匹配任意字符到下一个符合条件的字符 刚好匹配到:
    // image/png
    const mime = arr[0].match(/:(.*?);/)[1]  //image/png
    //[image,png] 获取图片类型后缀
    const suffix = mime.split('/')[1] //png
    const bstr = atob(arr[1])   //atob() 方法用于解码使用 base-64 编码的字符串
    let n = bstr.length
    const u8arr = new Uint8Array(n)
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
    }
    return new File([u8arr], `${filename}.${suffix}`, {
        type: mime
    })
},
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

F2E_zeke

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值