html案例:制作一个图片水印生成器,防止复印件被滥用

使用HTML结合JavaScript,可以轻松创建一个图片水印生成器,离线版本的单页面工具。该工具允许用户上传图片并添加自定义文本水印,生成带有可见标记的图像提供下载。这样,当复印件被滥用时,水印能提供所有权证明和溯源依据,有效防止非法复制和使用。

界面效果图如下:

代码部分:

html 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>高级图片加水印工具</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: 'Segoe UI', Arial, sans-serif;
            background-color: #f4f4f4;
            height: 100vh;
            overflow: hidden;
            display: flex;
        }
        .container {
            width: 100%;
            height: 100%;
            display: flex;
        }
        /* 图片预览区域 - 左侧 */
        .preview-panel {
            flex: 1;
            display: flex;
            justify-content: center;
            align-items: center;
            background-color: #eee;
            overflow: auto;
        }
        #canvas {
            max-width: 95vw;
            max-height: 95vh;
            border: 1px solid #ccc;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
            border-radius: 8px;
        }

        /* 参数控制面板 - 右侧 */
        .form-panel {
            width: 320px;
            background-color: #fff;
            padding: 20px;
            border-left: 1px solid #ddd;
            overflow-y: auto;
            height: 100vh;
        }
        .form-group {
            margin-bottom: 16px;
        }
        label {
            display: block;
            margin-bottom: 6px;
            font-weight: 500;
            color: #333;
        }
        input[type="text"],
        input[type="number"],
        input[type="range"],
        input[type="file"],
        select {
            width: 100%;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 14px;
        }
        input[type="color"] {
            width: 50px;
            height: 36px;
            cursor: pointer;
        }
        button {
            width: 100%;
            padding: 12px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            font-size: 16px;
            cursor: pointer;
            margin-top: 10px;
        }
        button:disabled {
            background-color: #ccc;
            cursor: not-allowed;
        }
        button:hover:not(:disabled) {
            background-color: #0056b3;
        }
        .switch {
            position: relative;
            display: inline-block;
            width: 50px;
            height: 24px;
        }
        .switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }
        .slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #ccc;
            transition: .4s;
            border-radius: 24px;
        }
        .slider:before {
            position: absolute;
            content: "";
            height: 18px;
            width: 18px;
            left: 4px;
            bottom: 3px;
            background-color: white;
            transition: .4s;
            border-radius: 50%;
        }
        input:checked + .slider {
            background-color: #007bff;
        }
        input:checked + .slider:before {
            transform: translateX(26px);
        }
        .switch-label {
            display: flex;
            align-items: center;
            gap: 10px;
        }
    </style>
</head>
<body>
<div class="container">
    <!-- 左侧:图片预览 -->
    <div class="preview-panel">
        <canvas id="canvas"></canvas>
    </div>

    <!-- 右侧:参数面板 -->
    <div class="form-panel">
        <h2 style="margin-bottom: 20px; color: #333;">水印设置</h2>

        <div class="form-group">
            <label for="imageInput">选择图片</label>
            <input type="file" id="imageInput" accept="image/*" />
        </div>

        <div class="form-group">
            <label for="presetSelect">选择预设:</label>
            <select id="presetSelect" onchange="applyPreset()">
                <option value="">请选择...</option>
                <!-- 预设选项将通过JavaScript动态生成 -->
            </select>
        </div>

        <div class="form-group">
            <label for="watermarkText">水印文字</label>
            <input type="text" id="watermarkText" placeholder="请输入水印文字" value="复印无效" />
        </div>
        
        <div class="form-group">
            <label class="switch-label">
                <span>开启水印错位</span>
                <label class="switch">
                    <input type="checkbox" id="staggeredToggle" />
                    <span class="slider"></span>
                </label>
            </label>
            <small style="color: #666;">开启后奇数行和偶数行水印交错排列,防拼接</small>
        </div>

        <div class="form-group">
            <label for="angle">倾斜角度(度)</label>
            <input type="number" id="angle" min="-360" max="360" step="1" value="45" />
        </div>

        <div class="form-group">
            <label for="color">水印颜色</label>
            <input type="color" id="color" value="#FF0000" />
        </div>

        <div class="form-group">
            <label for="fontSize">字体大小(px)</label>
            <input type="number" id="fontSize" min="10" max="100" step="1" value="32" />
        </div>

        <div class="form-group">
            <label for="fontFamily">字体</label>
            <select id="fontFamily">
                <option value="Arial">Arial</option>
                <option value="SimHei">黑体</option>
                <option value="SimSun">宋体</option>
                <option value="Microsoft YaHei">微软雅黑</option>
            </select>
        </div>

        <div class="form-group">
            <label for="opacity">透明度(0.0 - 1.0)</label>
            <input type="range" id="opacity" min="0" max="1" step="0.05" value="0.3" />
            <span id="opacityValue" style="font-size:12px; color:#666;">0.3</span>
        </div>

        <div class="form-group">
            <label for="horizontalSpacing">横向间距(px)</label>
            <input type="number" id="horizontalSpacing" min="0" value="100" />
        </div>

        <div class="form-group">
            <label for="verticalSpacing">纵向间距(px)</label>
            <input type="number" id="verticalSpacing" min="0" value="100" />
        </div>

        <button id="downloadBtn" disabled>📥 下载带水印图片</button>
    </div>
