js 给图片添加水印

2023-10-27


如何在图片上添加水印?
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)

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

js 给图片添加水印 的相关文章

随机推荐

  • stm32 USB CDC 不接电脑无程序一直在USB中断问题

    前些时间基于STM32CUBE 工具做了个用STM32F103的USB 接口 枚举成CDC的项目 接上电脑程序功能正常 但是在不接电脑时 程序一直在USB中断中 下在给大家分享一下我的解决方法 首先是在 stm32f1xx hal pcd
  • asp: AJAX Database

  • Python爬虫进阶:使用Scrapy库进行数据提取和处理

    在我们的初级教程中 我们介绍了如何使用Scrapy创建和运行一个简单的爬虫 在这篇文章中 我们将深入了解Scrapy的强大功能 学习如何使用Scrapy提取和处理数据 一 数据提取 Selectors和Item 在Scrapy中 提取数据主
  • python之UI自动化框架测试126邮箱(数据驱动)

    项目地址 https pan baidu com s 18XVd3IB SIQJygxUVdbuLA密码 74cm 目录和包介绍 Data目录 存放执行项目用例所需的数据文件 excel Conf目录 包含所有获取页面元素的定位表达式文件
  • python class函数解释

    init 是Python中的构造函数 构造函数用于初始化类的内部状态 为类的属性设置默认值 两个下划线开头的函数是声明该属性为私有 不能在类的外部被使用或访问 init 函数 方法 支持带参数类的初始化 也可为声明该类的属性 类中的变量 i
  • RuntimeError: Input type (torch.FloatTensor) and weight type (torch.cuda.FloatTensor) should be the

    RuntimeError Input type torch FloatTensor and weight type torch cuda FloatTensor should be the same or input should be a
  • IDEA的使用和快捷键

    从刚开始Eclipse到IDEA的不顺手 一点一点习惯上IDEA的使用 可能回去使用Eclipse估计会用不惯了 毕竟IDEA自带了很多工具maven git等工具 还有下边的一些窗口用着也还不错 大概记录一下自己在使用IDEA的过程和工作
  • 毕业项目SSM框架配置文件之applicationContext.xml

    applicationContext xml
  • 32. 最长有效括号

    32 最长有效括号 题目描述 给你一个只包含 和 的字符串 找出最长有效 格式正确且连续 括号子串的长度 示例1 输入 s 输出 2 解释 最长有效括号子串是 示例2 输入 s 输出 4 解释 最长有效括号子串是 示例3 输入 s 输出 0
  • C/C++中分离(detach)线程的陷阱

    C C 中分离 detach 线程的陷阱 版权声明 线程资源回收 pthread join pthread detach 什么时候分离线程会存在陷阱 方法论 既使用quick exit 又需要析构 PS 版权声明 本文为博主原创文章 未经博
  • QEventLoop 的使用

    背景 事件循环本质上就是一个无限循环 不停地去获取下一个事件 然后做出处理 直到 quit 事件发生 循环结束 QEventLoop QEventLoop 即 Qt 中的事件循环类 其中 exec 是启动事件循环 调用 exec 以后 调用
  • 在宿主机查看docker使用cpu、内存、网络、io情况

    使用命令 docker stats OPTIONS CONTAINER docker stats命令返回一个用于运行容器的实时数据流 要将数据限制到一个或多个特定的容器 可以指定一个由空格分隔的容器名称或ID的列表 您也可以指定一个已停止的
  • Guava缓存(一)基础

    1 概述 1 1 简介 Guava缓存 谷歌开源的一种本地缓存 使用本节点的内存来存储的 实现原理类似于ConcurrentHashMap 使用多个segments方式的细粒度锁 在保证线程安全的同时 支持高并发场景需求 同时支持多种类型的
  • linux并发服务器 —— IO多路复用(八)

    半关闭 端口复用 半关闭只能实现数据单方向的传输 当TCP 接中A向 B 发送 FIN 请求关闭 另一端 B 回应ACK 之后 A 端进入 FIN WAIT 2 状态 并没有立即发送 FIN 给 A A 方处于半连接状态 半开关 此时 A
  • 热门免费高效scrum敏捷项目管理工具

    Scrum中非常强调公开 透明 直接有效的沟通 这也是 可视化的管理工具 在敏捷开发中如此重要的原因之一 通过 可视化的管理工具 让所有人直观的看到需求 故事 任务之间的流转状态 可以使团队成员更加快速适应敏捷开发流程 Leangoo领歌
  • Elasticsearch高可用和路由规则

    文章目录 Lucene Elasticsearch ELK stack Elasticsearch集群和分片路由 高可用 Lucene 迄今为止最先进 性能最好的 功能最全的搜素引擎库 Elasticsearch 基于Apache Luce
  • flutter之Align控件

    Align控件即对齐控件 能将子控件所指定方式对齐 并根据子控件的大小调整自己的大小 对齐子控件的方式 bottomCenter 0 5 1 0 底部中心 bottomLeft 0 0 1 0 左下角 bottomRight 1 0 1 0
  • Unity机器学习2 ML-Agents第一个例子

    Unity机器学习2 ML Agents第一个例子 上一节我们安装了机器学习mlagents的开发环境 本节我们创建第一个例子 了解什么是机器学习 我们的例子很简单 就是让机器人自主移动到目标位置 不能移动到地板范围外 首先我们来简单的了解
  • SpringBoot整合ElasticSearch实现模糊查询,排序,分页,高亮

    目录 前言 1 框架集成 SpringData 整体介绍 1 1Spring Data Elasticsearch 介绍 2 框架集成Spring Data Elasticsearch 2 1版本说明 2 2 idea创建一个springb
  • js 给图片添加水印

    如何在图片上添加水印 1 把图片或者图片文件转成image元素 2 把转成的image转成canvas 3 在生成的canvas中添加水印 先看效果 1 把图片或者图片文件转成image元素 function urlToImg url re