九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、少し前に公開した初心者向き!electron で簡単なメモ帳をつくってみようという記事が思いのほか好評のようでした。
正直な所Electron
の開発は個人的にも好きなので、せっかくなら何かブログ執筆に役立つアプリをつくってみようと考えるようになりました。
そこで、今回作るアプリは 画像をドラッグ&ドロップするだけでリサイズする アプリです。つまり、ファイルを選んで(複数可)Electronのウィンドウに持っていけば、自動でリサイズが実行できるという便利アプリですね。
というのも、このブログの画像は最大横幅を640px
と決めていているのですが、たまに大きな画像がたくさんあるときはいちいちconvert
コマンドを打って変換していて、なかなかめんどうなのです。(もっというとプラグインも入れてますが、このプラグインは一旦別サーバーへ送信してから変換し、その直後にダウンロードするタイプなので、アップロードに時間がかかってしまうのです・・・)
では、さっそく開発を始めましょう!
※ 開発環境: Electron 2.0.5
目次
Electron開発ができるようにする
インストールは、初心者向き!electron で簡単なメモ帳をつくってみようの “electronのインストール” に詳しく書いてあるので、そちらを見てください。
フォルダ名はauto-resize
です。
アプリの設定をする
アプリの設定はmain.js
で行います。
ではやってみましょう!
const electron = require('electron') const app = electron.app const BrowserWindow = electron.BrowserWindow let mainWindow function createWindow () { const screen = electron.screen.getPrimaryDisplay() const width = 350 const height = 350 const x = parseInt(screen.workAreaSize.width*0.5 - width*0.5) // Create the browser window. mainWindow = new BrowserWindow({ width: width, height: height, x: x, y: 0, autoHideMenuBar: true, webPreferences: { preload: path.join(__dirname, 'preload.js'), nodeIntegration: true } }) // 省略
【追記:2020.3.19】Electron v5
以降のためにwebPreferences
を追加しました。
まず、モジュール読み込みはモニター(デスクトップ)のサイズを取得するためにelectron
が必要なので、3行に分けています。
そして、実際にウィンドウを作るcreateWindow()
の中では、ウィンドウのサイズ、メニューバー非表示、そして位置がトップの中央にくるよう計算して設定しています。
これで、このアプリを起動すると画像の位置にウィンドウが現れるようになりました。
レイアウトをつくる
では、アプリのレイアウトをつくりましょう。
やはりここはCSSフレームワークを使ってサクッとつくりたいので次のコマンドでbootstrap
をインストールします。
npm install bootstrap --save
また、今回もJSフレームワークにはVue
を使うのでこちらもインストールします。
npm install vue --save
では、index.html
にこの2つをセットしてレイアウトを作ります。(bootstrapはCSSだけ使います)
<!DOCTYPE html> <html> <head> <title>画像リサイズ</title> <meta charset="UTF-8"> <link rel="stylesheet" href="./node_modules/bootstrap/dist/css/bootstrap.min.css"> <style> .receiving-box-outer { display: table; width: 100%; padding: 15px 0; } .receiving-box-inner { height: 230px; color: #777; border: 10px dashed #aaa; border-radius: 10px; display: table-cell; vertical-align: middle; text-align: center; } </style> </head> <body> <div id="app" class="container"> <div class="receiving-box-outer"> <div class="receiving-box-inner">画像をドラッグ&ドロップしてください。</div> </div> <div class="row"> <div class="col-6"> <label class="text-muted">横幅:</label> </div> <div class="col-6"> <label class="text-muted">高さ:</label> </div> </div> <div class="row"> <div class="col-6"> <input type="number" min="0" class="form-control" placeholder="例: 640"> </div> <div class="col-6"> <input type="number" min="0" class="form-control" placeholder="例: 0(自動)"> </div> </div> </div> <script> require('./renderer.js') </script> </body> </html>
このコードを実行するとこうなります。
JavaScript部分をつくる
では本題の画像をリサイズする部分を作っていきましょう。
Vueの基本形をつくる
まずVue.jsを読み込み基本形を作ります。
<script src="./node_modules/vue/dist/vue.min.js"></script> <script> new Vue({ el: '#app' }) </script>
もちろんHTMLタグへid登録するのも忘れないでください。
<div id="app" class="container">
横幅・高さの入力部分をつくる
サイズ指定の入力ボックスに変数width
、height
をそれぞれv-model
でバインディングしてリアルタイムに中身が変更されるようにします。また、初期値も代入します。
<input type="number" min="0" class="form-control" placeholder="例: 640" v-model="width"> <!-- 省略 --> <input type="number" min="0" class="form-control" placeholder="例: 0(自動)" v-model="height">
new Vue({ el: '#app', data: { width: 640, height: 0 } })
ドラッグ&ドロップのイベントをつくる
では続いて画像がドラッグ&ドロップされたときのイベントをつくります。
ページが表示された時点で必ず実行されるmounted
内にコードを書きましょう。
mounted() { document.ondragover = document.ondragenter = document.ondrop = document.ondragleave = (e) => { if(e.type == 'drop') { const files = e.dataTransfer.files this.resize(files) // ここで画像をリサイズ } e.preventDefault(); } }
※ ちなみにondrop
だけでいいような気がしますが、実際にはその他のイベントでe.preventDefault()
をしておく必要があります。
画像編集パッケージをインストールする
では、実際に画像をリサイズするコードをつくる前にJavaScriptで画像編集ができるlovell/sharpをインストールしておきましょう。
npm install sharp --save
そして、sharp
を読み込みます。
<script> require('./renderer.js') const sharp = require('sharp'); </script>
(注意)ただし、これだけでは実際にはアプリは起動できなくなります。なぜなら、sharp
はnode
のネイティブモジュールなので、electron
用に再構築する必要があるからです。
そのため、続いて次の作業も実行しておきましょう。
まず再構築パッケージをインストールします。
npm install --save-dev electron-rebuild
そして、次のコマンドで再構築を実行します。
./node_modules/.bin/electron-rebuild
画像リサイズ部分をつくる
まずは、ファイル操作に必要な2つのモジュールfs
、path
を呼び出します。
<script> require('./renderer.js') const sharp = require('sharp'); const fs = require('fs'); const path = require('path'); </script>
そして、methods
にresize()
を作ってこの中で画像をリサイズしていきます。
methods: { resize(files) { const width = parseInt(this.width) const height = parseInt(this.height) if(width > 0 || height > 0) { for(file of files) { // ファイルタイプ・チェック if(!file.type.startsWith('image/')) { continue } // リサイズ情報 let dir = path.dirname(file.path) let extension = path.extname(file.path) let filename = path.basename(file.path, extension) let savePath = dir +'/resize-'+ filename + extension let size = { width: (width > 0) ? width : null, height: (height > 0) ? height : null, fit: (width > 0 && height > 0) ? sharp.fit.fill : null } // リサイズ sharp(file.path) .resize(size) .toBuffer() .then((data) => { // 保存 fs.writeFileSync(savePath, data, 'binary') }) } } else { alert('最低でも横幅か高さは指定してください。') } } },
やっていることは、files
をループでひとつずつ処理をしながら、
- ファイルが画像かどうかをチェック
- もし画像ならリサイズ情報をつくる
- そして “sharp” を使って画像をリサイズして保存(*1)
※1 ・・・ 画像のファイル名はresize-******
になります。
なお、リサイズ情報にあるsharp.fit.fill
はアスペクト比(縦横比)を無視する設定です。
テストしてみる
では次の画像を使って実際にテストしてみましょう。
(元画像のサイズ: 300 x 320 ピクセル)
横幅を決めてリサイズする
まずは横幅を150px
、高さを0
(自動)にしてやってみましょう。
ファイルをアプリの中へドラッグ&ドロップします。
すると新しい画像が作成されました。
実際に作成された画像です。
横幅150px
、高さが160px
の画像ができました。
成功です!
高さを決めてリサイズする
では、次は逆に横幅を0
(自動)にして、高さ200px
でやってみましょう。
結果はこうなりました。
横幅188px
、高さが200px
の画像です。
横幅と高さを決めてリサイズする
では、最後に横幅を300px
、高さ150px
でやってみましょう。
結果です。
画像としては歪んでしまいましたが、思い通りにいきました。
おまけ
フォーカスしたら数字を自動選択するようにする
いちいち横幅や高さの数字を消してから入力するのはめんどうなので、フォーカスがあたったら選択状態にするようにします。
<input type="number" min="0" class="form-control" placeholder="例: 1280" v-model="width" @focus="($event.target.select())"> <input type="number" min="0" class="form-control" placeholder="例: 0(自動)" v-model="height" @focus="($event.target.select())">
最後の横幅と高さを自動保存する
毎回サイズを入力するのはめんどうなので、横幅と高さのデータをjson
ファイルに保存し、アプリ起動時に前のデータを代入するようにしましょう。
サイズを保存するコード
まず、変数が変化したときに呼ばれるwatch
にwidth
とheight
を監視するようにし、変化があったらsaveSize()
を呼ぶようにします。
watch: { width() { this.saveSize() }, height() { this.saveSize() } },
そして、saveSize()
ではこれら横幅と高さ2つのデータをJSONにして保存します。
saveSize() { const json = JSON.stringify({ width: this.width, height: this.height }); fs.writeFileSync('size.json', json) }
サイズを呼び出すコード
まず、保存したJSONファイルからサイズを取得するloadSize()
を作ります。
loadSize() { if(fs.existsSync('size.json')) { const json = fs.readFileSync('size.json') const size = JSON.parse(json) this.width = size.width this.height = size.height } }
そして、mounted()
の中でこのloadSize()
を呼ぶようにすればOKです。
mounted() { this.loadSize() // 省略 }
※ なお、説明のために直書きしましたが、size.json
ファイル名は一元管理すべきなのでdata
に入れておくべきです。
data: { width: 640, height: 0, sizeJsonFile: 'size.json' },
教材ソースコードをダウンロードする
今回実際に開発したソースコード一式を以下からダウンロードすることができます。
※ ただし、npm
のパッケージなどはご自身でインストールしてください。(npm install でOKなはずです)