```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>智能盖章工具</title>
<style>
:root {
--primary: #4361ee;
--success: #06d6a0;
--danger: #ef476f;
--warning: #ffd166;
--dark: #1e293b;
--light: #f8fafc;
--gray: #94a3b8;
--border: #e2e8f0;
--shadow: 0 4px 12px rgba(0,0,0,0.08);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', system-ui, sans-serif;
}
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.app {
max-width: 1400px;
margin: 0 auto;
background: white;
border-radius: 16px;
overflow: hidden;
box-shadow: var(--shadow);
}
.header {
background: var(--dark);
color: white;
padding: 24px;
text-align: center;
}
.header h1 {
font-weight: 600;
font-size: 28px;
margin-bottom: 8px;
}
.header p {
color: var(--gray);
font-size: 16px;
}
.main {
display: flex;
min-height: 700px;
}
.sidebar {
width: 320px;
background: var(--light);
border-right: 1px solid var(--border);
padding: 24px;
}
.content {
flex: 1;
position: relative;
background: #f1f5f9;
}
.panel {
background: white;
border-radius: 12px;
padding: 20px;
margin-bottom: 24px;
box-shadow: var(--shadow);
}
.panel-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 16px;
color: var(--dark);
display: flex;
align-items: center;
gap: 8px;
}
.btn {
display: block;
width: 100%;
padding: 14px;
border: none;
border-radius: 10px;
font-weight: 500;
font-size: 15px;
cursor: pointer;
transition: all 0.2s;
margin-bottom: 12px;
}
.btn-primary {
background: var(--primary);
color: white;
}
.btn-success {
background: var(--success);
color: white;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0,0,0,0.12);
}
.btn:active {
transform: translateY(0);
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--dark);
font-size: 14px;
}
.form-control {
width: 100%;
padding: 12px;
border: 2px solid var(--border);
border-radius: 10px;
font-size: 15px;
transition: border-color 0.2s;
}
.form-control:focus {
outline: none;
border-color: var(--primary);
}
.slider-container {
display: flex;
align-items: center;
gap: 12px;
}
.slider {
flex: 1;
}
.slider-value {
width: 40px;
text-align: center;
font-weight: 500;
color: var(--dark);
}
#canvas {
position: absolute;
top: 20px;
left: 20px;
background: white;
box-shadow: 0 8px 24px rgba(0,0,0,0.1);
cursor: crosshair;
}
.stamp {
position: absolute;
cursor: move;
user-select: none;
}
.stamp-handle {
position: absolute;
top: -32px;
right: 0;
display: flex;
gap: 6px;
}
.handle-btn {
width: 28px;
height: 28px;
border: none;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
transition: all 0.2s;
}
.delete-btn {
background: var(--danger);
color: white;
}
.handle-btn:hover {
transform: scale(1.1);
}
.empty-state {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
color: var(--gray);
}
.empty-state i {
font-size: 64px;
margin-bottom: 16px;
opacity: 0.3;
}
.empty-state p {
font-size: 18px;
margin-bottom: 24px;
}
.toolbar {
position: absolute;
top: 20px;
left: 20px;
background: rgba(255,255,255,0.95);
backdrop-filter: blur(10px);
padding: 12px 16px;
border-radius: 12px;
box-shadow: var(--shadow);
display: flex;
gap: 12px;
}
.tool-btn {
width: 40px;
height: 40px;
border: none;
border-radius: 10px;
background: white;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
transition: all 0.2s;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.tool-btn:hover {
background: var(--primary);
color: white;
transform: translateY(-2px);
}
.page-info {
position: absolute;
bottom: 20px;
right: 20px;
background: rgba(0,0,0,0.7);
color: white;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
}
</style>
</head>
<body>
<div class="app">
<div class="header">
<h1>智能盖章工具</h1>
<p>拖拽定位 · 混合模式 · 骑缝章 · 一键下载</p>
</div>
<div class="main">
<div class="sidebar">
<div class="panel">
<h3 class="panel-title">📁 文件管理</h3>
<div class="form-group">
<label class="form-label">上传文档</label>
<input type="file" id="fileInput" class="form-control" accept=".pdf,.jpg,.jpeg,.png">
</div>
<div class="form-group">
<label class="form-label">上传印章</label>
<input type="file" id="stampInput" class="form-control" accept=".png,.jpg,.jpeg">
</div>
<button class="btn btn-primary" onclick="addStamp('normal')">
➕ 添加印章
</button>
<button class="btn btn-primary" onclick="addStamp('seam')">
📎 骑缝章
</button>
</div>
<div class="panel">
<h3 class="panel-title">⚙️ 印章设置</h3>
<div class="form-group">
<label class="form-label">混合模式</label>
<select id="blendMode" class="form-control">
<option value="normal">正常</option>
<option value="multiply">正片叠底</option>
<option value="screen">滤色</option>
<option value="overlay">叠加</option>
<option value="darken">变暗</option>
<option value="lighten">变亮</option>
</select>
</div>
<div class="form-group">
<label class="form-label">透明度 <span id="opacityValue">80%</span></label>
<div class="slider-container">
<input type="range" id="opacity" class="form-control slider" min="0" max="100" value="80">
</div>
</div>
<div class="form-group">
<label class="form-label">大小 <span id="sizeValue">150px</span></label>
<div class="slider-container">
<input type="range" id="size" class="form-control slider" min="50" max="300" value="150">
</div>
</div>
</div>
<button class="btn btn-success" onclick="download()">
⬇️ 下载文件
</button>
</div>
<div class="content">
<canvas id="canvas"></canvas>
<div class="empty-state" id="emptyState">
<div style="font-size: 64px; margin-bottom: 16px;">📝</div>
<p>请上传文件开始使用</p>
<button class="btn btn-primary" onclick="document.getElementById('fileInput').click()">
上传文件
</button>
</div>
</div>
</div>
</div>
<script>
// 全局变量
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let fileData = null;
let stamps = [];
let selectedStamp = null;
let isDragging = false;
let dragOffset = { x: 0, y: 0 };
// 初始化
canvas.width = 800;
canvas.height = 600;
// 事件监听
document.getElementById('fileInput').addEventListener('change', handleFileUpload);
document.getElementById('stampInput').addEventListener('change', handleStampUpload);
document.getElementById('blendMode').addEventListener('change', updateStampProperty);
document.getElementById('opacity').addEventListener('input', updateOpacity);
document.getElementById('size').addEventListener('input', updateSize);
// 文件上传处理
function handleFileUpload(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(event) {
fileData = {
data: event.target.result,
type: file.type
};
loadFile();
document.getElementById('emptyState').style.display = 'none';
};
reader.readAsDataURL(file);
}
// 印章上传处理
function handleStampUpload(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(event) {
if (stamps.length > 0) {
stamps[stamps.length - 1].src = event.target.result;
render();
}
};
reader.readAsDataURL(file);
}
// 加载文件
function loadFile() {
if (fileData.type.includes('image')) {
const img = new Image();
img.onload = function() {
canvas.width = img.width > 1200 ? 1200 : img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
img.src = fileData.data;
} else {
// PDF模拟显示
canvas.width = 800;
canvas.height = 1000;
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#333';
ctx.font = '24px Arial';
ctx.textAlign = 'center';
ctx.fillText('PDF文档预览', canvas.width/2, canvas.height/2);
}
}
// 添加印章
function addStamp(type) {
if (!fileData) {
alert('请先上传文件');
return;
}
const defaultStamp = createDefaultStamp();
const stamp = {
id: Date.now(),
x: 100,
y: 100,
width: 150,
height: 150,
src: defaultStamp,
type: type,
blend: 'normal',
opacity: 0.8
};
stamps.push(stamp);
render();
}
// 创建默认印章
function createDefaultStamp() {
const tempCanvas = document.createElement('canvas');
const size = 100;
tempCanvas.width = size;
tempCanvas.height = size;
const tempCtx = tempCanvas.getContext('2d');
// 圆形红色印章
tempCtx.beginPath();
tempCtx.arc(size/2, size/2, size/2-2, 0, 2 * Math.PI);
tempCtx.fillStyle = '#ef476f';
tempCtx.fill();
tempCtx.strokeStyle = '#d90429';
tempCtx.lineWidth = 2;
tempCtx.stroke();
// 文字
tempCtx.fillStyle = 'white';
tempCtx.font = 'bold 14px Arial';
tempCtx.textAlign = 'center';
tempCtx.fillText('印章', size/2, size/2 + 5);
return tempCanvas.toDataURL();
}
// 渲染
function render() {
// 重新绘制背景
if (fileData) {
if (fileData.type.includes('image')) {
const img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
drawStamps();
};
img.src = fileData.data;
} else {
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#333';
ctx.font = '24px Arial';
ctx.textAlign = 'center';
ctx.fillText('PDF文档', canvas.width/2, canvas.height/2);
drawStamps();
}
} else {
ctx.fillStyle = '#f8fafc';
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawStamps();
}
}
// 绘制印章
function drawStamps() {
stamps.forEach(stamp => {
const img = new Image();
img.onload = function() {
ctx.save();
ctx.globalCompositeOperation = stamp.blend;
ctx.globalAlpha = stamp.opacity;
if (stamp.type === 'seam') {
drawSeamStamp(img, stamp);
} else {
ctx.drawImage(img, stamp.x, stamp.y, stamp.width, stamp.height);
}
ctx.restore();
};
img.src = stamp.src;
});
}
// 绘制骑缝章
function drawSeamStamp(img, stamp) {
const segments = 3;
const segmentHeight = stamp.height / segments;
for (let i = 0; i < segments; i++) {
ctx.drawImage(
img,
0, i * segmentHeight, stamp.width, segmentHeight,
stamp.x, stamp.y + i * segmentHeight, stamp.width, segmentHeight
);
}
}
// 鼠标事件
canvas.addEventListener('mousedown', startDrag);
canvas.addEventListener('mousemove', drag);
canvas.addEventListener('mouseup', endDrag);
canvas.addEventListener('mouseleave', endDrag);
function startDrag(e) {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 检查印章点击
for (let i = stamps.length - 1; i >= 0; i--) {
const stamp = stamps[i];
if (x >= stamp.x && x <= stamp.x + stamp.width &&
y >= stamp.y && y <= stamp.y + stamp.height) {
selectedStamp = stamp;
isDragging = true;
dragOffset.x = x - stamp.x;
dragOffset.y = y - stamp.y;
return;
}
}
}
function drag(e) {
if (!isDragging || !selectedStamp) return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
selectedStamp.x = x - dragOffset.x;
selectedStamp.y = y - dragOffset.y;
render();
}
function endDrag() {
isDragging = false;
selectedStamp = null;
}
// 删除印章
function deleteStamp(id) {
stamps = stamps.filter(s => s.id !== id);
render();
}
// 更新印章属性
function updateStampProperty() {
if (selectedStamp) {
selectedStamp.blend = this.value;
render();
}
}
function updateOpacity() {
const value = this.value;
document.getElementById.getElementById