• WPF 应用迁移到 Electron 框架过程记录


    前一段时间我用 WPF 开发了一个查看 emoji 表情的小工具 https://github.com/he55/EmojiViewer ,由于最近我使用 macOS 系统比较多,我想能在 macOS 系统上也能使用这个工具。于是我尝试将 WPF 应用迁移到 Electron 框架,感觉这个框架很强大,在这里记录一下应用迁移的过程。

    安装 Electron 环境

    • 安装 nodejs。到官网 https://nodejs.org/en 下载最新的 nodejs,然后安装
    • 打开命令行输入 git clone https://github.com/electron/electron-quick-start.git 命令克隆 Electron 模板项目,使用模板可以快速搭建应用。
    • 然后使用 cd electron-quick-start 目录进入到目录,接着运行 npm install 命令还原项目。
    • 使用 vscode 打开文件夹,项目文件如下

    编写代码

    • Electron 分为主进程和渲染进程,对文件、系统和窗口的操作需要在主线程,界面渲染在渲染进程。创建窗口属于主进程的工作,需要到 main.js 文件编写代码。创建窗口使用 BrowserWindow 对象,widthheight 分别设置窗口宽度和高度,autoHideMenuBar 设置是否隐藏菜单,最后使用 loadFile 加载页面文件并显示窗口。
    function createWindow() {
      const mainWindow = new BrowserWindow({
        width: 915,
        height: 560,
        autoHideMenuBar: true,
        webPreferences: {
          preload: path.join(__dirname, 'preload.js')
        }
      })
    
      mainWindow.loadFile('index.html')
    }
    
    • 监听 whenReady 事件,等待应用初始化完成后显示窗口
    app.whenReady().then(() => {
      createWindow()
    
      app.on('activate', function () {
        if (BrowserWindow.getAllWindows().length === 0) createWindow()
      })
    })
    
    • 修改 index.html 文件,界面部分使用了 vue 进行渲染
    html>
    <html>
    
    <head>
      <meta charset="UTF-8">
      <script src="vue.global.js">script>
      <link href="./styles.css" rel="stylesheet">
      <title>EmojiViewertitle>
    head>
    
    <body>
      <div id="app" class="container">
        <ul class="left">
          <li v-for="(item, key) in categories" :class="{active: item.isActive}" @click="catetoryItemClick(item)">{{ key }}li>
        ul>
        <ul class="main" ref="mainElement">
          <li v-for="emoji in emojis" :class="{active: emoji.isActive}" @click="emojiItemClick(emoji)">
            <img :src="emoji.previewImage" alt="">
            <p>{{emoji.name}}p>
          li>
        ul>
        <div class="right">
          <img :src="selectedEmoji.previewImage">
          <p>{{ selectedEmoji.name }}p>
          <button @click="copyEmoji(selectedEmoji)" type="button">Copy Emojibutton>
          <button @click="copyImage(selectedEmoji)" type="button">Copy Imagebutton>
          <button @click="openFile(selectedEmoji)" type="button">Open Filebutton>
        div>
      div>
    
      <script src="./renderer.js">script>
    body>
    
    html>
    
    • renderer.js 文件中编写页面处理代码
    window.addEventListener('DOMContentLoaded', async () => {
        const { createApp, ref, onMounted } = Vue
        let emojiData = await ipc.getData()
    
        createApp({
            setup() {
                const mainElement = ref(null)
    
                const categories = ref(emojiData)
                const emojis = ref([])
                const selectedEmoji = ref({})
    
                function copyEmoji(emoji) {
                    ipc.ipc('writeText', emoji.metadata.glyph)
                }
                function copyImage(emoji) {
                    ipc.ipc('writeImage', emoji.previewImage)
                }
                function openFile(emoji) {
                    ipc.ipc('showItemInFolder', emoji.previewImage)
                }
    
                let lastSelectedEmojis
                function catetoryItemClick(items) {
                    if (lastSelectedEmojis) {
                        lastSelectedEmojis.isActive = false
                    }
    
                    items.isActive = true
                    lastSelectedEmojis = items
    
                    // const main = document.querySelector('.main')
                    mainElement.value.scrollTop = 0
                    emojis.value = items
                }
    
                function emojiItemClick(emoji) {
                    if (selectedEmoji.value) {
                        selectedEmoji.value.isActive = false
                    }
    
                    emoji.isActive = true
                    selectedEmoji.value = emoji
                }
    
                onMounted(() => {
                    catetoryItemClick(emojiData['Activities'])
                    emojiItemClick(emojiData['Activities'][0])
                })
    
                return {
                    mainElement,
                    categories,
                    emojis,
                    selectedEmoji,
                    catetoryItemClick,
                    emojiItemClick,
                    copyEmoji,
                    copyImage,
                    openFile,
                }
            }
        }).mount('#app')
    })
    
    • 读取文件,node 提供了文件操作相关的 api 可以很方便的操作文件系统。
    function loadData(assetPath) {
      const dirs = fs.readdirSync(assetPath)
      const data = []
      const groupData = {}
      for (const dir of dirs) {
        const fullPath = path.resolve(assetPath, dir)
        const metadata = require(path.resolve(fullPath, 'metadata.json'))
        let previewImage
    
        let imagePaths = [path.resolve(fullPath, '3D'), path.resolve(fullPath, 'Default', '3D')]
        for (const imagePath of imagePaths) {
          if (fs.existsSync(imagePath)) {
            let files = fs.readdirSync(imagePath)
            if (files.length === 0)
              return
            previewImage = path.resolve(imagePath, files[0])
          }
        }
    
        const { unicode, group } = metadata
        const obj = {
          metadata,
          id: unicode,
          name: dir,
          previewImage,
        }
        data.push(obj)
    
        if (!groupData[group])
          groupData[group] = []
        groupData[group].push(obj)
      }
      return groupData
    }
    

    完整代码(WPF 版本) https://github.com/he55/EmojiViewer
    完整代码(vue 版本) https://github.com/he55/web-learn/tree/main/9.electron-emoji-viewer(vue)
    完整代码(js 原生版本) https://github.com/he55/web-learn/tree/main/6.electron-emoji-viewer

  • 相关阅读:
    Node简介
    每日一题——在windows x86/64 VS环境下,下面的程序会出现什么问题?运行结果是什么?为什么?
    1759E(方案枚举)
    Python 海龟绘图基础教学教案(十七)
    Linux内核——门相关入门知识
    美国CN2服务器速度怎么样
    semaphere部署,配置ldap
    人工智能图像修复研究,人工智能图像信息处理
    【笔试刷题训练】day_12
    Linux系统安装Ruby语言
  • 原文地址:https://www.cnblogs.com/he55/p/18046511