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>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
margin: 20px;
background-color: #f0f0f0;
}
h1 {
color: #333;
}
.container {
background-color: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
align-items: center;
}
input[type="file"], input[type="text"], select, input[type="number"], input[type="range"], input[type="color"] {
margin: 10px 0;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
width: 300px;
}
button {
margin: 10px;
padding: 10px 20px;
background-color: #007BFF;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
#canvas {
margin-top: 20px;
border: 1px solid #ccc;
box-shadow: 0 0 5px rgba(0,0,0,0.2);
display: none; /* 初始隐藏 canvas */
}
.options {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
width: 100%;
}
.option-group {
margin: 10px;
display: flex;
flex-direction: column;
align-items: center;
}
</style>
</head>
<body>
<div class="container">
<h1>图片文字水印工具</h1>
<input type="file" id="upload" accept="image/*">
<input type="text" id="watermark-text" placeholder="输入水印文字">
<div class="options">
<div class="option-group">
<label for="font-size">文字大小:</label>
<select id="font-size">
<option value="12">12px</option>
<option value="16">16px</option>
<option value="20" selected>20px</option>
<option value="24">24px</option>
</select>
</div>
<div class="option-group">
<label for="rotation">旋转角度(度):</label>
<input type="number" id="rotation" value="-45" step="1">
</div>
<div class="option-group">
<label for="font-family">字体:</label>
<select id="font-family">
<option value="Arial" selected>Arial</option>
<option value="Times New Roman">Times New Roman</option>
<option value="Courier New">Courier New</option>
<option value="Verdana">Verdana</option>
</select>
</div>
<div class="option-group">
<label for="opacity">透明度(0-100):</label>
<input type="range" id="opacity" min="0" max="100" value="50">
</div>
<div class="option-group">
<label for="horizontal-spacing">水平间距(像素):</label>
<input type="number" id="horizontal-spacing" value="50" min="10" step="10">
</div>
<div class="option-group">
<label for="vertical-spacing">行间距(像素):</label>
<input type="number" id="vertical-spacing" value="100" min="10" step="10">
</div>
<div class="option-group">
<label for="font-color">字体颜色选择:</label>
<input type="color" id="font-color" value="#FFFFFF">
</div>
</div>
<button id="add-watermark">添加水印</button>
<canvas id="canvas"></canvas>
<button id="download" style="display: none;">下载图片</button>
</div>
<script>
let originalImage = null;
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// 上传图片
document.getElementById('upload').addEventListener('change', function(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(event) {
const img = new Image();
img.onload = function() {
originalImage = img;
// 动态设置 canvas 尺寸为原图尺寸
canvas.width = img.width;
canvas.height = img.height;
// 绘制原图
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
document.getElementById('download').style.display = 'none';
// 显示 canvas
canvas.style.display = 'block';
};
img.src = event.target.result;
};
reader.readAsDataURL(file);
});
// 添加水印
document.getElementById('add-watermark').addEventListener('click', function() {
if (!originalImage) {
alert('请先上传图片');
return;
}
const text = document.getElementById('watermark-text').value;
if (!text) {
alert('请输入水印文字');
return;
}
const fontSize = document.getElementById('font-size').value;
const rotation = parseFloat(document.getElementById('rotation').value);
const fontFamily = document.getElementById('font-family').value;
const opacity = parseInt(document.getElementById('opacity').value) / 100;
const horizontalSpacing = parseInt(document.getElementById('horizontal-spacing').value);
const verticalSpacing = parseInt(document.getElementById('vertical-spacing').value);
const fontColor = document.getElementById('font-color').value;
// 清除 canvas 并重新绘制原图
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(originalImage, 0, 0, canvas.width, canvas.height);
// 设置水印样式
ctx.font = `${fontSize}px ${fontFamily}`;
ctx.fillStyle = `rgba(${parseInt(fontColor.slice(1, 3), 16)}, ${parseInt(fontColor.slice(3, 5), 16)}, ${parseInt(fontColor.slice(5, 7), 16)}, ${opacity})`;
ctx.textBaseline = 'middle';
ctx.save();
// 移动到 canvas 中心并旋转
ctx.translate(canvas.width / 2, canvas.height / 2);
const angle = rotation * Math.PI / 180;
ctx.rotate(angle);
// 计算水印布局
const textWidth = ctx.measureText(text).width;
const diagonal = Math.sqrt(canvas.width * canvas.width + canvas.height * canvas.height);
const cols = Math.ceil(diagonal / (textWidth + horizontalSpacing));
const rows = Math.ceil(diagonal / verticalSpacing);
// 绘制水印
for (let i = -rows; i < rows; i++) {
for (let j = -cols; j < cols; j++) {
const x = j * (textWidth + horizontalSpacing);
const y = i * verticalSpacing;
ctx.fillText(text, x, y);
}
}
ctx.restore();
document.getElementById('download').style.display = 'inline-block';
});
// 下载图片
document.getElementById('download').addEventListener('click', function() {
const dataURL = canvas.toDataURL('image/png');
const a = document.createElement('a');
a.href = dataURL;
a.download = 'watermarked-image.png';
a.click();
});
</script>
</body>
</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>
.container {
width: 500px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
}
canvas {
max-width: 100%;
margin-top: 15px;
border: 1px solid #ddd;
}
h1 {
font-size: 20px;
text-align: center;
margin-bottom: 20px;
}
.section {
margin-bottom: 15px;
}
input[type="file"] {
margin: 5px 0;
}
.buttons {
display: flex;
gap: 10px;
margin: 10px 0;
}
button {
padding: 8px 15px;
cursor: pointer;
background-color: #f0f0f0;
border: 1px solid #ccc;
border-radius: 4px;
}
#download {
display: block;
width: 100%;
padding: 10px;
background-color: #4CAF50;
color: white;
border: none;
margin-top: 15px;
}
.coord {
margin-top: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1>图片添加图片水印工具</h1>
<div class="section">
<label>选择背景图片:</label>
<input type="file" id="bgImage" accept="image/*">
<span id="bgFileName"></span>
</div>
<div class="section">
<label>上传图片水印:</label>
<input type="file" id="watermark" accept="image/*">
<span id="wmFileName"></span>
</div>
<div class="section">
<label>水印缩放比例:</label>
<input type="range" id="scale" min="10" max="100" value="50">
<span id="scaleValue">50%</span>
</div>
<div class="section">
<label>水印透明度:</label>
<input type="range" id="opacity" min="0" max="100" value="100">
<span id="opacityValue">100%</span>
</div>
<div class="section">
<h3>水印位置调整</h3>
<div class="buttons">
<button type="button" onclick="adjustY(-20)">上移</button>
<button type="button" onclick="adjustY(20)">下移</button>
<button type="button" onclick="adjustX(-20)">左移</button>
<button type="button" onclick="adjustX(20)">右移</button>
</div>
<div class="coord">
X 坐标:<span id="xCoord">90</span>
Y 坐标:<span id="yCoord">50</span>
</div>
</div>
<button type="button" id="download">下载生成图片</button>
<canvas id="previewCanvas"></canvas>
</div>
<script>
const canvas = document.getElementById('previewCanvas');
const ctx = canvas.getContext('2d');
let bgImage, watermarkImage;
// 文件加载处理
document.getElementById('bgImage').addEventListener('change', function(e) {
loadImage(e.target.files[0]).then(img => {
bgImage = img;
updateCanvasSize();
drawImages();
});
});
document.getElementById('watermark').addEventListener('change', function(e) {
loadImage(e.target.files[0]).then(img => {
watermarkImage = img;
drawImages();
});
});
// 图像加载函数
function loadImage(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => resolve(img);
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
}
// 更新Canvas尺寸匹配背景图
function updateCanvasSize() {
if (!bgImage) return;
canvas.width = bgImage.width;
canvas.height = bgImage.height;
}
// 核心绘制函数
function drawImages() {
if (!bgImage || !watermarkImage) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制背景图
ctx.drawImage(bgImage, 0, 0);
// 计算水印参数
const scale = document.getElementById('scale').value / 100;
const opacity = document.getElementById('opacity').value / 100;
const x = parseInt(document.getElementById('xCoord').textContent);
const y = parseInt(document.getElementById('yCoord').textContent);
// 绘制水印
ctx.save();
ctx.globalAlpha = opacity;
ctx.drawImage(
watermarkImage,
x, y,
watermarkImage.width * scale,
watermarkImage.height * scale
);
ctx.restore();
}
// 滑块和按钮事件绑定
document.getElementById('scale').addEventListener('input', function(e) {
document.getElementById('scaleValue').textContent = e.target.value + '%';
drawImages();
});
document.getElementById('opacity').addEventListener('input', function(e) {
document.getElementById('opacityValue').textContent = e.target.value + '%';
drawImages();
});
// 坐标调整函数(修改后)
function adjustX(delta) {
const x = parseInt(document.getElementById('xCoord').textContent) + delta;
document.getElementById('xCoord').textContent = x;
drawImages();
}
function adjustY(delta) {
const y = parseInt(document.getElementById('yCoord').textContent) + delta;
document.getElementById('yCoord').textContent = y;
drawImages();
}
// 下载功能
document.getElementById('download').addEventListener('click', () => {
if (!bgImage || !watermarkImage) {
alert('请先选择背景图和水印图');
return;
}
const link = document.createElement('a');
link.download = 'watermarked-image.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
</script>
</body>
</html>