【uniapp】图片添加canvas水印
目录
- 需求&背景
- 实现
- 地理位置
- 添加水印
- ios补充
需求&背景
需求:拍照后给图片添加水印, 水印包含经纬度、用户信息、公司logo等信息。
效果图:
方案:使用canvas添加水印。
具体实现:上传图片组件是项目里现有的,主要还是使用uni.chooseImage,这里不做赘述。
在上传图片组件中增加一个参数判断是否添加水印,在获取到图片、上传到后端之前对图片进行加工。
实现
在模板中添加canvas。
template:
<template><view class="md-upload"><!-- 其他组件上传图片逻辑 --><canvas v-if="waterMarkParams.display" canvas-id="waterMarkCanvas" :style="canvasStyle" /></view>
</template>
props:
props: {//其他props参数。。。waterMask: { // 是否添加水印type: Boolean,default: () => false}},
其他的一些数据
//这个放data
waterMarkParams: {display: false, // 控制 canvas 创建与销毁canvasWidth: 300, // 默认宽度canvasHeight: 225 // 默认高度
},
latitude: "",
longitude: "",
address: "",
orgFullName: "",
userName: "",
currentTime: ""//这个放computed
canvasStyle() {
return {position: 'fixed', // 移除到屏幕外left: '9999px',width: this.waterMarkParams.canvasWidth + 'px',height: this.waterMarkParams.canvasHeight + 'px'};
}
地理位置
这里有一个小问题,尝试了很多方法都没办法在success回调中去绘制canvas,退而其次选择在mounted里去获得地理位置,不知道有没有更好的写法欢迎指正。
注:如果要获取地理位置,type只能选择gcj02,而且这个type只适用于app,用h5开发调试会显示不出来。
mounted() {uni.getLocation({type: 'gcj02',geocode: true,success: (res) => {//经纬度this.longitude = res.longitude; this.latitude = res.latitude;//拼接地址this.address = res.address.province + res.address.city + res.address.district + res.address.street + res.address.streetNum + res.address.poiName;}})},
添加水印
入参的src是uni.chooseImage的success回调里返回的path
// 添加水印async getWaterMark(src) {//绘图方法startfunction fillText(context, x, y, content, font, fontStyle, textAlign) {context.save();context.font = font;context.fillStyle = fontStyle;context.textAlign = textAlign;context.fillText(content, x, y);}function fillCircle(context, x, y, r, fillStyle) {context.save();context.beginPath();context.arc(x, y, r, 0, 2 * Math.PI);context.fillStyle = fillStyle;context.fill();context.closePath();}function fillRect(context, x, y, width, height, fillStyle) {context.save();context.fillStyle = fillStyle;context.fillRect(x, y, width, height);}//绘图方法endlet that = this;that.waterMarkParams.display = true;this.currentTime = moment().format('YYYY-MM-DD HH:mm:ss'); //获取当前时间if(!this.orgFullName){ //获取运营商信息await this.getOrgFullName();}this.userName = uni.getStorageSync("userInfo").userTitle; //获取用户信息return new Promise((resolve, reject) => {// 获取图片信息,配置 canvas 尺寸uni.getImageInfo({src,success: (res) => {try {console.log("成功获取图片信息",res);// 修复部分手机(如红米9)手机屏幕比较窄拍摄出来的图片水印压缩着覆盖的问题that.waterMarkParams.canvasWidth = Math.max(res.width, 886);that.waterMarkParams.canvasHeight = res.height;// 等待 canvas 元素创建that.$nextTick(async () => {let context = uni.createCanvasContext('waterMarkCanvas', that);const { canvasWidth, canvasHeight } = that.waterMarkParams;// 绘制前清空画布context.clearRect(0, 0, canvasWidth, canvasHeight);// 将图片src放到cancas内,宽高必须为图片大小context.drawImage(src, 0, 0, canvasWidth, canvasHeight, canvasWidth, canvasHeight);const fangweiY = 320;// 保证水印能完整显示出来 x是画布宽度两倍 y是画布高度减去防伪码位置和字体大小const [x, y] = [canvasWidth / 2, canvasHeight - (fangweiY + 100)];// 坐标原点移动到画布左下角context.translate(0, canvasHeight);const icon_site = require("./site.png");const icon_camera = require("./camera.png");const icon_icon = require("./icon.png");context.drawImage(icon_site, 40,-360,30,46);fillText(context, 80, -326, this.siteName, '500 40px "Microsoft YaHei"', 'white', 'left');fillRect(context, 40, -290, 10, 260, "#FF0000");fillText(context, 70, -250, "时间:" + this.currentTime, '30px "Microsoft YaHei"', 'white', 'left');fillText(context, 70, -190, "运维商:" + this.orgFullName, '30px "Microsoft YaHei"', 'white', 'left');fillText(context, 70, -130, "经纬度:" + that.longitude + ", " + that.latitude, '30px "Microsoft YaHei"', 'white', 'left');if(that.address.length > 15){ //地址过长时截断显示fillText(context, 70, -70, "地址:" + that.address.slice(0,15), '30px "Microsoft YaHei"', 'white', 'left');fillText(context, 70, -40, "" + that.address.slice(15), '30px "Microsoft YaHei"', 'white', 'left');}else {fillText(context, 70, -70, "地址:" + that.address, '30px "Microsoft YaHei"', 'white', 'left');}//移动到右下角let textLength = that.userName.length * 30;context.translate(canvasWidth, 0);if(that.address.length > 15){context.drawImage(icon_icon, -180, -130,72,35);fillText(context, -40, -100, "光伏", '30px "Microsoft YaHei"', 'white', 'right');context.drawImage(icon_camera, -1 * textLength - 130, -70,38,34);fillText(context, -40, -40, that.userName + " 拍摄", '30px "Microsoft YaHei"', 'white', 'right');}else{context.drawImage(icon_icon, -180, -160,72,35);fillText(context, -40, -130, "光伏", '30px "Microsoft YaHei"', 'white', 'right');context.drawImage(icon_camera, -1 * textLength - 130, -100,38,34);fillText(context, -40, -70, that.userName + " 拍摄", '30px "Microsoft YaHei"', 'white', 'right');}// 一定要加上一个定时器否则进入到页面第一次可能会无法正常拍照,后几次才正常setTimeout(() => {// 本次绘画完重开开始绘画,并且在绘画完毕之后再保存图片,不然页面可能会出现白屏等情况context.draw(false, () => {console.log('!!!!!开始绘画', canvasWidth, canvasHeight);uni.canvasToTempFilePath({canvasId: 'waterMarkCanvas',fileType: 'jpg',width: canvasWidth,height: canvasHeight,destWidth: canvasWidth,destHeight: canvasHeight,success: ({ tempFilePath }) => {console.log('绘制成功');that.waterMarkParams.display = false;resolve(tempFilePath);},fail: (err) => {reject(err);console.log(err);}},that);});}, 1000);console.log("完成绘制");});} catch (error) {console.log(error);}},fail: (err) => {reject(err);console.log(err);}});});},
最后用这个方法返回的url替换原本上传时的url,就是添加了水印的图片
let waterUrl = await this.getWaterMark(lists[i].url);
lists[i].filePath = waterUrl;
ios补充
如果ios出现了图片空白,把方法里的定时器时间加长(我从1000加到了2500),uni.chooseImage也选择压缩不要选择原图
相关文章:
【uniapp】图片添加canvas水印
目录 需求&背景实现地理位置添加水印 ios补充 需求&背景 需求:拍照后给图片添加水印, 水印包含经纬度、用户信息、公司logo等信息。 效果图: 方案:使用canvas添加水印。 具体实现:上传图片组件是项目里现有的ÿ…...
Java 大视界 -- Java 大数据在智能金融反欺诈中的技术实现与案例分析(114)
💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…...
机器学习数学基础:43.外生变量与内生变量
外生变量与内生变量:模型中的因果角色 在因果模型(像结构方程模型、回归分析这类)里,外生变量和内生变量是用来区分变量来源和相互关系的重要概念。下面从定义、实例、差异以及应用场景四个方面来详细介绍: 一、定义…...
Bean 的生命周期主要包括以下阶段:
Bean 的生命周期主要包括以下阶段: 定义 :在配置文件或注解中定义 Bean,包括其类、作用域等信息。 实例化 :Spring 容器根据定义创建 Bean 的实例。 属性赋值 :容器为 Bean 设置配置的属性值。 初始化 :…...
Unity游戏开发中的网格简化与LOD技术(Mesh Simplification LOD)
在Unity游戏开发中,网格简化(Mesh Simplification)和LOD(Level of Detail)技术是优化渲染性能的关键手段,尤其在处理复杂场景和高精度模型时至关重要。以下是一套系统的实现方案与优化策略: 一、…...
3.7[Q]CV
对于一个由cmake构建的项目,什么时候应该执行cmake指令?什么时候执行make指令?即,一个cmake构建的项目,各步骤的意义是什么?当我修改了部分代码后,重启项目该执行什么命令? view,mod…...
发行思考:全球热销榜的频繁变动
几点杂感: 1、单机游戏销量与在线人数的衰退是剧烈的,有明显的周期性,而在线游戏则稳定很多。 如去年的某明星游戏,最高200多万在线,如今在线人数是48名,3万多。 而近期热门的是MH,在线人数8…...
Springboot全局LocalDateTime时间格式化配置
我们对字段的日期格式化时一般会用注解: JsonFormat(pattern "yyyy-MM-dd HH:mm:ss", timezone "GMT8")private Date createDate;但是每个字段都要写也太麻烦了 不是我的全局化作风 在application.yml中配置全局时间格式化只会对Date类型有用: jackson:d…...
Redis主从复制
目录 点单问题 启动多个redis服务器 配置主从结构 查看主从结构信息 断开主从结构 修改主从结构 主从复制的拓扑结构 主从复制的基本流程 全量复制和部分复制 全量复制的流程 部分复制的流程 实时复制的流程 主从复制总结 主从复制是基于分布式系统进行讨论的&am…...
玩转python:掌握Python数据结构之栈Stack
栈(Stack)是计算机科学中一种非常基础且重要的数据结构。它的特点是后进先出(LIFO,Last In First Out),就像我们生活中叠盘子一样,最后放上去的盘子总是最先被拿走。本文将用通俗易懂的语言和丰…...
电脑如何拦截端口号,实现阻断访问?
如果你弟弟喜欢玩游戏,你可以查询该应用占用的端口,结合以下方法即可阻断端口号,让弟弟好好学习,天天向上! 拦截端口可以通过防火墙和路由器进行拦截 ,以下是常用方法: 方法 1:使用…...
DeepSeek 医疗大模型微调实战讨论版(第一部分)
DeepSeek医疗大模型微调实战指南第一部分 DeepSeek 作为一款具有独特优势的大模型,在医疗领域展现出了巨大的应用潜力。它采用了先进的混合专家架构(MoE),能够根据输入数据的特性选择性激活部分专家,避免了不必要的计算,极大地提高了计算效率和模型精度 。这种架构使得 …...
Apache Httpd 多后缀解析
目录 1.原因 2.环境 3.复现 4.防御 1.Apache Httpd 多后缀解析原因 Apache HTTP Server 在处理文件请求时,通常会根据文件的后缀来确定如何处理该文件。例如,.php文件会被交给 PHP 解释器处理,而.html文件则直接作为静态文件返回。 然而…...
2025年03月07日Github流行趋势
项目名称:ai-hedge-fund 项目地址url:https://github.com/virattt/ai-hedge-fund项目语言:Python历史star数:12788今日star数:975项目维护者:virattt, seungwonme, KittatamSaisaard, andorsk, arsaboo项目…...
Jenkins在Windows上的使用(二):自动拉取、打包、部署
(一)Jenkins全局配置 访问部署好的Jenkins服务器网址localhost:8080,完成默认插件的安装后,接下来将使用SSH登录远程主机以实现自动化部署。 1. 配置插件 选择dashboard->Manage Jenkins->plugins 安装下面两个插件 …...
【JavaEE】-- 多线程(初阶)4
文章目录 8.多线程案例8.1 单例模式8.1.1 饿汉模式8.1.2 懒汉模式 8.2 阻塞队列8.2.1 什么是阻塞队列8.2.2 生产者消费者模型8.2.3 标准库中的阻塞队列8.2.4 阻塞队列的应用场景8.2.4.1 消息队列 8.2.5 异步操作8.2.5 自定义实现阻塞队列8.2.6 阻塞队列--生产者消费者模型 8.3 …...
测试直播postman+Jenkins所学
接口自动化 什么是接口?本质上就是一个url,用于提供数据。后台程序提供一种数据地址,接口的数据一般是从数据库中查出来的。 postman自动化实操: 一般来说公司会给接口文档,如果没有,通过拦截,…...
5人3小时复刻Manus?开源OpenManus项目全解剖,我的DeepSeek股票报告这样诞生
大家好,我是大 F,深耕AI算法十余年,互联网大厂技术岗。分享AI算法干货、技术心得。 更多文章可关注《大模型理论和实战》、《DeepSeek技术解析和实战》,一起探索技术的无限可能! OpenManus是什么 1. 项目背景 OpenManus 是由 MetaGPT 核心团队仅用 3 小时复刻而成的开源…...
【javaEE】多线程(基础)
1.❤️❤️前言~🥳🎉🎉🎉 Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的…...
0.大模型开发知识点需求综述
文章目录 一、机器学习与深度学习基础二、自然语言处理(NLP)基础三、大模型架构四、训练优化技术五、数据处理与预处理六、分布式训练与并行化策略七、微调方法与参数高效微调八、训练框架、工具与自动化流程九、评估与部署十、前沿技术与未来趋势 已更新…...
WHAT - 前端阻塞场景梳理
目录 前言场景梳理1. JavaScript 执行阻塞主线程场景优化思路具体代码示例1. 长时间运行的同步 JavaScript 代码2. 过多的主线程任务(如频繁的 setTimeout/setInterval)3. 未优化的第三方库或框架初始化逻辑总结 2. 样式计算与布局(Layout&am…...
微前端框架 Qiankun 的应用及问题分析
一、Qiankun 的核心应用场景与优势 多技术栈共存与灵活集成 Qiankun 支持主应用与子应用使用不同技术栈(如 Vue、React、Angular 等),通过 HTML Entry 方式接入子应用,无需深度改造子应用即可实现集成,降低了技术迁移成…...
bash: uwsgi: 未找到命令
针对你提出的“bash: uwsgi: command not found”问题,以下是一些可能的解决方案,请按照步骤逐一排查: 1、检查uwsgi命令是否正确: 确保你输入的命令是uwsgi,而不是uWSGI或其他变体。 2、确认uwsgi是否已安装&…...
HAL库,配置adc基本流程
1. 初始化阶段---cubemx (1) GPIO初始化 函数:HAL_GPIO_Init() 作用:配置ADC引脚为模拟输入模式。 代码示例: // 使能GPIOA时钟 __HAL_RCC_GPIOA_CLK_ENABLE();// 配置PA1为模拟输入 GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStr…...
【Unity】 HTFramework框架(六十一)Project窗口文件夹锁定器
更新日期:2025年3月7日。 Github源码:[点我获取源码] Gitee源码:[点我获取源码] 索引 Project窗口文件夹锁定器框架文件夹锁定自定义文件夹锁定限制条件 Project窗口文件夹锁定器 在Project窗口中,文件夹锁定器能够为任何文件夹加…...
网络安全技术整体架构 一个中心三重防护
网络安全技术整体架构:一个中心三重防护 在信息技术飞速发展的今天,网络安全的重要性日益凸显。为了保护信息系统不受各种安全威胁的侵害,网络安全技术整体架构应运而生。本文将详细介绍“一个中心三重防护”的概念,并结合代码示…...
《AJAX:前端异步交互的魔法指南》
什么是AJAX AJAX(Asynchronous JavaScript and XML,异步 JavaScript 和 XML) 是一种用于创建异步网页应用的技术,允许网页在不重新加载整个页面的情况下,与服务器交换数据并局部更新页面内容。尽管名称中包含 XML&…...
Elasticsearch 2025/3/7
高性能分布式搜索引擎。 数据库模糊搜索比较慢,但用搜索引擎快多了。 下面是一些搜索引擎排名 Lucene是一个Java语言的搜索引擎类库(一个工具包),apache公司的顶级项目。 优势:易扩展、高性能(基于倒排索引…...
LLM论文笔记 19: On Limitations of the Transformer Architecture
Arxiv日期:2024.2.26机构:Columbia University / Google 关键词 Transformer架构幻觉问题数学谜题 核心结论 1. Transformer 无法可靠地计算函数组合问题 2. Transformer 的计算能力受限于信息瓶颈 3. CoT 可以减少 Transformer 计算错误的概率&#x…...
那年周五放学
2025年3月7日,周五,天气晴,脑子一瞬间闪过02-05年中学期间某个周五下午,17:00即将放学的场景,那种激动,那种说不上的欣喜感,放学后,先走一段316国道,再走一段襄渝铁路&am…...
002-SpringCloud-OpenFeign(远程调用)
SpringCloud-OpenFeign 1.引入依赖2.编写一个远程调用接口3.测试 1.引入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId> </dependency><dependencyManageme…...
SAP 顾问的五年职业规划
SAP 顾问的职业发展受到技术进步、企业需求变化和全球经济环境的影响,因此制定长远规划充满挑战。面对 SAP 产品路线图的不确定性,如向 S/4HANA 和 Business Technology Platform (BTP) 的转变,顾问必须具备灵活性,以保持竞争力和…...
Pandas使用stack和pivot实现数据透视
Pandas的stack和pivot实现数据透视 经过统计得到多维度指标数据非常常见的统计场景,指定多个维度,计算聚合后的指标 案例:统计得到“电影评分数据集”,每个月份的每个分数被评分多少次:(月份,分…...
图像生成-ICCV2019-SinGAN: Learning a Generative Model from a Single Natural Image
图像生成-ICCV2019-SinGAN: Learning a Generative Model from a Single Natural Image 文章目录 图像生成-ICCV2019-SinGAN: Learning a Generative Model from a Single Natural Image主要创新点模型架构图生成器生成器源码 判别器判别器源码 损失函数需要源码讲解的私信我 S…...
c++ 操作符重载详解与示例
c 操作符重载详解与示例 操作符重载详解一、基本规则二、必须作为成员函数重载的运算符1. 赋值运算符 2. 下标运算符 []3. 函数调用运算符 ()4. 成员访问运算符 ->5. 转型运算符 三、通常作为非成员函数重载的运算符1. 算术运算符 2. 输入/输出运算符 << >> 四、…...
在Spring Boot项目中分层架构
常见的分层架构包括以下几层: 1. Domain 层(领域层) 作用:领域层是业务逻辑的核心,包含与业务相关的实体类、枚举、值对象等。它是对业务领域的抽象,通常与数据库表结构直接映射。 主要组件: 实体类(Entity):与数据库表对应的Java类,通常使用JPA或MyBatis等ORM框架…...
upload-labs详解(1-12)文件上传分析
目录 uploa-labs-main upload-labs-main第一关 前端防御 绕过前端防御 禁用js Burpsuite抓包改包 upload-labs-main第二关 上传测试 错误类型 upload-labs-env upload-labs-env第三关 上传测试 查看源码 解决方法 重命名,上传 upload-labs-env第四关…...
无人机应用探索:玻纤增强复合材料的疲劳性能研究
随着无人机技术的快速发展,轻量化已成为其结构设计的核心需求。玻纤增强复合材料凭借高强度、低密度和优异的耐环境性能,成为无人机机身、旋翼支架等关键部件的理想选择。然而,无人机在服役过程中需应对复杂多变的环境:高空飞行时…...
计算机毕业设计Python+DeepSeek-R1大模型空气质量预测分析(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
【渗透测试】基于时间的盲注(Time-Based Blind SQL Injection)
发生ERROR日志告警 查看系统日志如下: java.lang.IllegalArgumentException: Illegal character in query at index 203: https://api.weixin.qq.com/sns/jscode2session?access_token90_Vap5zo5UTJS4jbuvneMkyS1LHwHAgrofaX8bnIfW8EHXA71IRZwsqzJam9bo1m3zRcSrb…...
学习threejs,Animation、Core、CustomBlendingEquation、Renderer常量汇总
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️Animation常量汇总1.1.1 循…...
2、数据库的基础学习(中):分组查询、连接查询 有小例子
二、分组函数 功能:用作统计使用,又称为聚合函数或者统计函数或组函数 1、分类: sum 求和、avg 平均值、max最大值、min 最小值、count 计算个数 2、参数支持哪些类型 Sum\avg 一般处理数值型数据 max、min 可以数值型也可以字符型…...
Ubuntu搭建最简单WEB服务器
安装apache2 sudo apt install apache2 检查状态 $ sudo systemctl status apache2 ● apache2.service - The Apache HTTP ServerLoaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor prese>Active: active (running) since Thu 2025-03-06 09:51:10…...
如何学习编程?
如何学习编程? 笔记来源:How To Study Programming The Lazy Way 声明:该博客内容来自链接,仅作为学习参考 写在前面的话: 大多数人关注的是编程语言本身,而不是解决问题和逻辑思维。不要试图记住语言本身…...
OpenCV计算摄影学(14)实现对比度保留去色(Contrast Preserving Decolorization)的函数decolor()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 将彩色图像转换为灰度图像。它是数字印刷、风格化的黑白照片渲染,以及许多单通道图像处理应用中的基本工具。 cv::decolor 是 OpenCV…...
K8s 1.27.1 实战系列(七)Deployment
一、Deployment介绍 Deployment负责创建和更新应用程序的实例,使Pod拥有多副本,自愈,扩缩容等能力。创建Deployment后,Kubernetes Master 将应用程序实例调度到集群中的各个节点上。如果托管实例的节点关闭或被删除,Deployment控制器会将该实例替换为群集中另一个节点上的…...
Python第十五课:机器学习入门 | 从猜想到预测
🎯 本节目标 理解机器学习两大核心范式(监督/无监督学习)掌握特征工程的核心方法论实现经典算法:线性回归与K-Means聚类开发实战项目:房价预测模型理解模型评估与调优基础 一、机器学习核心概念(学生与老师…...
python 程序一次启动有两个进程的问题(flask)
0. 背景 写了一个使用 flask 作为服务框架的程序,发现每次启动程序的时候,使用 ps 都能观察到两个 python 进程。 此外,这个程序占用了 GPU 资源,我发现有两个 python 进程,分别占用了完全相同的 GPU 显存 1. 原因 …...
使用jcodec库,访问网络视频提取封面图片上传至oss
注释部分为FFmpeg(确实方便但依赖太大,不想用) package com.zuodou.upload;import com.aliyun.oss.OSS; import com.aliyun.oss.model.ObjectMetadata; import com.aliyun.oss.model.PutObjectRequest; import com.zuodou.oss.OssProperties;…...
MyBatis-Plus 与 Spring Boot 的最佳实践
在现代 Java 开发中,MyBatis-Plus 和 Spring Boot 的结合已经成为了一种非常流行的技术栈。MyBatis-Plus 是 MyBatis 的增强工具,提供了许多便捷的功能,而 Spring Boot 则简化了 Spring 应用的开发流程。本文将探讨如何将 MyBatis-Plus 与 Spring Boot 进行整合,并分享一些…...