js 给图片添加水印

news2024/11/15 6:42:13


如何在图片上添加水印?
1、把图片或者图片文件转成image元素
2、把转成的image转成canvas
3、在生成的canvas中添加水印

先看效果


 

1、把图片或者图片文件转成image元素

 

function urlToImg(url) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.addEventListener('load', () => resolve(img));
        img.crossOrigin = 'Anonymous';
        img.src = url;
    })
}
function blobToImg(blob) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.addEventListener('load', () => {
            const img = new Image();
            img.addEventListener('load', () => resolve(img));
            img.crossOrigin = 'Anonymous';
            img.src = reader.result;
        })
        reader.readAsDataURL(blob);
    })
}


2、把转成的image转成canvas

function 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中添加水印

function watermark(canvas, text, font, fillStyle, rotate) {
    return new Promise((resolve, reject) => {
        let context = canvas.getContext('2d');
        context.font = font;
        context.fillStyle = fillStyle;
        context.rotate(rotate * Math.PI / 180);
        context.textAlign = 'center';
        context.textBaseline = 'Middle';
        const textWidth = context.measureText(text).width;
        for (let i = (canvas.height * 0.5) * -1; i < 800; i += (textWidth + (textWidth / 5))) {
            for (let j = 0; j < canvas.height * 1.5; j += 128) {
                // 填充文字,i 间距, j 间距
                context.fillText(text, i, j);
            }
        }
        canvas.toBlob((blob) => resolve(blob));
    })
}


base64转图片文件

function base64ToFile(dataurl, filename = 'image.png') {
    if (!dataurl) {
        return '';
    }
    let arr = dataurl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, {type: mime});
}

生成水印图片

/**
*
  * @param {File | String} image 图片文件或图片url地址
  * @param {String} text 水印文字
  * @param {String} font 字体属性
  * @param {String} fillStyle 字体颜色
  * @param {Number} rotate 旋转角度
*/
export async function addWaterMark(
image,
text = `井底的蜗牛 1234567890 ${getCurDay().year}-${getCurDay().month}-${getCurDay().date}`,
font = '16px microsoft yahei',
fillStyle = 'rgba(0, 0, 0, 0.3)',
rotate = -45
) {

    let img;
    if (typeof (image) === 'string') {
        img = await urlToImg(image);
    } else {
        if (['image/png', 'image/jpeg'].includes(image.type)) {
            img = await blobToImg(image);
        } else {
            return Promise.reject('不支持该类型的文件加水印');
        }
    }
    let canvas = imgToCanvas(img);
    let blob = await watermark(canvas, text, font, fillStyle, rotate);
    let newImg = await blobToImg(blob);
    let src = newImg.src;
    let newFile = base64ToFile(src, 'image.png');
    let newUrl = window.URL.createObjectURL(newFile);

    return {newFile, src, newUrl};
}

完整代码

/**
 *
 * @param {File | String} image 图片文件或图片url地址
 * @param {String} text 水印文字
 * @param {String} font 字体属性
 * @param {String} fillStyle 字体颜色
 * @param {Number} rotate 旋转角度
 */
export async function addWaterMark(
    image,
    text = `井底的蜗牛 1234567890 ${getCurDay().year}-${getCurDay().month}-${getCurDay().date}`,
    font = '16px microsoft yahei',
    fillStyle = 'rgba(0, 0, 0, 0.3)',
    rotate = -45
) {

    let img;
    if (typeof (image) === 'string') {
        img = await urlToImg(image);
    } else {
        if (['image/png', 'image/jpeg'].includes(image.type)) {
            img = await blobToImg(image);
        } else {
            return Promise.reject('不支持该类型的文件加水印');
        }
    }
    let canvas = imgToCanvas(img);
    let blob = await watermark(canvas, text, font, fillStyle, rotate);
    let newImg = await blobToImg(blob);
    let src = newImg.src;
    let newFile = base64ToFile(src, 'image.png');
    let newUrl = window.URL.createObjectURL(newFile);

    return {newFile, src, newUrl};
}

// 图片地址转image
function urlToImg(url) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.addEventListener('load', () => resolve(img));
        img.crossOrigin = 'Anonymous';
        img.src = url;
    })
}

// 图片文件转image
function blobToImg(blob) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.addEventListener('load', () => {
            const img = new Image();
            img.addEventListener('load', () => resolve(img));
            img.crossOrigin = 'Anonymous';
            img.src = reader.result;
        })
        reader.readAsDataURL(blob);
    })
}

