当前位置: 首页 > news >正文

重新认识下网页水印

使用背景图图片

在这里插入图片描述

单独使用 css 实现,使用 backgroundImage,backgroundRepeat 将背景图片平铺到需要加水印的容器中即可。
如果希望实现旋转效果,可以借助伪元素,将背景样式放到伪元素中,旋转伪元素实现:

<style>.watermark {position: relative;overflow: hidden;background-color: transparent;}.watermark::before {content: '';position: absolute;width: 160%;height: 160%;top: -20%;left: -20%;z-index: -1;background-image: url('./watermark.png');background-position: 0 0;background-origin: content-box;background-attachment: scroll;transform: rotate(-20deg);background-size: auto;background-repeat: round;opacity: 0.3;pointer-events: none;}
</style>

动态生成div

在这里插入图片描述

根据水印容器的大小动态生成div,div内可以任意设置文本样式和图片,借助userSelect禁止用户选中文本水印;

const addDivWaterMark = (el, text) => {const { clientWidth, clientHeight } = el;const waterWrapper = document.createElement('div');waterWrapper.className = "waterWrapper";const column = Math.ceil(clientWidth / 100);const rows = Math.ceil(clientHeight / 100);// 根据容器宽高动态生成divfor (let i = 0; i < column * rows; i++) {const wrap = document.createElement('div');wrap.className = "water";wrap.innerHTML = `<div class="water-item">${text}</div>`waterWrapper.appendChild(wrap)}el.append(waterWrapper)
}

Canvas写入图片做背景水印

在这里插入图片描述

将图片写入Canvas然后将Canvas作为背景图

  const img = new Image();const { ctx, canvas } = createWaterMark(config);img.onload = function () {ctx.globalAlpha = 0.2;ctx.rotate(Math.PI / 180 * 20);ctx.drawImage(img, 0, 16, 180, 100);canvasRef.value.style.backgroundImage = `url(${canvas.toDataURL()})`};img.src = ImageBg;

Canvas写入文字做背景水印

在这里插入图片描述

将文字写入Canvas然后将Canvas作为背景图

 const canvas = document.createElement('canvas');canvas.width = width;canvas.height = height;const ctx = canvas.getContext('2d');ctx.clearRect(0, 0, width, height);ctx.fillStyle = fillStyle;ctx.globalAlpha = opacity;ctx.font = fontctx.rotate(Math.PI / 180 * rotate);ctx.fillText(text, 0, 50);return canvas

Svg做水印

在这里插入图片描述

通过svg样式来控制水印样式,再将svg转换成base64的背景图

  const svgStr =`<svg xmlns="http://www.w3.org/2000/svg" width="180px" height="100px"><text x="0px" y="30px" dy="16px"text-anchor="start"stroke="#000"stroke-opacity="0.1"fill="none"transform="rotate(-20)"font-weight="100"font-size="16"> 前端小书童</text></svg>`;return `data:image/svg+xml;base64,${window.btoa(unescape(encodeURIComponent(svgStr)))}`;

shadowDom水印

在这里插入图片描述

使用customElements自定义个一个标签(可以使用其他任意标签,不过注意shadow DOM会使起同级的元素不显示。)
可以像shadow DOM写入style样式和水印节点(可以使用背景或者div形式)
shadow DOM内部实现的样式隔离不用担心写入的style影响页面其他元素样式,这个特性在微前端的实现中也被广泛使用。

 class ShadowMark extends HTMLElement {constructor() {super();const shadowRoot = this.attachShadow({ mode: 'open' });const wrapContainer = document.createElement('div')const style = document.createElement('style');style.textContent = `.wrapContainer {width: 100%;height: 100%;display: flex;flex-wrap: wrap;}.watermark-item {display: flex;font-size: 16px;opacity: .3;transform: rotate(-20deg);user-select: none;white-space: nowrap;justify-content: center;align-items: center;}`;const waterHeight = 100const waterWidth = 100const { clientWidth, clientHeight } = document.querySelector('.shadow-watermark')const column = Math.ceil(clientWidth / waterWidth)const rows = Math.ceil(clientHeight / waterHeight)wrapContainer.setAttribute('class', "wrapContainer")for (let i = 0; i < column * rows; i++) {const wrap = document.createElement('div')wrap.setAttribute('class', 'watermark-item')wrap.style.width = waterWidth + 'px'wrap.style.height = waterHeight + 'px'wrap.textContent = "前端小书童"wrapContainer.appendChild(wrap)}shadowRoot.appendChild(style);shadowRoot.appendChild(wrapContainer)}}customElements.define('shadow-mark', ShadowMark);

