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

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

相关推荐
mldong11 分钟前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排1 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60611 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角2 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!3 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰3 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi4 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen5 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马6 小时前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒6 小时前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端