零基础-5小时开发一个electron应用-[实践]

一、背景

三、技能升级

​ 明明可以用颜值取胜,非要靠才华?不对,明明可以用代码搞定,非要搞设计?步入正题,正好最近对electron比较感兴趣,又是要做工具,那就直接怼

1.electron介绍

​ electron最开始不叫这个名字,叫“Atom Shell”,顾名思义,它给我们的应用整合提供了一个可运行的平台,electron通过将ChromiumNode.js合并到同一个运行时环境中,并将其打包为Mac,Windows和Linux系统下的应用来实现这一目的,它负责比较难搞的部分,你只需把精力放在你的应用的核心上即可。现在比较流行的Atom编辑器也是基于electron开发。

官网介绍传送门

2.项目结构

.
├── dist                //应用程序打包输出
├── app
      ├── assets        //资源
      ├── config        //配置
      ├── pages         //主入口
      └── dist          //bundle文件输出
├── .babelrc            
├── index.html
├── app.js            
├── webpack.config.dev.js // dev环境开发
├── webpack.config.js   // prod环境打包
├── package.json
  • app.js为应用启动脚本

    const {app, BrowserWindow} = require('electron')
    const path = require('path')
    const url = require('url')
    // 保持一个对于 window 对象的全局引用,如果你不这样做,
    // 当 JavaScript 对象被垃圾回收, window 会被自动地关闭
    let win
    function createWindow () {
      // 创建浏览器窗口。
      win = new BrowserWindow({width: 800, height: 600})
      // 然后加载应用的 index.html。
      win.loadURL(url.format({
        pathname: path.join(__dirname, 'index.html'),
        protocol: 'file:',
        slashes: true
      }))
      // 打开开发者工具。
      win.webContents.openDevTools()
      // 当 window 被关闭,这个事件会被触发。
      win.on('closed', () => {
        // 取消引用 window 对象,如果你的应用支持多窗口的话,
        // 通常会把多个 window 对象存放在一个数组里面,
        // 与此同时,你应该删除相应的元素。
        win = null
      })
    }
    // Electron 会在初始化后并准备
    // 创建浏览器窗口时,调用这个函数。
    // 部分 API 在 ready 事件触发后才能使用。
    app.on('ready', createWindow)
    // 当全部窗口关闭时退出。
    app.on('window-all-closed', () => {
      // 在 macOS 上,除非用户用 Cmd + Q 确定地退出,
      // 否则绝大部分应用及其菜单栏会保持激活。
      if (process.platform !== 'darwin') {
        app.quit()
      }
    })
    app.on('activate', () => {
      // 在macOS上,当单击dock图标并且没有其他窗口打开时,
      // 通常在应用程序中重新创建一个窗口。
      if (win === null) {
        createWindow()
      }
    })

3.不同系统的启动方式

系统启动方式
macOS / Linux$ ./node_modules/.bin/electron .
Windows$ .\node_modules.bin\electron .

4.开发过程

技术选型为electron + react + webpack,总体思路是利用Canvas做图片的合并:

  • 第一步,模板设计,模板的宽高(画布宽高),获取截图相对于模型图片的x,y偏移,以及截图的大小

  • 第二步,将第一步获取的数据抽象成配置文件config/flex.js

  • 第三步,初始化画布

    /**
     * [initCanvas 初始画布]
     */
    initCanvas = () => {
      const canvas = document.createElement('canvas')
      canvas.width = this.state.canvasWidth
      canvas.height = this.state.canvasHeight
      const context = canvas.getContext('2d')
      this.setState({
        context,
        canvas,
      })
    }
  • 第四部,首先将机型模板图片利用canvas绘制

    /**
     * [draw description]
     * @param  {[type]}   url      [图片url]
     * @param  {[type]}   x        [相对x偏移]
     * @param  {[type]}   y        [相对y偏移]
     * @param  {Function} callback [合成后回调]
     */
    draw = (url, x, y, callback) => {
      const image = new Image();
      image.onload = () => {
        if (!callback) {
          // 模板
          this.state.context.drawImage(image, x, y)
        } else {
          // 截图
          this.state.context.drawImage(image, x, y, this.state.width, this.state.height)
          callback(this.state.canvas.toDataURL('image/png'))      }
      }
      image.src = url;
    }
    componentDidMount() {
      this.initCanvas()
      // 绘制模型机图片
      this.draw(this.state.theme, 0, 0)
      document.getElementById('upload').addEventListener('change', this.uploadImage)
    }

第五步,截图上传事件处理

/**
 * [uploadImage 上传图片回调]
 * @param  {[type]} event [description]
 */
uploadImage = (event) => {
  const reader = new FileReader()
  const file = event.target.files[0] || event.dataTransfer.files[0]
  reader.onload = (e) => {
    const base64 = e.target.result;
    if (file.size > 1024 * 1024 * 10) {
      toast.warn("图片大小不能超过10M", {
        position: toast.POSITION.BOTTOM_RIGHT,
      })
      return
    }
    this.combine(base64,  (url) => {
      this.setState({
        theme: url,
        combineSuccess: true,
      })
    })
  }
  reader.readAsDataURL(file)
}
componentDidMount() {
  this.initCanvas()
  this.draw(this.state.theme, 0, 0)
  document.getElementById('upload').addEventListener('change', this.uploadImage)
}
componentWillUnmount() {
  document.getElementById('upload').removeEventListener('change', this.uploadImage)
}

