Electron 实战:纯图片尺寸调节工具(协助锁定纵横比)

在这里插入图片描述

在这里插入图片描述

前言

通过前面两篇文章的学习想必大家已经对Electron有所了解了,接下来仅进入实战环节。
先说一下为何做这么一个项目?
不知道大家有没有遇到这种情况,就是有些网页需要指定图片尺寸才可以进行上传,网上也有这样的网页但是网上那些网页不是广告就是付费,真正实现免费的并不多,我在wps上面以及电脑自带的图库上面也都没有找到类似的功能,所以就自己开发了一个,可以直接运行到我的真机上面,也可以打包成应用程序供大家来使用

功能概述

一句话功能:上传图片 → 输入目标尺寸 → 自动/手动缩放 → 保存高清结果


✅ 功能清单

功能说明
图片上传支持 JPG / PNG / WebP 等常见格式
尺寸输入可分别设置宽度和高度(单位:px)
锁定纵横比勾选后修改宽/高自动计算另一值
高质量缩放使用 Canvas 高质量重绘(非简单拉伸)
一键保存导出为 PNG(透明通道保留)

项目结构

image-resize-app/
├── main.js          # 主进程:窗口 + 文件保存
├── preload.js       # 安全桥接 API
├── index.html       # 前端界面 + 缩放逻辑
└── package.json

完整代码

1. main.js(主进程)

const { app, BrowserWindow, ipcMain, dialog } = require('electron');
const path = require('path');
const fs = require('fs');
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 650,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true
}
});
win.loadFile('index.html');
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
});
// 保存图片到本地
ipcMain.handle('save-image', async (_, { dataUrl }) => {
const { canceled, filePath } = await dialog.showSaveDialog({
filters: [
{ name: 'PNG', extensions: ['png'] },
{ name: 'JPEG', extensions: ['jpg', 'jpeg'] }
]
});
if (!canceled && filePath) {
const buffer = Buffer.from(dataUrl.split(',')[1], 'base64');
fs.writeFileSync(filePath, buffer);
return filePath;
}
});

2. preload.js(安全暴露 API)

const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('api', {
saveImage: (data) => ipcRenderer.invoke('save-image', data)
});

3. index.html(核心功能)

<!DOCTYPE html>
  <html>
    <head>
        <meta charset="UTF-8">
      <title>图片尺寸调节工具</title>
        <style>
          body {
          font-family: -apple-system, BlinkMacSystemFont, sans-serif;
          margin: 0;
          padding: 20px;
          background: #f9fafb;
          }
          .container {
          max-width: 750px;
          margin: 0 auto;
          background: white;
          border-radius: 12px;
          box-shadow: 0 4px 12px rgba(0,0,0,0.08);
          padding: 25px;
          }
          h1 {
          text-align: center;
          color: #2d3748;
          margin-bottom: 25px;
          }
          .controls {
          display: flex;
          flex-wrap: wrap;
          gap: 15px;
          align-items: center;
          margin-bottom: 20px;
          }
          .input-group {
          display: flex;
          align-items: center;
          gap: 8px;
          }
          input[type="number"] {
          width: 90px;
          padding: 6px 8px;
          border: 1px solid #cbd5e0;
          border-radius: 6px;
          font-size: 14px;
          }
          label {
          display: flex;
          align-items: center;
          gap: 6px;
          font-size: 14px;
          color: #4a5568;
          }
          button {
          padding: 8px 18px;
          background: #3182ce;
          color: white;
          border: none;
          border-radius: 6px;
          cursor: pointer;
          font-size: 14px;
          }
          button:hover { background: #2c5aa0; }
          button:disabled {
          background: #cbd5e0;
          cursor: not-allowed;
          }
          canvas {
          width: 100%;
          height: auto;
          border: 1px solid #e2e8f0;
          border-radius: 8px;
          background: #f8fafc;
          margin-top: 10px;
          }
          .info {
          text-align: center;
          color: #718096;
          font-size: 13px;
          margin-top: 12px;
          }
        </style>
      </head>
      <body>
          <div class="container">
        <h1> 图片尺寸调节工具</h1>
            <div class="controls">
              <input type="file" id="imageInput" accept="image/*">
                <div class="input-group">
              <label>宽:</label>
                  <input type="number" id="widthInput" min="1" value="800">
                <span>×</span>
                    <input type="number" id="heightInput" min="1" value="600">
                  <label><input type="checkbox" id="aspectLock" checked> 锁定比例</label>
                  </div>
                <button id="saveBtn" disabled> 保存图片</button>
                </div>
              <canvas id="outputCanvas"></canvas>
              <p class="info">上传图片后,可自由调整尺寸。勾选“锁定比例”避免图像变形。</p>
              </div>
              <script>
                const imageInput = document.getElementById('imageInput');
                const widthInput = document.getElementById('widthInput');
                const heightInput = document.getElementById('heightInput');
                const aspectLock = document.getElementById('aspectLock');
                const saveBtn = document.getElementById('saveBtn');
                const canvas = document.getElementById('outputCanvas');
                const ctx = canvas.getContext('2d');
                let originalImage = null;
                let originalRatio = 1;
                // 修改宽度时联动高度(如果锁定比例)
                widthInput.addEventListener('input', () => {
                if (aspectLock.checked && originalImage) {
                const w = parseInt(widthInput.value) || 1;
                heightInput.value = Math.round(w / originalRatio);
                }
                render();
                });
                // 修改高度时联动宽度(如果锁定比例)
                heightInput.addEventListener('change', () => {
                if (aspectLock.checked && originalImage) {
                const h = parseInt(heightInput.value) || 1;
                widthInput.value = Math.round(h * originalRatio);
                }
                render();
                });
                // 切换锁定比例状态
                aspectLock.addEventListener('change', render);
                // 上传图片
                imageInput.addEventListener('change', (e) => {
                const file = e.target.files[0];
                if (!file) return;
                const img = new Image();
                img.onload = () => {
                originalImage = img;
                originalRatio = img.width / img.height;
                // 默认设为目标尺寸(不超过原图)
                const maxWidth = Math.min(img.width, 1920);
                const maxHeight = Math.min(img.height, 1080);
                widthInput.value = maxWidth;
                heightInput.value = Math.round(maxWidth / originalRatio);
                render();
                saveBtn.disabled = false;
                };
                img.src = URL.createObjectURL(file);
                });
                // 渲染缩放后的图片
                function render() {
                if (!originalImage) return;
                const w = parseInt(widthInput.value) || 1;
                const h = parseInt(heightInput.value) || 1;
                canvas.width = w;
                canvas.height = h;
                // 关键:使用高质量缩放(关闭 imageSmoothing 可实现像素风,但这里要清晰)
                ctx.imageSmoothingEnabled = true;
                ctx.imageSmoothingQuality = 'high';
                ctx.clearRect(0, 0, w, h);
                ctx.drawImage(originalImage, 0, 0, w, h);
                }
                // 保存图片
                saveBtn.addEventListener('click', async () => {
                const mimeType = 'image/png'; // 也可根据扩展名动态切换
                const dataUrl = canvas.toDataURL(mimeType);
                const path = await window.api.saveImage({ dataUrl });
                if (path) alert(`✅ 已保存至:\n${path}`);
                });
              </script>
            </body>
          </html>

4. package.json

{
"name": "image-resize-app",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^latest"
}
}

