在网页中添加水印的实现方法

在网页设计中,为了保护内容的版权以及增加一些特殊效果,经常需要在页面上添加水印。本文将介绍一种通过Canvas和JavaScript实现在网页上添加水印的方法。

功能:

  1. 允许自定义水印内容、字体颜色
  2. 可以防止用户删除水印元素、修改样式等其他手段隐藏水印

效果
在这里插入图片描述
代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .watermarked {
            width: 1109px !important;
            height: 4042px !important;
            max-width: unset !important;
            max-height: unset !important;
            position: absolute !important;
            top: 0 !important;
            left: 0 !important;
            padding: 0 !important;
            margin: 0 !important;
            z-index: 11 !important;
            pointer-events: none !important;
            background-repeat: repeat !important;
            opacity: 0.5 !important;
            display: block !important;
            visibility: visible !important;
            clip: initial !important;
            clip-path: initial !important;
            transform: initial !important;
        }
    </style>
</head>

<body>

    <div class="div1">
        <div id="watermark-container" class="watermarked"></div>
    </div>


    <script>

        document.addEventListener('DOMContentLoaded', function () {
            ensureWatermarkExists();
            observeDocument();
        });

        function ensureWatermarkExists() {
            let container = document.getElementById('watermark-container');
            if (!container) {
                container = document.createElement('div');
                container.id = 'watermark-container';
                document.body.appendChild(container);
            }
            resetStyles(container);
        }

        function resetStyles(element) {
            // 定义原始样式
            const originalStyles = {
                width: '1109px',
                height: '4042px',
                position: 'absolute',
                top: '0',
                left: '0',
                padding: '0',
                margin: '0',
                zIndex: '11',
                pointerEvents: 'none',
                backgroundRepeat: 'repeat',
                opacity: '0.5',
                display: 'block',
                visibility: 'visible',
                clip: 'initial',
                clipPath: 'initial',
                transform: 'initial'
            };

            // 应用原始样式
            for (const [key, value] of Object.entries(originalStyles)) {
                element.style[key] = value;
            }
            createWatermark();
        }

        let originalParentNode = null;  // 用于保存原始的父节点
        let originalNextSibling = null;  // 用于保存原始的相对位置

        function ensureWatermarkExists() {
            let container = document.getElementById('watermark-container');
            if (!container) {
                container = document.createElement('div');
                container.id = 'watermark-container';
                // 在正确的位置插入
                if (originalParentNode) {
                    originalParentNode.insertBefore(container, originalNextSibling);
                } else {
                    // 如果没有原始位置信息,直接添加到文档的末尾
                    document.body.appendChild(container);
                }
            }
            resetStyles(container);
        }

        function observeDocument() {
            const observer = new MutationObserver(function(mutations) {
                mutations.forEach(function(mutation) {
                    if (mutation.type === 'childList') {
                        let watermarkRemoved = Array.from(mutation.removedNodes).some(node => node.id === 'watermark-container');
                        if (watermarkRemoved) {
                            // 记住原始位置
                            originalParentNode = mutation.target;
                            originalNextSibling = findNextSibling(mutation);

                            console.log('watermark-container 节点被删除,尝试重新创建...');
                            ensureWatermarkExists();
                        }
                    } else if (mutation.type === 'attributes' && mutation.target.id === 'watermark-container') {
                        console.log('watermark-container 样式被修改,尝试重置...');
                        resetStyles(mutation.target);
                    }
                });
            });

            // 配置观察器选项:
            const config = { childList: true, subtree: true, attributes: true };

            // 开始观察整个文档
            observer.observe(document, config);
        }

        // 找到下一个兄弟节点
        function findNextSibling(mutation) {
            const siblings = Array.from(mutation.target.childNodes);
            const indexOfRemovedNode = siblings.findIndex(node => node.id === 'watermark-container');
            return siblings[indexOfRemovedNode + 1];
        }



        function createWatermark() {
            const watermarkText = 'zhangsan(张三)1234332'; // 水印文本
            const fontSize = 20; // 字体大小

            const canvas = document.createElement('canvas');
            fillText(canvas, watermarkText, fontSize);

            // 将画布作为背景图像应用到容器中
            const container = document.getElementById('watermark-container');
            container.style.backgroundImage = `url(${canvas.toDataURL()})`;
        }
        function fillText(canvas, txt, fontSize) {
            const ctx = canvas.getContext('2d');
            const ratio = window.devicePixelRatio || 1;

            let width = 300;
            let height = 300;

            canvas.width = width;
            canvas.height = height;
            canvas.style.width = width + 'px';
            canvas.style.height = height + 'px';

            // 计算新字体大小
            ctx.fillStyle = 'red';
            ctx.font = fontSize + 'px serif';
            let textWidth = ctx.measureText(txt).width;
            let availableWidth = Math.sqrt((width * width) / 2); // 考虑到旋转后的可用宽度
            let newFontSize = Math.min(fontSize, availableWidth / textWidth * fontSize) * ratio;
            ctx.font = newFontSize + 'px serif';
            textWidth = ctx.measureText(txt).width; // 重新计算调整后的文本宽度

            // 旋转文本
            ctx.translate(width / 2, height / 2); // 将旋转中心移至画布中心
            ctx.rotate(-45 * Math.PI / 180); // 旋转 -45 度

            // 重新定位并填充文本
            ctx.fillText(txt, -textWidth / 2, newFontSize / 4);
        }


    </script>
