我会为您生成一个可直接运行的HTML文档,它利用浏览器原生的Canvas功能,帮助您将彩色图片转换为黑白效果。

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">图片转黑白 · 脚本实现</title>
<style>
* {
box-sizing: border-box;
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}
body {
background: #f4f6f9;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 2rem 1rem;
}
.card {
background: white;
max-width: 800px;
width: 100%;
border-radius: 28px;
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.08);
padding: 2rem 2rem 2.5rem;
transition: 0.2s;
}
h1 {
font-size: 1.9rem;
font-weight: 500;
letter-spacing: -0.3px;
color: #1e293b;
margin-top: 0.2rem;
margin-bottom: 0.5rem;
display: flex;
align-items: center;
gap: 8px;
}
h1 small {
font-size: 1rem;
font-weight: 400;
color: #64748b;
margin-left: 8px;
}
.sub {
color: #475569;
border-left: 4px solid #94a3b8;
padding-left: 14px;
margin: 0.8rem 0 1.8rem 0;
font-size: 0.95rem;
background: #f8fafc;
border-radius: 0 8px 8px 0;
line-height: 1.5;
}
.upload-area {
border: 2px dashed #cbd5e1;
border-radius: 24px;
padding: 2.5rem 1rem;
text-align: center;
background: #fafcff;
transition: all 0.2s;
cursor: pointer;
}
.upload-area:hover {
border-color: #3b82f6;
background: #f0f7ff;
}
.upload-area.dragover {
border-color: #2563eb;
background: #eef2ff;
transform: scale(1.01);
}
.upload-icon {
font-size: 2.8rem;
margin-bottom: 8px;
}
.upload-text {
font-size: 1.1rem;
font-weight: 450;
color: #1e293b;
}
.upload-hint {
font-size: 0.9rem;
color: #64748b;
margin-top: 6px;
}
#fileInput {
display: none;
}
.preview-grid {
display: flex;
flex-wrap: wrap;
gap: 1.8rem;
margin: 2rem 0 1.2rem;
justify-content: center;
}
.img-box {
flex: 1 1 200px;
min-width: 160px;
background: #f1f5f9;
border-radius: 18px;
padding: 12px 12px 16px;
box-shadow: 0 2px 6px rgba(0,0,0,0.02);
transition: 0.1s;
}
.img-box p {
margin: 6px 0 4px 0;
font-weight: 450;
font-size: 0.9rem;
color: #334155;
display: flex;
justify-content: space-between;
}
.img-box img, .img-box canvas {
width: 100%;
height: auto;
border-radius: 12px;
display: block;
background: #ffffff;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
}
.action-bar {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
margin: 1.6rem 0 0.6rem;
gap: 12px;
}
.btn-group {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.btn {
background: #ffffff;
border: 1px solid #d1d9e8;
padding: 0.6rem 1.2rem;
border-radius: 40px;
font-weight: 500;
font-size: 0.9rem;
color: #1e293b;
cursor: pointer;
transition: 0.15s;
display: inline-flex;
align-items: center;
gap: 6px;
box-shadow: 0 1px 2px rgba(0,0,0,0.02);
}
.btn-primary {
background: #1e293b;
border: 1px solid #1e293b;
color: white;
}
.btn-primary:hover {
background: #0f172a;
border-color: #0f172a;
}
.btn-outline {
border: 1px solid #cbd5e1;
background: white;
}
.btn-outline:hover {
background: #f1f5f9;
}
.btn:active {
transform: scale(0.96);
}
.btn:disabled {
opacity: 0.4;
pointer-events: none;
filter: grayscale(0.3);
}
.slider-box {
display: flex;
align-items: center;
gap: 12px;
background: #f8fafc;
padding: 0.2rem 1rem 0.2rem 1.2rem;
border-radius: 40px;
border: 1px solid #e2e8f0;
}
.slider-box label {
font-size: 0.85rem;
font-weight: 450;
color: #334155;
}
input[type="range"] {
width: 100px;
height: 4px;
-webkit-appearance: none;
background: #cbd5e1;
border-radius: 4px;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: #1e293b;
cursor: pointer;
border: 2px solid white;
box-shadow: 0 1px 4px rgba(0,0,0,0.2);
}
.status {
color: #475569;
font-size: 0.9rem;
margin-top: 8px;
min-height: 28px;
padding: 0 4px;
}
.footer-note {
border-top: 1px solid #e9edf2;
margin-top: 1.8rem;
padding-top: 1.2rem;
font-size: 0.8rem;
color: #6b7280;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
@media (max-width: 550px) {
.card { padding: 1.5rem; }
.preview-grid { flex-direction: column; }
.action-bar { flex-direction: column; align-items: stretch; }
.slider-box { justify-content: center; }
}
</style>
</head>
<body>
<div class="card">
<h1>
🎯 图片转黑白
<small>· 浏览器脚本</small>
</h1>
<div class="sub">
⚡ 上传彩色图片,使用 Canvas 像素操作实时转换为黑白 · 可调节灰度强度
</div>
<!-- 上传区域 -->
<div class="upload-area" id="dropZone">
<div class="upload-icon">📸</div>
<div class="upload-text">点击上传 或 拖拽图片到这里</div>
<div class="upload-hint">支持 JPG / PNG / WebP</div>
<input type="file" id="fileInput" accept="image/*">
</div>
<!-- 预览区域 -->
<div class="preview-grid" id="previewContainer">
<div class="img-box" id="originalBox">
<p>🖼️ 原图 <span style="font-weight:400; color:#64748b;" id="originalInfo">—</span></p>
<img id="originalImage" src="" alt="原始图片" style="display:none;">
<canvas id="originalCanvas" style="display:none;"></canvas>
<div style="min-height:120px; display: flex; align-items: center; justify-content: center; color:#94a3b8;" id="placeholderOriginal">
<span>等待上传</span>
</div>
</div>
<div class="img-box" id="bwBox">
<p>⚫ 黑白效果 <span style="font-weight:400; color:#64748b;" id="bwInfo">—</span></p>
<canvas id="bwCanvas" style="display:none; width:100%; height:auto;"></canvas>
<div style="min-height:120px; display: flex; align-items: center; justify-content: center; color:#94a3b8;" id="placeholderBW">
<span>等待转换</span>
</div>
</div>
</div>
<!-- 控制栏 -->
<div class="action-bar">
<div class="btn-group">
<button class="btn btn-primary" id="convertBtn" disabled>✨ 转换为黑白</button>
<button class="btn btn-outline" id="downloadBtn" disabled>⬇ 下载黑白图</button>
</div>
<div class="slider-box">
<label for="strengthSlider">强度</label>
<input type="range" id="strengthSlider" min="0" max="100" value="100">
<span style="font-size:0.8rem; min-width:32px; font-weight:500;" id="strengthValue">100%</span>
</div>
</div>
<div class="status" id="statusMessage">💡 请上传一张图片开始</div>
<div class="footer-note">
<span>🛠 基于 Canvas · 像素级灰度算法</span>
<span>⚙️ 调节「强度」可混合原图色彩</span>
</div>
</div>
<script>
(function() {
// DOM 元素
const fileInput = document.getElementById('fileInput');
const dropZone = document.getElementById('dropZone');
const originalImage = document.getElementById('originalImage');
const originalCanvas = document.getElementById('originalCanvas');
const bwCanvas = document.getElementById('bwCanvas');
const placeholderOriginal = document.getElementById('placeholderOriginal');
const placeholderBW = document.getElementById('placeholderBW');
const convertBtn = document.getElementById('convertBtn');
const downloadBtn = document.getElementById('downloadBtn');
const strengthSlider = document.getElementById('strengthSlider');
const strengthValue = document.getElementById('strengthValue');
const statusMessage = document.getElementById('statusMessage');
const originalInfo = document.getElementById('originalInfo');
const bwInfo = document.getElementById('bwInfo');
// 状态
let currentFile = null; // 当前上传的文件
let imageLoaded = false; // 原图是否已加载到 img
let bwGenerated = false; // 是否已生成黑白图
let bwDataUrl = ''; // 最近生成的黑白 dataURL (用于下载)
// ---------- 辅助: 隐藏占位,显示图片/画布 ----------
function showOriginalElement() {
placeholderOriginal.style.display = 'none';
originalImage.style.display = 'block';
originalCanvas.style.display = 'none'; // 原图用img展示
}
function showOriginalPlaceholder() {
placeholderOriginal.style.display = 'flex';
originalImage.style.display = 'none';
originalCanvas.style.display = 'none';
}
function showBWCanvas() {
placeholderBW.style.display = 'none';
bwCanvas.style.display = 'block';
}
function showBWPlaceholder() {
placeholderBW.style.display = 'flex';
bwCanvas.style.display = 'none';
}
// ---------- 重置状态 (除了文件信息) ----------
function resetStates(keepFile = false) {
if (!keepFile) {
currentFile = null;
imageLoaded = false;
}
bwGenerated = false;
bwDataUrl = '';
convertBtn.disabled = true;
downloadBtn.disabled = true;
// 不重置滑块值
}
// ---------- 加载图片文件 ----------
function loadImageFile(file) {
// 校验类型
if (!file || !file.type.startsWith('image/')) {
statusMessage.textContent = '❌ 请上传有效的图片文件 (jpg/png/webp)';
return;
}
resetStates(true); // 保留 currentFile (下面会覆盖)
currentFile = file;
const reader = new FileReader();
reader.onload = (e) => {
const dataURL = e.target.result;
originalImage.onload = () => {
// 原图加载完成
imageLoaded = true;
// 显示原图
showOriginalElement();
// 更新图像信息
const w = originalImage.naturalWidth;
const h = originalImage.naturalHeight;
originalInfo.textContent = `${w} × ${h}`;
// 重置黑白区域
showBWPlaceholder();
bwInfo.textContent = '—';
bwGenerated = false;
bwDataUrl = '';
convertBtn.disabled = false;
downloadBtn.disabled = true;
statusMessage.textContent = `✅ 已加载: ${file.name} (${w}×${h}) · 点击「转换为黑白」`;
};
originalImage.onerror = () => {
statusMessage.textContent = '⚠️ 图片加载失败,请重试';
showOriginalPlaceholder();
resetStates(false);
};
originalImage.src = dataURL;
};
reader.onerror = () => {
statusMessage.textContent = '⚠️ 文件读取失败';
};
reader.readAsDataURL(file);
}
// ---------- 转换核心: 彩色 -> 黑白 (带强度混合) ----------
function convertToBlackWhite() {
if (!imageLoaded || !originalImage.complete || originalImage.naturalWidth === 0) {
statusMessage.textContent = '⚠️ 原图尚未加载完成,请等待';
return;
}
const img = originalImage;
const w = img.naturalWidth;
const h = img.naturalHeight;
// 设置原画布尺寸 (备用,但不一定显示)
originalCanvas.width = w;
originalCanvas.height = h;
const ctxOrig = originalCanvas.getContext('2d');
ctxOrig.drawImage(img, 0, 0);
// 设置黑白画布
bwCanvas.width = w;
bwCanvas.height = h;
const ctxBw = bwCanvas.getContext('2d');
// 先把原图画到黑白画布上 (作为基础)
ctxBw.drawImage(img, 0, 0);
// 获取像素数据
const imageData = ctxBw.getImageData(0, 0, w, h);
const data = imageData.data;
const strength = parseInt(strengthSlider.value, 10) / 100; // 0 ~ 1
// 如果没有强度 (strength=0) 直接就是原彩,但这种情况一般没必要,但仍处理
if (strength === 0) {
// 什么都不做,就是原图
} else {
// 遍历像素,应用灰度 + 混合
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// 标准灰度 luminosity 权重
const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
if (strength === 1) {
// 完全黑白
data[i] = gray; // R
data[i + 1] = gray; // G
data[i + 2] = gray; // B
} else {
// 混合: strength 控制黑白比例, (1-strength) 保留原色
const mixR = Math.round(gray * strength + r * (1 - strength));
const mixG = Math.round(gray * strength + g * (1 - strength));
const mixB = Math.round(gray * strength + b * (1 - strength));
data[i] = mixR;
data[i + 1] = mixG;
data[i + 2] = mixB;
}
// alpha 不变
}
}
// 将处理后的数据放回
ctxBw.putImageData(imageData, 0, 0);
// 显示黑白画布
showBWCanvas();
bwInfo.textContent = `${w} × ${h}`;
bwGenerated = true;
bwDataUrl = bwCanvas.toDataURL('image/png');
downloadBtn.disabled = false;
statusMessage.textContent = `🎯 黑白转换完成 (强度: ${strengthSlider.value}%)`;
}
// ---------- 下载黑白图片 ----------
function downloadBW() {
if (!bwGenerated || !bwDataUrl) {
statusMessage.textContent = '⚠️ 还没有可下载的黑白图片,请先转换';
return;
}
const link = document.createElement('a');
link.download = `黑白_${currentFile ? currentFile.name.replace(/\.[^.]+$/, '') : 'image'}.png`;
link.href = bwDataUrl;
link.click();
statusMessage.textContent = `⬇️ 下载已开始 (PNG 格式)`;
}
// ---------- 事件绑定 ----------
// 1. 上传按钮 / 拖拽
dropZone.addEventListener('click', () => fileInput.click());
fileInput.addEventListener('change', (e) => {
if (e.target.files && e.target.files.length > 0) {
loadImageFile(e.target.files[0]);
}
fileInput.value = ''; // 允许重复选择相同文件
});
// 拖拽事件
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('dragover');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('dragover');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('dragover');
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
loadImageFile(e.dataTransfer.files[0]);
}
});
// 2. 转换按钮
convertBtn.addEventListener('click', convertToBlackWhite);
// 3. 下载按钮
downloadBtn.addEventListener('click', downloadBW);
// 4. 滑块实时更新数值 + 自动重转换 (如果已经生成过黑白)
strengthSlider.addEventListener('input', function() {
strengthValue.textContent = `${this.value}%`;
if (imageLoaded && bwGenerated) {
// 如果已经有黑白图,重新转换以应用新的强度
convertToBlackWhite();
} else if (imageLoaded) {
// 有图但还没转黑白:什么都不做,或者显示提示
// 但为了友好,可以自动转换?这里选择如果图片已加载,点击转换才执行;但滑块不自动转(除非已转换过)
// 更合理:如果图片加载了且没有黑白,不动;但如果图片加载了,显示提示可以转换
if (!bwGenerated) {
statusMessage.textContent = '💡 拖动强度后,点击「转换为黑白」生效';
}
}
});
// 额外: 如果图片加载后用户没按转换,但拖动滑块,显示提示
strengthSlider.addEventListener('mouseup', function() {
if (imageLoaded && !bwGenerated) {
statusMessage.textContent = '👆 点击「转换为黑白」应用当前强度';
}
});
// 5. 快捷键 & 初始化占位
showOriginalPlaceholder();
showBWPlaceholder();
resetStates(false);
statusMessage.textContent = '💡 请上传一张图片开始';
// 如果用户点击原图或黑白占位,不做特殊处理
console.log('🖼 图片黑白转换脚本已启动');
})();
</script>
</body>
</html>
核心功能与操作流程
您可以通过上传图片并调节强度,轻松获得理想的黑白效果,其核心转换逻辑基于像素级的灰度算法,并提供了直观的控制选项。
- 图片上传:您可以通过点击上传区域或直接拖拽图片文件(支持 JPG、PNG、WebP 格式)来加载需要处理的彩色图片。
- 黑白转换与强度调节:点击“转换为黑白”按钮即可应用效果,您还可以通过“强度”滑块,在纯黑白与原图色彩之间进行混合调节,获得丰富的灰度过渡效果。
- 结果预览与下载:页面会并排显示原图与转换后的黑白效果,确认效果后,点击“下载黑白图”按钮即可将结果保存为 PNG 格式的图片。