第一种(较简单)
- 依据图片门路获取图片数据,转成blob类型
- 用
FileReader
读取图片blob数据为dataURL - 创立img标签,src属性为dataURL
- 监听
img.onload
, 创立canvas,将img对象draw
在canvas里 - 增加水印
- 应用
canvas.toBlob
转成最终图像
代码
/** * * @param {图片门路} imgUrl * @param {图片宽度} width * @param {图片高度} height * @param {水印文字} watermarkText */async function watermark( imgUrl, width = 300, height = 300, watermarkText = "zpfei.ink") { // 1. 依据图片门路获取图片数据,转成blob类型 const fileBlob = await fetch(imgUrl) .then((r) => r.blob()) .then((file) => file); // 2. 用`FileReader`读取图片blob数据为dataURL const reader = new FileReader(); reader.readAsDataURL(fileBlob); // 3. 创立img标签,src属性为dataURL const tempImg = await new Promise((resolve) => { reader.onload = () => { const img = document.createElement("img"); img.src = reader.result; resolve(img); }; }); // 4. 监听`img.onload`, 创立canvas,将img对象`draw`在canvas里 const canvas = await new Promise((resolve) => { const canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; tempImg.onload = () => { const ctx = canvas.getContext("2d"); ctx.drawImage(tempImg, 0, 0); // 5. 增加水印 ctx.fillStyle = "red"; ctx.textBaseline = "middle"; ctx.fillText(watermarkText, 20, 20); resolve(canvas); }; }); // 6. 应用`canvas.toBlob`转成最终图像 const newImg = await new Promise((resolve) => { canvas.toBlob((canvasBlob) => { const newImg = document.createElement("img"), url = URL.createObjectURL(canvasBlob); newImg.onload = function () { // 图片加载实现后销毁objectUrl URL.revokeObjectURL(url); }; newImg.src = url; resolve(newImg); }); }); // document.body.appendChild(newImg); return newImg;}
const imgUrl = "https://cdn.jsdelivr.net/gh/zhangpanfei/static@demo/img/test.jpg";fetch(imgUrl) .then((r) => r.blob()) .then((file) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { const canvas = document.createElement("canvas"); canvas.width = 400; canvas.height = 400; const img = document.createElement("img"); img.src = reader.result; img.onload = () => { const ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); ctx.fillStyle = "red"; ctx.textBaseline = "middle"; ctx.fillText("zpfei.ink", 20, 20); canvas.toBlob((canvasBlob) => { const newImg = document.createElement("img"), url = URL.createObjectURL(canvasBlob); newImg.onload = function () { // 图片加载实现后销毁objectUrl URL.revokeObjectURL(url); }; newImg.src = url; document.body.appendChild(newImg); }); }; }; });
第二种(简略)
- 图片门路转成canvas
- canvas增加水印
- canvas转成img
代码
/** * 图片门路转成canvas * @param {图片url} url */async function imgToCanvas(url) { // 创立img元素 const img = document.createElement("img"); img.src = url; img.setAttribute("crossOrigin", "anonymous"); // 避免跨域引起的 Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported. await new Promise((resolve) => (img.onload = resolve)); // 创立canvas DOM元素,并设置其宽高和图片一样 const canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; // 坐标(0,0) 示意从此处开始绘制,相当于偏移。 canvas.getContext("2d").drawImage(img, 0, 0); return canvas;}/** * canvas增加水印 * @param {canvas对象} canvas * @param {水印文字} text */function addWatermark(canvas, text) { const ctx = canvas.getContext("2d"); ctx.fillStyle = "red"; ctx.textBaseline = "middle"; ctx.fillText(text, 20, 20); return canvas;}/** * canvas转成img * @param {canvas对象} canvas */function convasToImg(canvas) { // 新建Image对象,能够了解为DOM var image = new Image(); // canvas.toDataURL 返回的是一串Base64编码的URL // 指定格局 PNG image.src = canvas.toDataURL("image/png"); return image;}// 运行示例async function run() { const imgUrl = "https://cdn.jsdelivr.net/gh/zhangpanfei/static@demo/img/test.jpg"; // 1.图片门路转成canvas const tempCanvas = await imgToCanvas(imgUrl); // 2.canvas增加水印 const canvas = addWatermark(tempCanvas, "zpfei.ink"); // 3.canvas转成img const img = convasToImg(canvas); // 查看成果 document.body.appendChild(img);}