</div>

<script>    
    // 定义预设配置
    const presets = [
        // 综合
        { name: "租房合同使用", text: "本复印件仅用于租赁XX小区XX房使用,复印无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "购房资格审核", text: "本件仅用于购房资格审查,不得用于其他用途。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "公司入职材料", text: "本复印件仅用于XX公司员工入职档案建立,不得外泄。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "子女入学报名", text: "本复印件仅用于XX小学2025年秋季入学报名使用,他用无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "公积金提取", text: "本复印件仅用于住房公积金提取申请,不得挪作他用。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "社保办理", text: "本件仅用于社会保险参保登记,复印无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "医保报销", text: "本复印件仅用于医疗保险费用报销,有效期至2025年12月31日。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "签证申请", text: "本复印件仅用于申办XX国旅游签证,他用无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "营业执照办理", text: "本件仅用于个体工商户营业执照申请,复印无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "婚姻登记", text: "本复印件仅用于结婚登记手续,不得用于其他用途。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "银行开户", text: "本复印件仅用于在XX银行开立个人账户,他用无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "学历认证", text: "本复印件仅用于学信网学历认证,有效期至2025年12月。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "医院住院登记", text: "本复印件仅用于XX医院住院身份核验,不得外传。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "商业保险投保", text: "本复印件仅用于重大疾病保险投保审核,他用无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "理赔材料提交", text: "本件仅用于意外伤害保险理赔,不得用于其他索赔。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "商标注册材料", text: "本复印件仅用于个体工商户商标注册申请,他用无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "公司股东变更", text: "本件仅用于XX有限公司股东信息变更备案,复印无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "招投标文件附件", text: "本复印件仅作为XX项目投标文件组成部分,不得单独使用。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "物业装修备案", text: "本件仅用于住宅装修手续登记,有效期至2025年11月30日。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "小区停车位登记", text: "本复印件仅用于业主固定停车位信息备案,他用无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "社区志愿者登记", text: "本复印件仅用于社区志愿服务信息录入,不得外泄。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "遗产继承公证", text: "本件仅用于遗产继承公证程序,复印无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "保险理赔", text: "本复印件仅用于XX保险公司意外险理赔申请,他用无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "物业备案", text: "本复印件仅用于XX小区业主信息备案,不得外传。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "政府补贴申请", text: "本复印件仅用于2025年低收入家庭住房补贴申请,他用无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "人才落户申请", text: "本复印件仅用于XX市人才引进落户申请,他用无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "积分入户材料", text: "本件仅用于2025年XX市积分入户审核,复印无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "公租房申请", text: "本复印件仅用于公共租赁住房资格审核,不得用于其他用途。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "低保申请", text: "本复印件仅用于城乡居民最低生活保障申请,有效期至2025年12月。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "出国定居材料", text: "本复印件仅用于移民XX国定居手续,不得另作他用。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "护照换发", text: "本件仅用于普通护照换发申请,有效期至2025年11月30日。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "法院立案材料", text: "本复印件仅用于XX民事案件立案,不得用于其他诉讼。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "仲裁申请", text: "本复印件仅用于劳动争议仲裁提交材料,他用无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "公证委托书附件", text: "本件仅作为委托公证的附件使用,不得单独使用。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "遗产分割协议备案", text: "本复印件仅用于遗产分割协议备案,复印无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "驾校报名", text: "本复印件仅用于XX驾校C1驾驶证培训报名使用。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "电力开户", text: "本复印件仅用于住宅用电户名登记,不得外泄。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "燃气开户", text: "本件仅用于家庭燃气开户手续,仅限XX燃气公司使用。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "宽带安装", text: "本复印件仅用于XX运营商宽带业务办理,复印无效。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "手机实名制登记", text: "本复印件仅用于手机号码实名认证,不得用于其他业务。", color: "#0000FF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },


        // 银行类
        { name: "银行贷款专用", text: "本复印件仅用于XX银行个人贷款申请,他用无效。", color: "#FFD700", fontFamily: "Microsoft YaHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "信用卡申请", text: "本件仅用于XX银行信用卡审批,他用无效。", color: "#FFD700", fontFamily: "Microsoft YaHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "贷款结清证明使用", text: "本复印件仅用于贷款结清后征信更新材料,不得挪用。", color: "#FFD700", fontFamily: "Microsoft YaHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },


        // 政务类
        { name: "车辆年检", text: "本复印件仅用于机动车年度检验,复印无效。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "残疾证办理", text: "本件仅用于残疾人证申领,仅供残联审核使用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "老年证办理", text: "本复印件仅用于60周岁以上老年人优待证办理,他用无效。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "驾驶证换证", text: "本件仅用于驾驶证期满换证手续,他用无效。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "房屋过户登记", text: "本复印件仅用于房产过户手续,复印无效,仅供XX不动产登记中心使用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "车辆上牌", text: "本复印件仅用于机动车注册登记,仅供车管所使用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "法律诉讼材料", text: "本复印件作为XX案件证据提交法院使用,不得另作他用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "学术论文查重材料", text: "本复印件仅用于职称评审论文查重,仅供人事部门使用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "教师资格认定", text: "本复印件仅用于中小学教师资格认定申请,复印无效。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "职称评审材料", text: "本件仅用于2025年度高级工程师职称评审,不得另用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "科研项目申报", text: "本复印件仅用于国家自然科学基金项目申报材料。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "专利申请辅助材料", text: "本件仅用于发明人身份证明,仅供知识产权局审核。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "法人身份证明", text: "本复印件仅用于企业法人代表身份核验,仅供市场监管局使用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "房屋租赁备案", text: "本复印件仅用于房屋租赁合同备案登记,有效期至2025年12月。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "房东缴税材料", text: "本件仅用于出租房屋个人所得税申报,仅供税务部门使用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "港澳通行证签注", text: "本复印件仅用于个人旅游签注办理,仅供出入境管理部门使用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "监护人资格证明", text: "本复印件仅用于未成年人监护关系确认,仅供民政部门使用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "领养手续材料", text: "本件仅用于收养登记程序,不得用于其他用途。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "军人家属优待证办理", text: "本复印件仅用于现役军人家属优待政策申请,他用无效。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "退役军人补贴申领", text: "本件仅用于退役军人生活补贴申报,有效期至2025年12月。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "创业补贴申请", text: "本复印件仅用于高校毕业生创业补贴申请,不得挪用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "失业登记", text: "本件仅用于失业人员信息登记,仅供就业服务中心使用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "灵活就业参保", text: "本复印件仅用于灵活就业人员社保参保,复印无效。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "生育津贴申领", text: "本复印件仅用于生育保险待遇申请,有效期至2025年12月31日。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "新生儿上户口", text: "本复印件仅用于新生儿出生登记,仅供派出所使用。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "疫苗接种证明", text: "本件仅用于儿童预防接种信息核验,他用无效。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },
        { name: "工伤认定材料", text: "本件仅用于工伤事故认定申请,不得用于保险骗赔。", color: "#CCCCFF", fontFamily: "SimHei", fontSize: 50, opacity: 0.25, horizontalSpacing: 1200, verticalSpacing: 160, angle: 45 },

    ];

    // 获取 DOM 元素
    const imageInput = document.getElementById('imageInput');
    const watermarkText = document.getElementById('watermarkText');
    const downloadBtn = document.getElementById('downloadBtn');
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    let originalImage = null;
    
    const opacitySlider = document.getElementById('opacity');
    const opacityValue = document.getElementById('opacityValue');
    const staggeredToggle = document.getElementById('staggeredToggle');// 错位开关
    
    // 显示当前透明度值
    opacityValue.textContent = opacitySlider.value;
    opacitySlider.addEventListener('input', () => {
        opacityValue.textContent = opacitySlider.value;
        updatePreview();
    });

    // === 动态填充预设选项 ===
    function populatePresets() {
        presets.forEach(preset => {
            const option = document.createElement('option');
            option.value = JSON.stringify(preset);
            option.textContent = preset.name;
            presetSelect.appendChild(option);
        });
    }
    // === 应用选中的预设 ===
    function applyPreset() {
        const selectedOption = presetSelect.options[presetSelect.selectedIndex];
        if (!selectedOption.value) return;

        const preset = JSON.parse(selectedOption.value);

        watermarkText.value = preset.text;
        document.getElementById('color').value = preset.color;
        document.getElementById('fontSize').value = preset.fontSize;
        document.getElementById('fontFamily').value = preset.fontFamily;
        document.getElementById('opacity').value = preset.opacity;
        document.getElementById('horizontalSpacing').value = preset.horizontalSpacing;
        document.getElementById('verticalSpacing').value = preset.verticalSpacing;
        document.getElementById('angle').value = preset.angle;

        updatePreview();
    }
    
    // === 更新预览 ===
    function updatePreview() {
        if (!originalImage) return;

        // 获取所有参数
        const text = watermarkText.value.trim() || '水印';
        const angle = parseInt(document.getElementById('angle').value);
        const color = document.getElementById('color').value;
        const fontSize = document.getElementById('fontSize').value + 'px';
        const fontFamily = document.getElementById('fontFamily').value;
        const opacity = parseFloat(document.getElementById('opacity').value);
        const horizontalSpacing = parseInt(document.getElementById('horizontalSpacing').value);
        const verticalSpacing = parseInt(document.getElementById('verticalSpacing').value);
        const isStaggered = staggeredToggle.checked; // 是否开启错位

        // 设置画布尺寸为原图大小
        canvas.width = originalImage.naturalWidth;
        canvas.height = originalImage.naturalHeight;

        // 清空画布
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // 绘制原始图片
        ctx.drawImage(originalImage, 0, 0);

        // 开始绘制水印
        ctx.save();
        ctx.font = `bold ${fontSize} ${fontFamily}`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';

        // 解析颜色并设置透明度
        const r = parseInt(color.slice(1, 3), 16);
        const g = parseInt(color.slice(3, 5), 16);
        const b = parseInt(color.slice(5, 7), 16);
        ctx.fillStyle = `rgba(${r}, ${g}, ${b}, ${opacity})`;

        // 计算中心点用于旋转
        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;

        // 应用旋转
        ctx.translate(centerX, centerY);
        ctx.rotate(angle * Math.PI / 180);

        // 网格状绘制水印
        const stepX = horizontalSpacing;
        const stepY = verticalSpacing;

        // 平铺水印
        //for (let y = -canvas.height; y < canvas.height * 2; y += stepY) {
        //    for (let x = -canvas.width; x < canvas.width * 2; x += stepX) {
        //        ctx.fillText(text, x, y);
        //    }
        //}
        // 错位水印
        let row = 0;
        for (let y = -canvas.height; y < canvas.height * 2; y += stepY) {
            const offsetX = isStaggered && row % 2 === 1 ? stepX / 2 : 0; // 奇数行偏移一半
            for (let x = -canvas.width; x < canvas.width * 2; x += stepX) {
                ctx.fillText(text, x + offsetX, y);
            }
            row++;
        }

        ctx.restore(); // 恢复变换

        // 启用下载按钮
        downloadBtn.disabled = false;
    }

    // === 图片上传:图片选择事件 ===
    imageInput.addEventListener('change', function (e) {
        const file = e.target.files[0];
        if (!file) return;

        const reader = new FileReader();
        reader.onload = function (event) {
            originalImage = new Image();
            originalImage.onload = function () {
                updatePreview(); // 图片加载完成后再绘制
            };
            originalImage.src = event.target.result;
        };
        reader.readAsDataURL(file);
    });

    // 
    // === 所有输入绑定更新:监听所有参数输入事件 ===
    [
        watermarkText,
        document.getElementById('staggeredToggle'),
        document.getElementById('angle'),
        document.getElementById('color'),
        document.getElementById('fontSize'),
        document.getElementById('fontFamily'),
        document.getElementById('opacity'),
        document.getElementById('horizontalSpacing'),
        document.getElementById('verticalSpacing')
    ].forEach(input => {
        input.addEventListener('input', updatePreview);
    });

    // === 下载功能:下载按钮点击事件 ===
    downloadBtn.addEventListener('click', function () {
        //const link = document.createElement('a');
        //link.href = canvas.toDataURL('image/png');
        //link.download = 'watermarked_image.png';
        //link.click();
        
        
        // 使用 JPEG 格式,质量设为 0.8(80% 质量,文件更小)
        const dataURL = canvas.toDataURL('image/jpeg', 0.8);
        const link = document.createElement('a');
        link.href = dataURL;
        link.download = `watermarked_image_${Date.now()}.jpg`;
        link.click();
    });

    // 页面加载时初始化
    window.onload = () => {
        populatePresets();
    };
</script>
</body>
</html>
相关推荐
寒月霜华4 小时前
JavaWeb-html、css-网页正文制作
前端·css·html
执沐4 小时前
HTML实现流星雨
前端·html
*濒危物种*4 小时前
HTML标签语法,基本框架
前端·css·html
软件技术NINI4 小时前
html css js网页制作成品——HTML+CSS+js早餐铺网页设计(4页)附源码
javascript·css·html
^_^ 纵歌4 小时前
rust主要用于哪些领域
开发语言·后端·rust
IT_陈寒4 小时前
Vue3性能优化实战:这7个技巧让我的应用提速50%,尤雨溪都点赞!
前端·人工智能·后端
_OP_CHEN4 小时前
C++基础:(十三)list类的模拟实现
开发语言·c++·反向迭代器·stl·list·list模拟实现·vector和list对比
froginwe114 小时前
R Excel 文件:高效数据处理与可视化分析利器
开发语言
艾小码4 小时前
前端必备:JS数组与对象完全指南,看完秒变数据处理高手!
前端·javascript