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

纯图片尺寸调节工具(支持锁定纵横比)

    • 前言
    • 功能概述
    • [✅ 功能清单](#✅ 功能清单)
    • [📂 项目结构](#📂 项目结构)
    • [🧩 完整代码](#🧩 完整代码)
      • [1. `main.js`(主进程)](#1. main.js(主进程))
      • [2. `preload.js`(安全暴露 API)](#2. preload.js(安全暴露 API))
      • [3. `index.html`(核心功能)](#3. index.html(核心功能))
      • [4. `package.json`](#4. package.json)
    • [▶️ 如何运行?](#▶️ 如何运行?)
    • [🔍 技术亮点](#🔍 技术亮点)
    • [🚀 鸿蒙部分](#🚀 鸿蒙部分)
    • 测试
    • [✅ 总结](#✅ 总结)

前言

通过前面两篇文章的学习想必大家已经对Electron有所了解了,接下来仅进入实战环节。

先说一下为何做这么一个项目?

不知道大家有没有遇到这种情况,就是有些网页需要指定图片尺寸才可以进行上传,网上也有这样的网页但是网上那些网页不是广告就是付费,真正实现免费的并不多,我在wps上面以及电脑自带的图库上面也都没有找到类似的功能,所以就自己开发了一个,可以直接运行到我的真机上面,也可以打包成应用程序供大家来使用

功能概述

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


✅ 功能清单

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

📂 项目结构

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

🧩 完整代码

1. main.js(主进程)

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

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron');

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

3. index.html(核心功能)

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

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

▶️ 如何运行?

bash 复制代码
npm install --save-dev electron
npm start

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

bash 复制代码
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 + 鸿蒙设备)

相关推荐
半瓶神仙醋1 小时前
uniapp 项目接入 sentry监控
前端
谁黑皮谁肘击谁在连累直升机1 小时前
包及其导入
前端·后端
0***141 小时前
JavaScript视频处理案例
开发语言·javascript·音视频
在下历飞雨1 小时前
Kuikly 基础之封装自定义音频播放模块
前端
vim怎么退出1 小时前
React 项目诡异白屏事故复盘:JSON.stringify、循环引用、setState 死循环,一个都没跑
前端·debug
Danny_FD1 小时前
使用Highcharts创建3D环形图
前端·echarts
我的div丢了肿么办1 小时前
js中async和await 的详细讲解
前端·javascript·vue.js
程序员小寒2 小时前
前端性能优化之CSS篇
前端·css·性能优化
种时光的人2 小时前
关于人人开源框架renren-fast-vue前端npm install安装报错的问题解决方法
前端·vue.js·npm