</body>

</html>

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

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

相关文章

基于原子搜索算法优化概率神经网络PNN的分类预测 - 附代码

基于原子搜索算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于原子搜索算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于原子搜索优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

windows安装Git【超详细图解】

目录 git安装地址 git配置 提交代码时使用的命令 git安装地址 Git for WindowsWe bring the awesome Git VCS to Windowshttps://gitforwindows.org/ 打开终端&#xff0c;输入git --version git配置 git config --global user.name "用户名" git config --g…

镀膜与干刻中的平均自由程是什么?

在芯片制造中&#xff0c;镀膜和干刻是其中的重要环节&#xff0c;通常要用到CVD&#xff0c;RIE等技术&#xff0c;对材料表面进行纳米级的精细操作。在这些工序中&#xff0c;原子&#xff0c;分子&#xff0c;离子等&#xff0c;会在气体或真空中进行自由运动&#xff0c;直…

基础模型的自然语言处理能力综述

NLP作为一个领域为基础模型开辟了道路。虽然这些模型在标准基准测试中占据主导地位&#xff0c;但这些模型目前获得的能力与那些将语言描述为人类交流和思维的复杂系统的能力之间存在明显的差距。针对这一点&#xff0c;我们强调语言变异的全部范围&#xff08;例如&#xff0c…

Yolov5安装运行过程中出现的问题

Yolov5安装运行过程中出现的问题合集 安装问题pip 安装 requirements.txtcmd下如何退出python&#xff1f;升级numpy protobuf版本过高AttributeError: Can’t get attribute ‘SPPF’ on <module ‘models.common’ from 地址找不到图片NameError: name warnings is not de…

想要精通算法和SQL的成长之路 - 摩尔投票法的运用

想要精通算法和SQL的成长之路 - 摩尔投票法的运用 前言一. 多数元素1.1 摩尔投票法 二. 多数元素II2.1 分析 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 多数元素 原题链接 1.1 摩尔投票法 简单来说&#xff0c;假设数组 num 的众数是 x&#xff0c;数组长度为n。 有…

基于Java+SpringBoot+Vue3+Uniapp+TypeScript(有视频教程)前后端分离健身预约系统设计与实现

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

UE5制作场景时的小技巧和注意事项

UE5制作场景时的小技巧和注意事项 一、场景相关 1.1灯光 1.1.1构建完光照,发现场景都是黑的 可能是所有灯光是静态灯光,把skylight改为动态,如果改完之后还是黑色的,那就在构建一次,就应该没问题了 1.1.2场景中有多个动态光会造成阴影闪烁 需要将skylight变为固定 1…

C语言之for while语句详解

C语言之for while语句详解 文章目录 C语言之for while语句详解简介1 while语句1.1while语句的格式1.2 while语句的实践 2 for2.1 for语句格式2.2 for循环的实践 3 do while3.1 do while语句格式3.2 do while循环的实践 3 循环中break和continue3.1 while语句中的break和continu…