盲水印

canvas画布(canvas.getContext(‘2d’))调用 getImageData 得到一个 ArrayBuffer,用于记录画布每个像素的 rgba 值

r: Red取值范围0~255
g: Green取值范围0~255
b:Blue取值范围0~255
a:Alpha 透明度取值范围0~1,0代表全透明
可以理解为每个像素都是通过红、绿、蓝三个颜色金额透明度来合成颜色

方案一:低透明度方案的暗水印

在这里插入图片描述

当把水印内容的透明度 opacity 设置很低时,视觉上基本无法看到水印内容,但是通过修改画布的 rgba 值,可以使水印内容显示出来。
选择固定的一个色值例如R,判断画布R值的奇偶,将其重置为0或者255,低透明的内容就便可以显示出来了。

const decode = (canvas, colorKey, flag, otherColorValue) => {const ctx = canvas.getContext('2d');const originalData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);let data = originalData.data;for (let i = 0; i < data.length; i++) {//筛选每个像素点的R值if (i % 4 == colorKey) {if (data[i] % 2 == 0) {//如果色值为偶数data[i] = flag ? 255 : 0;} else {//如果色值为奇数data[i] = flag ? 0 : 255;}} else if (i % 4 == 3) {//透明度不作处理continue;} else {// 关闭其他色值if (otherColorValue !== undefined) {data[i] = otherColorValue}}}ctx.putImageData(originalData, 0, 0);
}

方案二:将水印内容以像素偏差记录到画布中

在这里插入图片描述

用画布和水印后的画布绘制的像素进行ArrayBuffer对比,在存在水印像素的位置(水印画布透明度不为0)修改图片画布的奇偶,这样通过上面指定色值和奇偶去解码时,修改的文本像素就会被显示出来;

