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

纯前端实现主题对比图生成器,不用再手动拼 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-widthmax-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 项目一个 ⭐

相关推荐
ZC跨境爬虫24 分钟前
跟着MDN学HTML_day_48:(Node接口)
前端·javascript·ui·html·音视频
PieroPc2 小时前
CAMWATCH — 局域网摄像头监控系统 Fastapi + html
前端·python·html·fastapi·监控
巴巴博一3 小时前
2026 最新:Trae / Cursor 一键接入 taste-skill 完整教程(让 AI 前端告别“AI 味”)
前端·ai·ai编程
kyriewen3 小时前
半夜三点线上崩了,AI替我背了锅——用AI排错,五分钟定位三年老bug
前端·javascript·ai编程
kyriewen3 小时前
我让 AI 当了 24 小时全年无休的“毒舌考官”
前端·ci/cd·ai编程
hexu_blog4 小时前
vue+java实现图片批量压缩
java·前端·vue.js
IT_陈寒4 小时前
为什么你应该学习JavaScript?
前端·人工智能·后端
lifejump4 小时前
Empire(帝国)CMS 7.5 XSS注入
前端·安全·xss
无风听海4 小时前
OAuth 2.0 前端通道与后端通道深入剖析
前端·oauth
sakiko_4 小时前
UIKit学习笔记8-发送照片、拍摄照片并发送
前端·swift·uikit