// 图片转canvas
function 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;
}

function base64ToFile(dataurl, filename = 'image.png') {
    if (!dataurl) {
        return '';
    }
    let arr = dataurl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], filename, {type: mime});
}

// canvas 添加水印
function watermark(canvas, text, font, fillStyle, rotate) {
    return new Promise((resolve, reject) => {
        let context = canvas.getContext('2d');
        context.font = font;
        context.fillStyle = fillStyle;
        context.rotate(rotate * Math.PI / 180);
        context.textAlign = 'center';
        context.textBaseline = 'Middle';
        const textWidth = context.measureText(text).width;
        for (let i = (canvas.height * 0.5) * -1; i < 800; i += (textWidth + (textWidth / 5))) {
            for (let j = 0; j < canvas.height * 1.5; j += 128) {
                // 填充文字,i 间距, j 间距
                context.fillText(text, i, j);
            }
        }
        canvas.toBlob((blob) => resolve(blob));
    })
}

function getCurDay() {
    var datetime = new Date();
    var year = datetime.getFullYear();
    var month = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;
    var date = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();
    return {
        year,
        month,
        date
    }
}

调用方法

 const res = await addWaterMark('https://images.modao.cc/images/images-2021/landingpage/design/bottom-section-bg-1.svg');
    console.log(res)

注意:有些图片会报跨域问题这个服务器中配置处理跨域,这里不多解释

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/638706.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何实现在线书签内容替换

书签广泛应用于企业的各种办公自动化业务场景中。例如&#xff1a;在范式合同模板中将甲乙方书签自动替换成具体的公司名称&#xff1b;在红头文件模板中将红头标题书签替换成具体的行政指令&#xff1b;在各种协议模板中将协议日期书签替换为当前日期&#xff1b;等等。 在这…

低代码赛道拥挤 生态聚合成为破局关键

在云计算和移动互联网的强劲推动下&#xff0c;企业数字化转型的步伐正在加速&#xff0c;对于软件应用开发的需求也呈现出爆发式的增长。这样的背景下&#xff0c;低代码平台凭借其独特的优势迅速崛起并引发了业界的广泛关注。 自2020年以来&#xff0c;低代码领域已成为投资…

5年测试岗,自动化测试经验总结(真实)他的测试之路高歌猛进...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 8年测试工程师的自…

软考A计划-2023系统架构师-知识点集锦(2/4)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

26键 or 九宫格?不论哪个都不耽误黑客们窃取你的信息!

我妈找我说&#xff0c;可以邮寄杨梅了。我二话不说&#xff0c;直接开始给她我的邮寄地址。快速输入的地址让我想到一个问题&#xff0c;现在的输入法是不是知道的太多了&#xff1f; 我只要打出“浙江省杭州市”&#xff0c;联想出来的就是我所在的市区和街道&#xff0c;下…

【git切换分支/tag】git stash保存暂不提交的更改

目录 问题git stash使用方法git stash pop 还原修改 git stash使用、修改指定tag的代码 其他git指令 问题 情景&#xff1a;分支1上开发新功能&#xff0c;临时切换到其他分支或tag上修改bug。 1、直接切换&#xff1a;如果没有冲突&#xff0c;分支1的修改会带到要切换的分支…

Python-深入不出的字符串

深入不出的字符串 字符串是由独立字符组成的一个序列&#xff0c;通常包含在单引号&#xff08;‘’&#xff09;双引号 &#xff08;“”&#xff09;或者三引号之中&#xff08;‘’’ ‘’或"“” “”"&#xff0c;两者一样&#xff09;&#xff0c;比如下面几种…

感觉被榨干了,被美团拷打一小时...

普通本科毕业后&#xff0c;进了一家互联网公司&#xff0c;这几年里不断在积累经验&#xff0c;最终选择跳到美团&#xff0c;涨薪了50%&#xff0c;下面分享一下我个人的面经和一些心得建议。 面经 面团一面 自我介绍专业技能一条条核对下来 有软件测试流程、用例设计方法…

Layui项目实战

使用语言&#xff1a;C#&#xff0c;Js&#xff0c;Html 使用框架&#xff1a;MVC&#xff0c;Layui 使用插件&#xff1a;JQuery&#xff0c;Layui 一.Layui父窗体前端代码&#xff1a; 1.Html代码&#xff1a; <div class"layui-col-md12" style"paddin…

如今做泛娱乐出海,你需要融云《社交泛娱乐出海作战地图》

