使用canvas给图片添加水印

上接文章“图片处理”

canvas元素其实就是一个画布,我们可以很方便地绘制一些文字、线条、图形等,它也可以将一个img标签里渲染的图片画在画布上。

我们在上传文件到后端的时候,使用input标签读取用户本地文件后得到的其实是一个Blob对象(更精确的说是File对象,特殊的Blob对象);而在页面上展示一个图片使用的是img标签;绘制功能用canvas实现。

添加水印的功能需要在img标签、canvas画布、Blob对象这三者之间相互转换,通过一些API可以完成这个工作:

我们可以从本地读取图片Blob,然后渲染到img标签,使用canvas绘制img内容并且绘制水印内容到画布,再将canvas内容转为Blob对象上传服务器,这样就完整实现了图片+水印的功能。

一、本地读取图像文件渲染到img标签

本地读取图片文件将会得到一个Blob对象,我们可以借助FileReader.readAsDataURL方法读取Blob的内容,并得到一个Base64编码的文件内容,可以将该内容赋值给img.src从而在浏览器上渲染出本地的图像。当然,img并非必须渲染到DOM树。

读取操作是个异步操作,读取完成会触发load事件,为了便于之后的调用,我们可以用一个Promise包装这个操作,最后返回一个Promise对象。

function blobToImg (blob) {
  return new Promise((resolve, reject) => {
    let reader = new FileReader()
    reader.addEventListener('load', () => {
      let img = new Image()
      img.src = reader.result
      img.addEventListener('load', () => resolve(img))
    })
    reader.readAsDataURL(blob)
  })
}

二、将img标签内容绘制到canvas画布

调用canvas元素画布上下文对象的drawImage方法即可实现将img内容绘制到画布。


function imgToCanvas (img) {
  let canvas = document.createElement('canvas')
  canvas.width = img.width
  canvas.height = img.height
  let ctx = canvas.getContext('2d')
  ctx.drawImage(img, 0, 0)
  return canvas
}

drawImage这个方法可以传入多个参数,以定义绘制的图像的范围,这里传入的0, 0定义从图像左上角开始绘制,后面可以继续传入两个参数来定义图像的绘制终点,不过这里整个图片都要绘制到canvas,所以采用默认值即可。

三、canvas画布上绘制水印并转换为Blob对象

在图片上传的时候,我们通常采用FormData,图片文件以一个Blob对象的形式放到FormData中,所以我们需要把canvas再转为Blob以便文件上传等操作。利用HTMLCanvasElement.toBlob方法:

function watermark (canvas, text) {
  return new Promise((resolve, reject) => {
    let ctx = canvas.getContext('2d')
    // 设置填充字号和字体,样式
    ctx.font = "24px 宋体"
    ctx.fillStyle = "#FFC82C"
    // 设置右对齐
    ctx.textAlign = 'right'
    // 在指定位置绘制文字,这里指定距离右下角20坐标的地方
    ctx.fillText(text, canvas.width - 20, canvas.height - 20)
    canvas.toBlob(blob => resolve(blob))
  })
}

四、图片添加水印完整接口

将以上三个步骤结合起来,就完整地实现了从图片添加水印,下面是一个简单的使用示例:从本地选择一个图片文件,然后添加水印后,在传入的dom元素下预览添加水印后的图片。


function imgWatermark (dom, text) {
  let input = document.createElement('input')
  input.setAttribute('type', 'file')
  input.setAttribute('accept', 'image/*')
  input.onchange = async () => {
    let img = await blobToImg(input.files[0])
    let canvas = imgToCanvas(img)
    let blob = await watermark(canvas, text)
    // 此处将Blob读取到img标签,并在dom内渲染出来;如果是上传文件,可以将blob添加到FormData中
    let newImage = await blobToImg(blob)
    dom.appendChild(newImage)
  }
  input.click()
}

给页面加一个id为container的div元素,然后如下调用:


let dom = document.querySelector('#container')
imgWatermark(dom, '水印文字')

这样就完整地给图片添加了水印效果,下面看一下实际效果。

添加水印前:

 

添加水印后(水印内容:“canvas添加的水印”):

五、总结

本文仅仅介绍了图像+水印文字的简单实现,但是涉及的一些接口其实很有用。比如有时候遇到的一个功能是头像上传的预览和剪裁,这时候你可以利用FileReader来读取文件内容预览,利用CanvasRenderingContext2D.drawImage来实现剪裁功能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HNU-电路与电子学-小班3

第三次讨论 1 、直接用晶体管而不是逻辑门实现异或门,并解释这个电路是如何工作的。 (6个 MOS 管构成) 2 、通信双方约定采用 7 位海明码进行数据传输。请为发送方设计海明码校验位 生成电路,采用功能块和逻辑门为接收方设计海…

ISO_IEC_7816-3

介绍 ISO/IEC 7816 是一系列标准,规定了集成电路卡和此类卡的使用 互换。 这些卡是用于在外部世界和卡中的集成电路之间协商的信息交换的识别卡。 作为信息交换的结果,卡传递信息(计算结果、存储的数据)和/或修改其内容&#xff0…

路径规划算法:基于果蝇优化的路径规划算法- 附代码

路径规划算法:基于果蝇优化的路径规划算法- 附代码 文章目录 路径规划算法:基于果蝇优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要:本文主要介绍利用智能优化算法果蝇…

2023全国酒店数据