STM32与ZigBee无线通信技术在工业自动化中的应用

工业自动化是指利用电子技术、计算机技术和通信技术等手段&#xff0c;对工厂、设备和生产过程进行自动化控制和管理的过程。在工业自动化中&#xff0c;可靠的无线通信技术对于实时数据的传输和设备的协同控制至关重要。本文将介绍STM32微控制器与ZigBee无线通信技术在工业自动…

君正X2100 读取CHIP_ID

每个处理器会有一个唯一的ID&#xff0c;这个ID可用做产品序列号&#xff0c;或其它。 X21000的CHIP_ID存放于芯片内部的efuse中&#xff0c;efuse是一次性可可编程存储器&#xff0c;初始值为全0&#xff0c;只能将0改为1&#xff0c;不能将1改为0。芯片出厂前会被写入一些信…

WPF中行为与触发器的概念及用法

完全来源于十月的寒流&#xff0c;感谢大佬讲解 一、行为 (Behaviors) behaviors的简单测试 <Window x:Class"Test_05.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winf…

STL简介

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解c中的STL库 > 毒鸡汤&#xff1a;路难行&a…

22款奔驰S450L升级流星雨大灯 感受最高配的数字大灯

“流星雨”数字大灯&#xff0c;极具辨识度&#xff0c;通过260万像素的数字微镜技术&#xff0c;实现“流星雨”仪式感与高度精确的光束分布&#xff1b;在远光灯模式下&#xff0c;光束精准度更达之前84颗LED照明的100倍&#xff0c;更新增坡道照明功能&#xff0c;可根据导航…

UE 程序化网格 计算横截面 面积

首先在构造函数内加上程序化网格&#xff0c;然后复制网格体到程序化网格组件上&#xff0c;将Static Mesh&#xff08;类型StaticMeshActor&#xff09;的静态网格体组件给到程序化网格体上 然后把StaticMesh&#xff08;类型为StaticMeshActor&#xff09;Instance暴漏出去 …

原型网络Prototypical Network的python代码逐行解释,新手小白也可学会!!-----系列6 (承接系列5)

文章目录 一、原始代码---随机采样和评估模型二、详细解释分析每一行代码 一、原始代码—随机采样和评估模型 def randomSample(self,D_set): #从D_set随机取支持集和查询集&#xff08;20个类中的其中一个类&#xff0c;shape为[20,105,105]&#xff09;index_list list(ran…

复旦大学EMBA深度链接深圳科创产业:聚焦智联,产融未来

作为科创成就的经济大区&#xff0c;深圳南山区通过跨界创新研发生态链条&#xff0c;领跑科创产业创新&#xff0c;以187.5平方公里的面积&#xff0c;雄踞着204家上市公司&#xff0c;地均生产总值产出达到了40.7亿元&#xff0c;相当于每平方公里出产超过1家上市公司&#x…

Java项目实战《苍穹外卖》 二、项目搭建

当我痛苦地站在你的面前 你不能说我一无所有 你不能说我两手空空 系列文章目录 苍穹外卖是黑马程序员2023年的Java实战项目&#xff0c;作为业余练手用&#xff0c;需要源码或者课程的可以找我&#xff0c;无偿分享 Java项目实战《苍穹外卖》 一、项目概述Java项目实战《苍穹外…

B-2:Linux系统渗透提权

B-2:Linux系统渗透提权 服务器场景:Server2204(关闭链接) 用户名:hacker 密码:123456 1.使用渗透机对服务器信息收集,并将服务器中SSH服务端口号作为flag提交; 使用nmap扫描,发现ssh服务端口为2283 Flag:2283 2.使用渗透机对服务器信息收集,并将服务器中…

复合、委托、继承

1. 单例模式 静态实例对象在getInstance函数中定义&#xff0c;这样只有在调用函数时才会生成对象 2. 复合 1. 类中封装另一个类某些功能&#xff1b; 2. 构造、析构的调用过程 指明了复合中如何调用被包含类的构造函数&#xff0c;可以直接写在初始化列表位置&#xff1b; 3.…