Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Use NPM
- ElectronはNodeとほとんど差がないので、NPMで入れたパッケージも使える。
- 今回は簡単な画像編集ができるアプリを作ってみる。
- ## 1. 設置
- ```sh
- npm init
- ```
- まず、Moduleを入れる前に`package.json`からつくる。
- 特に設定はいらないので、全部基本設定で十分。
- ```sh
- npm i -S jquery jimp tinycolor2
- ```
- `jimp`と`tinycolor2`は純粋にJavascriptだけで作られたImage編集moduleである。
- 次にアプリの起動部も入れておく
- ```js
- const electron = require('electron')
- const app = electron.app
- const BrowserWindow = electron.BrowserWindow
- var mainWindow = null
- app.on('window-all-closed', function () {
- app.quit()
- })
- app.on('ready', function () {
- mainWindow = new BrowserWindow({width: 800, height: 600})
- mainWindow.loadURL('file://' + __dirname + '/main-browser/index.html')
- mainWindow.on('closed', function () {
- mainWindow = null
- })
- })
- ```
- ## 2. Component 配置
- `index.html`
- ```html
- <!DOCTYPE html>
- <html>
- <head>
- <title>Picmod</title>
- <style>
- body {
- margin: 0;
- }
- #canvas {
- width: 100%;
- }
- </style>
- </head>
- <body>
- <div id='mainWindow'>
- <div id='pallet'>
- <button id='open'>Open</button>
- <button id='reset'>Reset</button>
- <button id='filter1'>Filter1</button>
- <button id='filter2'>Filter2</button>
- <button id='filter3'>Filter3</button>
- </div>
- <img id='canvas'>
- </div>
- <script>
- require('./index.js')
- </script>
- </body>
- </html>
- ```
- 簡単に写真を編集するボタンとイメージたぐを配置した。
- ## 3. 機能実装
- `index.js`を次のように作成する。
- ```js
- const fs = require('fs')
- const path = require('path')
- const $ = require('jquery')
- const Jimp = require('jimp')
- const electron = require('electron')
- const remote = electron.remote
- const data = {
- originalPath: null,
- tempPath: null
- }
- function getPicturePath () {
- return remote.app.getPath('pictures')
- }
- function getTempPath (filename) {
- var dataPath = remote.app.getPath('appData')
- if (filename != null) return path.join(dataPath, filename)
- return dataPath
- }
- function openImage () {
- var selectedPaths = remote.dialog.showOpenDialog({
- defaultPath: getPicturePath(),
- properties: ['openFile'],
- filters: [{
- name: 'Images',
- extensions: ['jpg', 'png', 'gif']
- }]
- })
- if (selectedPaths == null) return null
- return selectedPaths[0]
- }
- function copyFile (originalPath, targetPath, cb) {
- var rstream = fs.createReadStream(originalPath)
- var wstream = fs.createWriteStream(targetPath)
- rstream.pipe(wstream)
- rstream.on('end', cb)
- }
- $('#open').on('click', function () {
- if (data.tempPath != null) {
- fs.unlinkSync(data.tempPath)
- data.originalPath = null
- data.tempPath = null
- }
- var originalPath = openImage()
- if (originalPath != null) {
- data.originalPath = originalPath
- var filename = path.basename(originalPath)
- data.tempPath = getTempPath(filename)
- copyFile(data.originalPath, data.tempPath, function () {
- $('#canvas').attr('src', 'file://' + data.tempPath + '?' + new Date().getTime())
- })
- }
- })
- $('#reset').on('click', function () {
- copyFile(data.originalPath, data.tempPath, function () {
- $('#canvas').attr('src', 'file://' + data.tempPath + '?' + new Date().getTime())
- })
- })
- $('#filter1').on('click', function () {
- if (data.tempPath == null) return false
- Jimp.read(data.tempPath, function (err, image) {
- if (err) {
- console.error(err)
- }
- image.posterize(5).write(data.tempPath, function () {
- $('#canvas').attr('src', 'file://' + data.tempPath + '?' + new Date().getTime())
- })
- })
- })
- $('#filter2').on('click', function () {
- if (data.tempPath == null) return false
- Jimp.read(data.tempPath, function (err, image) {
- if (err) {
- console.error(err)
- }
- image.sepia().write(data.tempPath, function () {
- $('#canvas').attr('src', 'file://' + data.tempPath + '?' + new Date().getTime())
- })
- })
- })
- $('#filter3').on('click', function () {
- if (data.tempPath == null) return false
- Jimp.read(data.tempPath, function (err, image) {
- if (err) {
- console.error(err)
- }
- image
- .color([
- { apply: 'hue', params: [ -90 ] }
- ])
- .write(data.tempPath, function () {
- $('#canvas').attr('src', 'file://' + data.tempPath + '?' + new Date().getTime())
- })
- })
- })
- ```
- 全体的な機能はOpen buttonから写真を開いて、写真に幾つかの効果をいれてみることである。Jqueryは普通につかえるし、Jimpの使い方自体はあまり気にしなくていい。
- 今回重要であるのは、わざわざ`remote`から`dialog`をよびだしていることである。
- 一般的にWeb appでファイルを受け取るときは`<input type='file'>`を使う。
- しかし、今回の場合は`dialog`を呼び出したほうがいい。なぜなら、Electronも一応Chromeであって、`input`タグからファイルを選択しても正確な経路が取れない。
- (保安的な理由で`C:\fakepath\~`のように変な経路を返す)
- なので、正確な経路を取るためには`dialog`を使う必要がある。
- では、実行してみよう。
- ## 4. Web worker?
- 実行してみると一応働くと思うが、3番目のフィルターがかなり重いと思う。さらい、致命的な問題はJimpが純粋なJSでかかれているため、画面(Renderer process)が完全に凍ってしまう。
- 今回はその問題を解決するために、他のProcessから重い作業を起動させて、Renderer processが止まらないようにする。
- ここでWeb workerを使えばいいと思うが、ElectronはWeb workerが使えない。
- 正直に言うといらない。殆どのNodeのmoduleがElectronでも使えるので、`child_process`を使えば簡単に解決できる。
- では、やってみよう。
- まず、アプリが凍っているのかを確認するため簡単にlogを吐き出すボタンを`index.html`に追加する。
- ```html
- <button onclick='console.log("still alive!")'>Check</button>
- ```
- では、`child_process#fork`でフィルター3の部分を実行できるようにコードを分離してみよう
- 。
- `index.js`
- ```js
- var worker = null
- const ChildProcess = require('child_process')
- $('#filter3').on('click', function () {
- if (data.tempPath == null) return false
- if (worker) return false
- console.log(data.tempPath)
- worker = ChildProcess.fork(path.join(__dirname, 'filter3.js'), [data.tempPath])
- worker.on('exit', function () {
- console.log('done')
- $('#canvas').attr('src', 'file://' + data.tempPath + '?' + new Date().getTime())
- worker = null
- })
- })
- ```
- `filter3.js`
- ```js
- const Jimp = require('jimp')
- Jimp.read(process.argv[2], function (err, image) {
- if (err) {
- console.error(err)
- }
- image
- .color([
- { apply: 'hue', params: [ -90 ] }
- ])
- .write(process.argv[2], function () {
- process.exit()
- })
- })
- ```
- 実行してみると、フィルター3が処理中であっても、Checkボタンが働くと思う。
- ## 5. Native module
- しかし、これは一番最適なゴールではない。
- 本質的に遅すぎることが問題なので、Native moduleを入れる必要がある。
- ```sh
- npm i -S lwip
- ```
- LwipもJimpもほぼおなじものであるが、Native moduleである。
- `index.js`の3番めのFilterを次のように書き直す。
- ```js
- $('#filter3').on('click', function () {
- if (data.tempPath == null) return false
- lwip.open(data.tempPath, function (err, image) {
- if (err) {
- throw err
- }
- image
- .hue(90, function (err, image) {
- image.writeFile(data.tempPath, function (err) {
- if (err) throw err
- $('#canvas').attr('src', 'file://' + data.tempPath + '?' + new Date().getTime())
- })
- })
- })
- })
- ```
- 一応コードはこれで終わる。
- しかし、実行をしてみるとできないと思う。
- 理由はNative moduleは今使っているNodeに合わせてRebuildしなきゃならない。
- ElectronもNodeが実装されているが、今設置したNPMはそのNodeから実行されていない。
- つまり、バイナリーが設置されたらRebuildする必要がある。
- その役割は、`electron-rebuild`というものがする。
- ```sh
- npm i -D electron-prebuilt electron-rebuild
- ```
- `electron-rebuild`は`electron-prebuilt`を必要とするので、改めて設置する。
- 次にRebuildさせる。
- ```sh
- ./node_modules/.bin/electron-rebuild
- ```
- メッセージは何も出ないけど、これができたらおわりである。
- 実行してみるとめっちゃ時間がかかった3番めのFilterがかなり早くなっていることがわかると思う。
- ### Tip
- ちなみに、`package.json`の`scripts.postinstall`に`electron-rebuild`入れたら、次から設置するNative moduleは勝手にRebuildされるようにしたらかなり楽になる。
- ```js
- {
- "scripts": {
- "start": "electron index.js",
- "postinstall": "electron-rebuild"
- },
- }
- ```
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement