做文档配图太烦?一个小时写了个工具解决

纯前端实现主题对比图生成器,不用再手动拼 PS 了

起因

最近在整理项目文档,想展示应用的亮色和暗色主题对比。本来想用 Photoshop 做,但想想每次都要重复操作太烦了。在线工具试了几个,要么收费要么要上传图片。

干脆自己写一个吧。

最终效果

在线体验 :tageecc.github.io/theme-merge...

核心功能:

  • 上传两张图(亮色/暗色)
  • 对角线分割合并
  • 角度可调(-45° 到 45°)
  • 支持剪贴板粘贴
  • 本地处理,不上传服务器

技术实现

技术选型

考虑到要快速实现,选了最简单的方案:

  • HTML5 Canvas 处理图片
  • 纯 JavaScript,不依赖任何库
  • GitHub Pages 托管

目标是打开浏览器就能用。

核心算法

对角线分割的关键是 Canvas 的 clip() 方法:

javascript 复制代码
function drawMergedImage() {
  // 先画暗色主题作为底图
  ctx.drawImage(darkImg, 0, 0);
  
  // 保存状态
  ctx.save();
  
  // 创建对角线裁剪路径
  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();
}

角度计算这块稍微有点绕,用了基础的三角函数:

javascript 复制代码
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. 点击左右区域分别上传

判断点击位置在对角线哪一边:

javascript 复制代码
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 就行:

javascript 复制代码
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;
    }
  }
});

遇到的坑

坑1:Canvas 尺寸问题

一开始用 CSS 设置 canvas 大小,结果图片糊了。后来才知道:

  • CSS 控制显示大小
  • width/height 属性控制实际分辨率

坑2:大图卡顿

上传 4K 图片时,如果自己算每个像素会很慢。解决方案是直接用 Canvas 的 clip(),让浏览器处理裁剪,比自己算快多了。

坑3:移动端适配

手机上 canvas 会超出屏幕。用了 max-width 和 max-height 的 CSS:

css 复制代码
canvas {
  max-width: 90vw;
  max-height: 70vh;
  width: auto;
  height: auto;
}

显示时缩放,下载时保持原分辨率。

使用场景

这个工具特别适合:

  • 技术文档 - 展示功能的不同主题
  • 产品介绍 - 展示 UI 设计稿
  • 博客配图 - 写技术文章时的对比图
  • 开源项目 README - 展示项目效果

改进空间

现在够用了,但还有些可以优化的:

  1. 垂直分割模式 - 除了对角线,加个上下分割
  2. 更多导出格式 - 现在只支持 PNG,可以加 JPG、WebP
  3. 批量处理 - 一次处理多组图片
  4. 预设模板 - 保存常用的角度设置

总结

整个项目花了大概一个小时,代码不到 500 行。Canvas 的 clip() 确实挺好用的,配合基础的三角函数就能实现不错的效果。

如果你也需要做主题对比图,可以试试:

在线使用 :tageecc.github.io/theme-merge...
开源代码 :github.com/tageecc/the...

代码写得比较简单,有问题欢迎提 issue 或者直接改。


如果觉得有用,欢迎点赞收藏!也可以给 GitHub 项目一个 ⭐

相关推荐
光影少年10 小时前
RN与Flutter架构区别
前端·flutter·react native·react.js·架构·node.js
peter676810 小时前
css学习小结
前端·css·学习
liangshanbo121510 小时前
什么是CSS原子化?它的优劣势是什么?
前端·css·tensorflow·css原子化
.道阻且长.10 小时前
C++ 11:可变参数模板
前端·c++·算法
IMPYLH10 小时前
HTML 的 <th> 元素
前端·html
风骏时光牛马10 小时前
AI浪潮下程序员的职场破局与能力成长思考
前端
IMPYLH10 小时前
HTML 的 <thead> 元素
前端·html
FungLeo19 小时前
成为全栈·React 管理后台篇·总复盘:从“接口能调通”到“后台值得使用”
前端·react.js·前端框架·项目复盘·成为全栈
Csvn20 小时前
并发模式:让渲染学会排队、插队和让路
前端
可乐鸡翅yeah_20 小时前
新手梳理:M3U8 线上问题,哪些是前端锅,哪些是后端锅
前端·ios·音视频·实时音视频·m3u8·音视频在线播放