出海需要的从来都不是上头&#xff0c;而是专业。关注【融云全球互联网通信云】了解更多 星期四的下午&#xff0c;笔者收到了一份特殊的“投喂”——一份详实到出乎意料的《社交泛娱乐出海作战地图》&#xff08;下称《作战地图》&#xff09;。 内容上&#xff0c;它覆盖了从…

英语、数学不精,还可以选择IT行业吗?

临近毕业季择校季&#xff0c;又有一群小伙伴面临着无法确定专业选择方向的难题。 就IT行业来说&#xff0c;有很多同学会有这样的疑问&#xff1a; 对计算机专业的不是十分了解&#xff0c;能学吗&#xff1f; 之前数学不太好&#xff0c;怎么学编程&#xff1f; 学编程对…

Python 接口测试之Excel表格数据操作方法封装

引言 我们在做接口测试&#xff0c;经常会用到excel去管理测试数据&#xff0c;对Excel的操作比较频繁&#xff0c;那么使用python如何操作Excel文件的读与写呢&#xff1f;由于之前讲的都是大的框框&#xff0c;没有讲这么小的模块使用&#xff0c;现在就化整为0的讲解。 读…

黑客工具软件大全

黑客工具软件大全100套 给大家准备了全套网络安全梓料&#xff0c;有web安全&#xff0c;还有渗透测试等等内容&#xff0c;还包含电子书、面试题、pdf文档、视频以及相关的网络安全笔记 &#x1f447;&#x1f447;&#x1f447; 《黑客&网络安全入门&进阶学习包》 &a…

深度学习笔记之Seq2seq(三)注意力机制的执行过程

深度学习笔记之Seq2seq——注意力机制的执行过程 引言回顾&#xff1a;经典 Seq2seq \text{Seq2seq} Seq2seq模型中 Context \text{Context} Context向量的缺陷注意力机制的动机 Seq2seq \text{Seq2seq} Seq2seq中的 Attention \text{Attention} Attention结构注意力模型的数学…

chatgpt赋能python:Python如何合并集合?

Python如何合并集合&#xff1f; 在Python编程中&#xff0c;合并多个集合是一项常见的任务。集合合并在数据处理和分析领域中极为常见&#xff0c;例如在合并用户数据时&#xff0c;需要将多个相同字段的集合合并为一个完整的集合。 本文将介绍如何在Python中合并集合&#…

【语音之家】AI产业沙龙 —— 解读火山语音团队在国际顶会ACL2023的创新突破

由CCF语音对话与听觉专委会 、中国人工智能产业发展联盟&#xff08;AIIA&#xff09;评估组、火山语音、语音之家、希尔贝壳共同主办的【语音之家】AI产业沙龙——解读火山语音团队在国际顶会ACL2023的创新突破&#xff0c;将于2023年6月14日19:00-20:20线上直播。 沙龙简介 …

JVM中方法区、永久代、元空间详解以及关系?

首先我们需要先复习一下jvm的大致内存图&#xff0c;如下&#xff1a; 哦~ &#xff0c;想起来了&#xff0c;原来方法区属于jvm的运行时数据区&#xff0c;且作用就是存储类信息、方法信息、常量池信息等静态数据。 &#xff08;补充一下&#xff1a;运行时数据区中的红色是指…

​跨部门网络搭建,核心在这30行里

大家好&#xff0c;我的网工朋友。 在企业网络中&#xff0c;想要实现跨部门的VLAN互联互通&#xff0c;其实有很多方式。 你可以通过子接口实现&#xff0c;也可以通过VLAN-Interface实现。但在实际工作中&#xff0c;很多网工朋友&#xff0c;遇到这种情况&#xff0c;很容…

【Selenium2+python】自动化unittest生成测试报告

前言 批量执行完用例后&#xff0c;生成的测试报告是文本形式的&#xff0c;不够直观&#xff0c;为了更好的展示测试报告&#xff0c;最好是生成HTML格式的。 unittest里面是不能生成html格式报告的&#xff0c;需要导入一个第三方的模块&#xff1a;HTMLTestRunner 一、导…

java小技能: 使用response方式下载txt文件(使用response的方式进行文件的传输)

文章目录 引言I 后端代码1.1 思路1.2 封装1.3 用法1.4 防止文件名称中文乱码1.5 指示哪些报头可以公开 Access-Control-Expose-Headers1.6 创建目录II 前端下载文件引言 项目需求:进行txt文件的下载 其他思路:写好文件在下载的方式 本文思路:使用response的方式进行文件的传…