数据内容字段结构 hotel_id int(11) NOT NULL, name varchar(100) DEFAULT NULL, name_en varchar(100) DEFAULT NULL, short_name varchar(100) DEFAULT NULL, province varchar(20) DEFAULT NULL, city_id int(11) DEFAULT NULL, city varchar(20…

Android12之源码手动生成aidl对应java/cpp/ndk/rust服务(一百五十三)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

博客系统(ssm版本)

在前面的文章中给大家介绍过博客系统的servlet版本,但是servlet的技术非常的老旧,我们在企业中用的都是springboot相关的框架,本章内容就是讲述如何一步一步的利用ssm的技术来实现博客系统。 目录 前期配置 创建数据库 配置文件 公共文件…

【JavaSE】Java基础语法(五):数组详解

文章目录 🍸1.1 数组介绍🍸1.2 数组的动态初始化1.2.1 什么是动态初始化1.2.2 动态初始化格式🍸1.3 数组元素访问1.3.1 什么是索引1.3.2 访问数组元素格式1.3.3 示例代码 🍸1.4 内存分配1.4.1 内存概述1.4.2 java中的内存分配 &am…

【Java-10】深入浅出线程安全、死锁、状态、通讯、线程池

主要内容 线程安全线程死锁线程的状态线程间通讯线程池 1 线程安全 1.1 线程安全产生的原因 多个线程在对共享数据进行读改写的时候,可能导致的数据错乱就是线程的安全问题了 问题出现的原因 : 多个线程在对共享数据进行读改写的时候,可能导致的数据…

第五十天学习记录:C语言进阶:位段

位段 什么是位段 位段的声明和结构是类似的&#xff0c;有两个不同&#xff1a; 1、位段的成员可以是int,unsigned int或signed int。 2、位段的成员名后边有一个冒号和一个数字。 #define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>//位段-二进制位 struct A {int …

GoWeb -- gin框架的入门和使用(2)

前言 书接上回&#xff0c;在gin的框架使用中&#xff0c;还有着许多方法以及它们的作用&#xff0c;本篇博客将会接着上次的内容继续记录本人在学习gin框架时的思路和笔记。 如果还没有看过上篇博客的可以点此跳转。 map参数 请求url&#xff1a; http://localhost:8080/us…

什么是IPAM?如何使用IPAM来管理IP地址和DHCP?

在计算机网络中&#xff0c;IPAM&#xff08;IP Address Management&#xff09;是一种用于管理IP地址和DHCP&#xff08;Dynamic Host Configuration Protocol&#xff09;的工具或系统。IPAM旨在简化和集中管理IP地址分配、子网划分和DHCP配置等任务。本文将详细介绍IPAM的概…

奇偶分频电路

目录 偶数分频 寄存器级联法 计数器法 奇数分频 不满足50%占空比 50%占空比 偶数分频 寄存器级联法 寄存器级联法能实现2^N的偶数分频&#xff0c;具体做法是采用寄存器结构的电路&#xff0c;每当时钟上升沿到来的时候对输出结果进行翻转&#xff0c;以此来实现偶数分…

chatgpt赋能python:Python中日期转换:从字符串到日期对象

Python中日期转换&#xff1a;从字符串到日期对象 作为一个经验丰富的Python工程师&#xff0c;日期转换在我的日常编码工作中经常遇到。Python提供了一些内置函数和模块&#xff0c;可以将字符串转换为日期对象或将日期对象格式化为特定的字符串。本篇文章将带您深入了解Pyth…

【JavaSE】Java基础语法(二十二):包装类

文章目录 1. 基本类型包装类2. Integer类3. 自动拆箱和自动装箱4. int和String类型的相互转换 1. 基本类型包装类 基本类型包装类的作用 将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据常用的操作之一&#xff1a;用于基本数据类型与字符串之间的…

黑马Redis视频教程实战篇(三)

目录 一、优惠券秒杀 1.1 全局唯一ID 1.2 Redis实现全局唯一ID 1.3 添加优惠卷 1.4 实现秒杀下单 1.5 库存超卖问题分析 1.6 代码实现乐观锁解决超卖问题 1.7 优惠券秒杀-一人一单 1.8 集群环境下的并发问题 二、分布式锁 2.1 基本原理和实现方式对比 2.2 Redis分布…

【计算思维题】少儿编程 蓝桥杯青少组计算思维真题及详细解析第6套

少儿编程 蓝桥杯青少组计算思维真题及详细解析第6套 1、兰兰有一些数字卡片,从 1 到 100 的数字都有,她拿出几张数字卡片按照一定顺序摆放。想一想,第 5 张卡片应该是 A、11 B、12 C、13 D、14 答案:C 考点分析:主要考查小朋友们的观察能力和数学推理能力,从给定的图…

交换机的4种网络结构方式:级联方式、堆叠方式、端口聚合方式、分层方式

交换机是计算机网络中重要的网络设备之一&#xff0c;用于实现局域网&#xff08;LAN&#xff09;内部的数据转发和通信。交换机可以采用不同的网络结构方式来满足不同的网络需求和拓扑结构。本文将详细介绍交换机的四种网络结构方式&#xff1a;级联方式、堆叠方式、端口聚合方…

特瑞仕|关于无线射频

无线射频&#xff08;Radio Frequency, RF&#xff09;是指在一定频率范围内&#xff0c;通过无线电波进行通信和传输信息的技术。随着移动通信、物联网、智能家居等领域的不断发展&#xff0c;无线射频技术已经成为现代社会中不可或缺的一部分。本文将从以下几个方面对无线射频…

230530-论文整理-课题组2

对这些研究有点兴趣颇微。 文章目录 Rethinking Dense Retrieval’s Few-Shot AbilityDecoder-Only or Encoder-Decoder? Interpreting Language Model as a Regularized Encoder-DecoderPLOME: Pre-training with Misspelled Knowledge for Chinese Spelling CorrectionRead…

一般小型企业,一个CRM系统要多少钱?都有哪些功能?

客户关系管理crm多少钱一套&#xff1f; 不同CRM要价不同&#xff0c;甚至同一款CRM产品在不同客户方部署下来的价格也是有差别的。 这篇给大家分享几款可实操的CRM管理软件的价位&#xff0c;有需要的可以做以参考&#xff01; 一、简道云CRM管理系统 模版地址&#xff1a;…