const encode = (ctx, textData, color, originalData) => {for (let i = 0; i < originalData.data.length; i++) {// 只处理目标色值if (i % 4 == color) {// 水印画布透明度为0if (textData[i + offset] === 0 && (originalData.data[i] % 2 === 1)) {// 放置越界if (originalData.data[i] === 255) {originalData.data[i]--;} else {originalData.data[i]++;}// 水印画布透明度不为0} else if (textData[i + offset] !== 0 && (originalData.data[i] % 2 === 0)) {originalData.data[i]++;}}}ctx.putImageData(originalData, 0, 0);
}

方案三:数字加密

在图像信号的频域(变换域)中隐藏信息要比在空间域(上面得到的像素颜色的ArrayBuffer)中隐藏信息具有更好的防攻击性。
这部分暗水印的实现,可以直接使用阿里云提供给的api,不过需要图片资源藏到的阿里云的OSS下;

MutationObserver

可以发现上面水印基本都是通过增加节点或者背景图的形式来实现,那用户其实可以通过屏蔽样式或者删除Dom来消除水印,那么我们可以借用MutationObserver来监听下水印dom的变化,来阻止用户以这种形式来消除水印;

以上代码见:https://github.com/wenjuGao/watermark-demo

线上效果:https://watermark-demo.vercel.app/

参考:

https://www.cnblogs.com/88223100/p/Exploring-Web-Watermarking-Technology.html

https://blog.csdn.net/blueblueskyhua/article/details/120346195

https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API

相关文章:

重新认识下网页水印

使用背景图图片 单独使用 css 实现&#xff0c;使用 backgroundImage&#xff0c;backgroundRepeat 将背景图片平铺到需要加水印的容器中即可。 如果希望实现旋转效果&#xff0c;可以借助伪元素&#xff0c;将背景样式放到伪元素中&#xff0c;旋转伪元素实现&#xff1a; &l…...

Unity脚本练习

在C# 中 class 是创建类的标志&#xff0c;要创建类的话得现有class上面这个的逻辑是 类的访问权限&#xff0c; 关键字&#xff0c;类名以及类继承的父类在Unity中创建一个脚本或者添加一个组件&#xff0c;就相当于在Unity命名空间中创建了一个可以访问的类。这些类能够直接在…...

二十分钟带你了解JVM性能调优与实战进阶

ZGC 诞生原因 Java生态非常强大&#xff0c;但还不够&#xff0c;有些场景仍处于劣势&#xff0c;而ZGC的出现可以让Java语言抢占其他语言的某些特定领域市场。比如 谷歌主导的Android手机系统显示卡顿。证券交易市场&#xff0c;实时性要求非常高&#xff0c;目前主要是C主…...

对比应用层和内核层区别

一、所使用的空间不同&#xff1a; 应用层使用的空间是0-3G的用户空间。 内核层使用的是3-4G的内核空间。 二、打印信息所用函数不同&#xff1a; 应用层使用printf打印信息。 printf("打印信息\n"); 内核层使用printk打印信息。 …...

Hadoop服役新服务器

目录 0、准备一台新服务器 1、修改主机名 2、配置静态ip 3、配置xshell登录 4、关闭并禁用防火墙 5、分发hadoop和jdk文件 6、分发环境变量文件 7、source 环境变量 8、配置ssh 9、删除105节点的data、logs文件夹 10、单节点启动并关联到集群 11、验证新节点是否有效 0…...

YOLOv8详解 【网络结构+代码+实操】

文章目录YOLOv8 概述模型结构Loss 计算训练数据增强训练策略模型推理过程网络模型解析卷积神经单元&#xff08;model.py&#xff09;Yolov8实操快速入门环境配置数据集准备模型的训练/验证/预测/导出使用CLI使用python多任务支持检测实例分割分类配置设置操作类型训练预测验证…...

Visual Studio Code 1.76 发布

欢迎使用 Visual Studio Code 2023 年 2 月版&#xff0c;其中一些亮点包括&#xff1a; 配置文件 - 活动配置文件徽章&#xff0c;通过命令面板快速切换配置文件。辅助功能改进 - 新的音频提示&#xff0c;改进的终端屏幕阅读器模式。可移动的 Explorer 视图- 将资源管理器放…...

Vulnhub靶场----3、DC-3.2

文章目录一、环境搭建二、渗透流程三、思路总结一、环境搭建 靶场下载地址&#xff1a;https://download.vulnhub.com/dc/DC-3-2.zip kali&#xff1a;192.168.144.148 DC-3.2&#xff1a;192.168.144.151 更改驱动器连接设置&#xff1a; 二、渗透流程 1、信息收集nmap -T5 -…...

Windows电脑密码忘记解决方法

目录 背景 方法一 方法二 方法三 方法四 方法五 背景 个人电脑忘记了密码&#xff0c;无法登录用户界面。 方法一 1. 开机时常按 F11&#xff0c;如果是Win10一下系统&#xff0c;就常按 F8&#xff0c;知道出现一下图状 2. 选择疑难解答&#xff0c;再选择高级选项 3.…...

ChatGPT相关技术必读论文100篇(2.27日起,几乎每天更新)

按上篇文章《ChatGPT技术原理解析&#xff1a;从RL之PPO算法、RLHF到GPT-N、instructGPT》的最后所述 为了写本ChatGPT笔记&#xff0c;过去两个月翻了大量中英文资料/paper(中间一度花了大量时间去深入RL)&#xff0c;大部分时间读的更多是中文资料 2月最后几天读的更多是英文…...

【算法】算法题解---电话号码的字符组合

算法名称 电话号码的字符组合 算法描述 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&…...

提高上限之数学学习——数制转换及MECE原则学习

文章目录数制转换不同数制表达数制转换的方法换基法(换向十进制)除余法(十进制向其他进制转换)按位拆分法和按位合并法判断一个整数a&#xff0c;是否是2的整数次幂MECE原则学习数制转换 不同数制表达 数制转换的方法 换基法(换向十进制) 定义&#xff1a;给出数制转换的定量…...

字符函数和字符串函数(下)——“C”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容依旧是字符函数和字符串函数呀&#xff0c;这篇博客会讲一些内存相关的函数&#xff0c;下面&#xff0c;让我们进入字符函数和字符串函数的世界吧 字符串查找 strstr strtok 错误信息报告 strerror 字符操作 内存操作函…...

kafka docker 安装

先启动起 zookeeper &#xff08;1&#xff09;服务&#xff1a; 192.168.190.35docker run -d --name kafka1 \-p 9092:9092 \-e KAFKA_BROKER_ID0 \-e delete.topic.enabletrue \-e num.partitions1 \-e KAFKA_ZOOKEEPER_CONNECT192.168.192.35:2181 \-e KAFKA_ADVERTISED_LI…...

SpringBean管理

一.什么是SpringBean? 在Spring中将管理对象称为 Bean.Bean是由一个SpringIOC容器实例化,组装和管理的对象.也就是说,Bean并不是由我们程序员编写的,而是在程序运行过程中,由Spring通过反射机制生成的. SpringBean是Spring框架在运行时管理的对象,我们编写的大多数逻辑代码都…...

关于Vue3中reactive的意义

在学习Vue3的时候产生疑问&#xff1a; const addForm reactive({ // 这里面的reactive啥意思sysPre: null,diaPre: null,tem: null })查询解决 在Vue3中&#xff0c;响应式对象是指通过reactive函数转换而来的对象&#xff0c;它的属性可以被Vue自动监测&#xff0c;当属性…...

平衡三进制

平衡三进制 一、定义 平衡三进制&#xff0c;也称为对称三进制。这是一个不太标准的 计数体系。 正规的三进制的数字都是由 0,1,2 构成的&#xff0c;而平衡三进制的数字是由 -1,0,1 构成的。它的基数也是 3&#xff08;因为有三个可能的值&#xff09;。由于将 -1 写成数字…...

python爬取网站数据

开学前接了一个任务&#xff0c;内容是从网上爬取特定属性的数据。正好之前学了python&#xff0c;练练手。 编码问题 因为涉及到中文&#xff0c;所以必然地涉及到了编码的问题&#xff0c;这一次借这个机会算是彻底搞清楚了。 Unicode是一种编码方案&#xff0c;又称万国码…...

CSS的三大特性

&#x1f31f;所属专栏&#xff1a;前端只因变凤凰之路&#x1f414;作者简介&#xff1a;rchjr——五带信管菜只因一枚&#x1f62e;前言&#xff1a;该系列将持续更新前端的相关学习笔记&#xff0c;欢迎和我一样的小白订阅&#xff0c;一起学习共同进步~&#x1f449;文章简…...

Linux-scheduler之负载均衡(二)

四、调度域 SDTL结构 linux内核使用SDTL结构体来组织CPU的层次关系 struct sched_domain_topology_level {sched_domain_mask_f mask; //函数指针&#xff0c;用于指定某个SDTL的cpumask位图sched_domain_flags_f sd_flags; //函数指针&#xff0c;用于指定某个SD…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...