5.调试

  • 一期暂时没有实现代码的热加载
  • 首先启动npm run watch(webpack –watch)命令监听文件变动,然后npm run mac(或npm run win)启动electron
  • 文件每次变更需要手动刷新窗口 【注】如果要实现热加载可以参考segmentfault.com/a/119000000…

6.打包构建

(1)应用icon www.easyicon.net/covert/可以用在线转换工具

  • mac 为512 x 512

  • win 为256 x 256 (2)代码打包

    npm run build

(3)应用程序打包

// 打包exe文件
npm run build:win
// 打包dmg文件
npm run build:mac

四、下载图片遇到的问题

事情总没有那么完美,当截图大于1M会出现无法下载图片的情况,图片base64编码过大,a标签的href属性限制了字符串的长度,经过一番调研,base64改为文件流下载:

/**
 * [baseb4ToBlob base64转blob]
 * @param  {[type]} dataURL [base64编码]
 */
base64ToBlob(dataURL) {
  const parts = dataURL.split(';base64,')
  const contentType = parts[0].split(':')[1]
  const raw = window.atob(parts[1])
  const uInt8Array = new Uint8Array(raw.length)
  for (let i = 0; i < raw.length; ++i) {
    uInt8Array[i] = raw.charCodeAt(i)
  }
  const blob = new Blob([uInt8Array], {type: contentType})
  return window.URL.createObjectURL(blob)
}

五、结果

根据STAR法则,恒量下得到了什么结果:

方案耗时用户
PS大法5 min会PS的小哥哥小姐姐
iPhoneView10 sAnyone
  • What?效率提升300%~哈哈哈哈哈
  • 其他收获,electron开发入门,新技能get

六、代码仓库

github.com/geneking/ip…如果觉得不错,请顺手star :)

原文链接:www.keycode.me

上一篇:indexDB 不完全指南
下一篇:初识时下热门框架 - React

相关推荐

  • 高性能javascript--编程实践

    setTimeout()和settimeInterval()传递函数而不是字符串作为参数 引申: 用setTimeout()方法来模拟setInterval()与setInterval()之间的...

    7 个月前
  • 高德前端这五年:动态化技术的研发历程和全面落地实践

    前言 2015年2020年,历经5年发展,高德地图应用开发前端团队在业务快速发展中不断成长。一路走来,从小团队主要负责短期运营活动开发的散兵游勇,到现在团队规模100人,覆盖高德5大业务线,上百个模...

    2 个月前
  • 高德APP启动耗时剖析与优化实践(iOS篇)

    前言最近高德地图APP完成了一次启动优化专项,超预期将双端启动的耗时都降低了65%以上,iOS在iPhone7上速度达到了400毫秒以内。就像产品们用后说的,快到不习惯。

    2 个月前
  • 马蜂窝容器化平台前端赋能实践

    容器对前端开发真的有用吗?答案是肯定的。 最初当我向公司的前端同学「安利」容器技术的时候,很多人都会说:「容器?这不是用在后端的技术吗?我不懂啊,而且前端开发用不上吧。

    1 年前
  • 首页白屏优化实践

    前言 自从前端三大框架React、Vue、Angular面世以来,前端开发逐渐趋向规范化、统一化,大多数时候新建前端项目,首先想到使用的技术一定是三大框架之一,框架给前端开发带来了极大的便利和规范...

    10 个月前
  • 首发 | 闲鱼公开多年 Flutter 实践经验

    (https://img.javascriptcn.com/7f312df423086aca9f69ec144384ad5f) 阿里妹导读:Flutter从诞生到现在,已经成为了跨端开发的领跑者。

    9 个月前
  • 飞猪找一找,端侧 AI 的实践和展望

    本文介绍飞猪互动双十一彩蛋“飞猪找一找”, 一个基于 AI 图像识别的纯客户端游戏 (/public/upload/d1c681661c07508083ea69abbe26841c) 以这个游...

    2 个月前
  • 项目中资源缓存机制实践(静态资源和本地数据缓存)

    网络资源的缓存 核心方案 1. HTML文件 nocache 2. js,css文件 时间戳/哈希/版本号,长缓存 3. 图片资源 长缓存,更新时使用新链接地址 1. 前后端...

    9 个月前
  • 音频组件开发实践

    我的blog原文链接(https://github.com/phoebeCodeSpace/blog/blob/master/audio/vueaudio.md) 最近公司迭代的项目中,新增了音频...

    2 年前
  • 面向对象的JavaScript最佳实践?[关闭]

    gnoviceesatis(https://stackoverflow.com/users/52738/gnovice)提出了一个问题:Object Oriented Javascript best ...

    2 年前

官方社区

扫码加入 JavaScript 社区