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

0 阅读3分钟

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

起因

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

干脆自己写一个吧。

最终效果

demo

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

核心功能:

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

技术实现

技术选型

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

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

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

核心算法

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

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();
}

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

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;
    }
  }
});

遇到的坑

坑1:Canvas 尺寸问题

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

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

坑2:大图卡顿

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

坑3:移动端适配

手机上 canvas 会超出屏幕。用了 max-widthmax-height 的 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 项目一个 ⭐