▶️ 如何运行?

npm install --save-dev electron
npm start

建议使用 cnpm 或设置淘宝镜像加速安装。

npm config set registry https://registry.npmmirror.com
npm install -g cnpm --registry=https://registry.npmmirror.com
cnpm install --save-dev electron
npm start

技术亮点

  • 高质量缩放:通过 ctx.imageSmoothingQuality = 'high' 保证缩放清晰
  • 安全架构:使用 contextBridge 隔离主/渲染进程
  • 用户体验:默认尺寸不超过 1920×1080,避免内存溢出
  • 格式兼容:支持透明 PNG、JPG 等主流格式

鸿蒙部分

如何让原本用于 Electron 的前端 + 主进程架构,在鸿蒙系统中运行并兼容?

我们的最终目的是打包在真机上面,所以要去将代码迁移到dev上面,通过观察不难看出,结构都是类似的,所以理论上来说直接copy上去即可,理论成立就开始实战

在这里插入图片描述

✅ 答案总结
鸿蒙的“Electron 兼容”本质是:Web Engine 模拟 Chromium + Node.js 环境
华为在 HarmonyOS 的 Web Engine(Web 引擎) 中,通过以下方式实现了对 Electron 应用的部分兼容:

项目 说明
Web 页面渲染 使用 Chromium 内核,支持 HTML/CSS/JS,与 Electron 一致
Node.js API 模拟 通过 libadapter 层模拟部分 Node.js 接口(如文件、IPC)
主进程 → 渲染进程通信 使用 contextBridge + ipcRenderer 模式,与 Electron 保持一致
资源打包机制 将 main.js, preload.js, index.html 打包进 HAP(HarmonyOS Ability Package)

测试

图片之前的尺寸
在这里插入图片描述

开始进行修改

在这里插入图片描述

修改后的尺寸

在这里插入图片描述

✅ 总结

我们通过一个简单的 图片尺寸调节工具,展示了如何:

✅ 用 Electron 快速构建桌面应用
✅ 利用 Canvas 实现高质量图像缩放
✅ 通过鸿蒙 DevEco 的 Web Engine + libadapter 机制,无缝迁移到 HarmonyOS
✅ 实现“一套代码,双端运行”(PC + 鸿蒙设备)

posted @ 2025-12-20 08:03  yangykaifa  阅读(6)  评论(0)    收藏  举报