项目背景与开发动机
在整理项目文档时,我需要直观展示应用程序的亮色与暗色主题对比效果。最初打算使用 Photoshop 手动制作,但每次都要重复操作非常繁琐。尝试了几款在线图片对比工具,发现要么需要付费,要么强制上传图片到服务器,隐私无法保障。
于是决定自己动手开发一个本地运行的主题对比图生成工具。
最终效果与核心功能
在线体验:tageecc.github.io/theme-merge…
核心功能亮点:
- 支持上传两张图片(亮色主题与暗色主题)
- 沿对角线智能分割并合成一张对比图
- 分割角度可自由调节,范围 -45° 到 45°
- 支持从系统剪贴板直接粘贴图片
- 所有处理均在本地完成,图片不会上传至服务器,保障隐私安全
技术实现
技术选型
为实现快速开发与便捷部署,选择了最简单直接的技术方案:
- 采用 HTML5 Canvas 进行图片处理与渲染
- 使用纯 JavaScript 编写逻辑,不依赖任何第三方库或框架
- 借助 GitHub Pages 免费托管网站,用户直接访问即可使用
核心目标是:用户打开浏览器即可一键使用,无需安装任何软件。
核心算法
对角线分割的核心实现依赖于 Canvas 的 clip() 方法,通过对裁剪路径的精确控制实现分割效果:
function drawMergedImage() {// 先画暗色主题作为底图ctx.drawImage(darkImg, 0, 0);// 保存状态ctx.sa ve();// 创建对角线裁剪路径ctx.beginPath();ctx.moveTo(0, 0);ctx.lineTo(canvas.width, 0);ctx.lineTo(x1, y1);// 计算旋转后的端点ctx.lineTo(x2, y2);ctx.lineTo(0, canvas.height);ctx.closePath();// 应用裁剪并画亮色主题ctx.clip();ctx.drawImage(lightImg, 0, 0);ctx.restore();}
角度计算部分需要一点基础三角函数知识,以下是具体的实现代码:
const angle = parseFloat(angleSlider.value);const angleRad = (angle * Math.PI) / 180;const diagonal = Math.sqrt(canvas.width ** 2 + canvas.height ** 2);const baseAngle = Math.atan2(canvas.height, canvas.width);// 计算旋转后的对角线端点const x1 = canvas.width + diagonal * Math.cos(baseAngle + angleRad);const y1 = -diagonal * Math.sin(baseAngle + angleRad);const x2 = -diagonal * Math.cos(baseAngle + angleRad);const y2 = canvas.height + diagonal * Math.sin(baseAngle + angleRad);
交互体验优化
初始版本仅支持文件上传,后续根据用户需求增加了多项实用改进:
1. 点击左右两侧区域可分别上传对应的主题图片
通过判断点击点位于对角线的哪一侧来决定上传位置,实现逻辑如下:
function isPointOnLightSide(x, y) {const centerX = canvas.width / 2;const centerY = canvas.height / 2;const dx = x - centerX;const dy = y - centerY;// 旋转坐标系判断位置const rotatedX = dx * Math.cos(-angleRad) - dy * Math.sin(-angleRad);const rotatedY = dx * Math.sin(-angleRad) + dy * Math.cos(-angleRad);return rotatedY < rotatedX * baseSlope;}
2. 增加系统剪贴板粘贴支持
这一功能大幅提升了使用效率。现在截图后只需按 Ctrl+V 即可快速粘贴图片:
document.addEventListener('paste', (e) => {const items = e.clipboardData?.items;for (let i = 0; i < items.length; i++) {if (items[i].type.startsWith('image/')) {const file = items[i].getAsFile();loadImageToSide(file, determineSide());break;}}});
开发过程中遇到的问题
问题一:Canvas 尺寸设置不当
最初使用 CSS 控制 canvas 显示大小,导致导出的图片模糊不清。后来才意识到:
- CSS 的宽高只决定元素在页面上的显示尺寸
- Canvas 的 width 和 height 属性才决定实际绘制的分辨率
问题二:大图处理导致卡顿
加载 4K 分辨率图片时,若逐像素计算裁剪会非常缓慢。最终采用 Canvas 内置的 clip() 方法,将裁剪工作交给浏览器原生处理,性能显著提升。
问题三:移动端适配问题
在手机上访问时,Canvas 可能会超出屏幕边界。通过添加以下 CSS 样式进行约束:
canvas {max-width: 90vw;max-height: 70vh;width: auto;height: auto;}
这样在显示时自动缩放,而下载时仍保留原始高清分辨率。
典型应用场景
这款对角线主题对比图生成工具尤其适合以下场景:
- 技术文档编写 – 直观展示功能在不同主题模式下的视觉效果
- 产品介绍页面 – 呈现 UI 设计稿的亮色与暗色对比
- 技术博客配图 – 快速制作文章中的主题对比图
- 开源项目 README – 形象展示项目的亮暗主题切换效果
未来可优化方向
当前版本已满足基本需求,但仍有一些值得改进的功能:
- 垂直分割模式 – 除对角线外,增加上下或左右分割方式
- 更多导出格式 – 目前仅支持 PNG,后续可加入 JPG、WebP 等
- 批量处理能力 – 一次上传多组图片,批量生成对比图
- 预设模板 – 允许用户保存常用角度与分割参数,快速复用
总结
整个项目从构思到完成仅用约一小时,代码量不足 500 行。Canvas 的 clip() 方法非常高效,配合简单的三角学知识即可实现理想的对比图分割效果。
如果你也需要快速生成亮暗主题对比图,不妨试试这个工具:
在线使用:tageecc.github.io/theme-merge…
开源代码:github.com/tageecc/the…
代码结构清晰简单,如有任何问题欢迎提交 Issue 或直接贡献代码。
