electron-vue で生成したプロジェクトで electron-connect を使う

こんにちは。

今回は electron-vue で生成したプロジェクトで、
BrowserProcess 側、RendererProcess 側、両方のコードのリロードをさせる方法を紹介します。

コードのリロードは、electron-connect を利用します。
こちらで紹介されていました。すごく便利です。

ぼくのかんがえたさいきょうのElectron - Qiita
# はじめに(http://electron.atom.io/)はNode.js + HTML5フロントエンドのいいとこ取りな開発が行えるのが特徴です。その分、開発環境もElectronならではの考慮・工夫が...

ソースコード

Github にて公開しています。

electron-connect に対応させる

プロジェクトの生成

electron-vue でのプロジェクトの生成方法は、下の記事がわかりやすかったので参考にしてください。

ElectronでVue.jsを始める - Qiita
今回は、EletronをVue.jsでどうやって使うか、セットアップをするかを記事に書いていきたいと思います。このアドベントカレンダーの23日目の、@nakajmgさんと内容が少...

generate-electron-vue

この記事では、↑の設定で生成しました。

npm でインストール

$ npm install --save-dev electron-connect

tasks/runner.js を変更

これは、 npm run dev したときに実行されるファイルです。

--- a/tasks/runner.js
+++ b/tasks/runner.js
@@ -3,6 +3,8 @@
 const config = require('../config')
 const exec = require('child_process').exec
 const treeKill = require('tree-kill')
+const electronConnect = require('electron-connect').server.create()
+const fs = require('fs')

 let YELLOW = '\x1b[33m'
 let BLUE = '\x1b[34m'
@@ -33,10 +35,14 @@ function run (command, color, name) {
      * Start electron after successful compilation
      * (prevents electron from opening a blank window that requires refreshing)
      */
-    if (/Compiled/g.test(data.toString().trim().replace(/\n/g, '\n' + repeat(' ', command.length + 2))) && !isElectronOpen) {
-      console.log(`${BLUE}Starting electron...\n${END}`)
-      run('cross-env NODE_ENV=development electron app/src/main/index.dev.js', BLUE, 'electron')
-      isElectronOpen = true
+    if (/Compiled/g.test(data.toString().trim().replace(/\n/g, '\n' + repeat(' ', command.length + 2)))) {
+      if (!isElectronOpen) {
+        console.log(`${BLUE}Starting electron...\n${END}`)
+        electronConnect.start()
+        isElectronOpen = true
+      } else {
+        electronConnect.reload()
+      }
     }
   })

@@ -54,3 +60,7 @@ function exit (code) {

 console.log(`${YELLOW}Starting webpack-dev-server...\n${END}`)
 run(`webpack-dev-server --hot --colors --config webpack.renderer.config.js --port ${config.port} --content-base app/dist`, YELLOW, 'webpack')
+
+let timeoutId = null
+fs.watch('app/src/main', () => {
+  if (!timeoutId) {
+    electronConnect.restart()
+    timeoutId = setTimeout(() => { timeoutId = null }, 500)
+  }
+})

普段僕はダブルクォーテーション&セミコロンを付けて開発しますが、
ここは合わせます。

変更点としては、

  • isElectronOpen が false のとき、electronConnect を start します。
    • この時 Electron が勝手に起動されます。
    • cross-env の設定をしていないように見えますが、index.dev.js を読み込んだ時に NODE_ENV を production にするようになっているので不要だと思います。
  • isElectronOpen が true のときは、electronConnect を reload します。
  • BrowserProcess 側で動くコードは変更されたら restart します。

package.json を変更

electron-connect では、直接ファイルを指定できないので、
代わりに package.json で指定しましょう。

--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
   "name": "electron-test",
   "version": "0.0.0",
   "description": "An electron-vue project",
+  "main": "app/src/main/index.dev.js",
   "scripts": {
     "build": "node tasks/release.js",
     "build:clean": "cross-env PLATFORM_TARGET=clean node tasks/release.js",

main プロパティを指定しました。

app/src/renderer/main.js を変更

次のように変更します。

--- a/app/src/renderer/main.js
+++ b/app/src/renderer/main.js
@@ -10,3 +10,7 @@ import App from './App'
 new Vue({
   ...App
 }).$mount('#app')
+
+if (process.env.NODE_ENV === 'production') {
+  require('electron-connect').client.create()
+}

webpack.renderer.config.js を変更

依存に指定されていないモジュールによるエラーが起きるので、モジュールを除外します。

--- a/webpack.renderer.config.js
+++ b/webpack.renderer.config.js
@@ -16,7 +16,11 @@ let rendererConfig = {
   entry: {
     renderer: path.join(__dirname, 'app/src/renderer/main.js')
   },
-  externals: Object.keys(pkg.dependencies || {}),
+  externals: Object.keys(pkg.dependencies || {}).concat([
+    "spawn-sync",
+    "utf-8-validate",
+    "bufferutil"
+  ]),
   module: {
     rules: [
       {

実行

ok-electron-vue

改善できる点

  • BrowserProcess 側で動くコードを、runner.js で watch していますが、これは main ディレクトリ直下のファイルしか監視していません。子ディレクトリも監視するには、glob を使うなどする必要があります。
  • electron-connect には、reload 機能も含まれているので、webpack-dev-server を使う必要がなくなりました。
    • これについては、今度書こうと思います。

【Javascript】線の点・太さ(lineWidth)から、輪郭の座標を算出する

こんにちはー。更新が遅れてしまいすみません。

あけましておめでとうございます。今年もよろしくお願いします。

今回は、線の点・太さから、輪郭の座標をとる方法を紹介します。
HTML5 Canvas の stroke() メソッドを、SVG などを使わず、自分で実装する感じです。

なお、自分で考えて手探りでやってみた方法なので、もっと効率のいい方法があるかもしれません。

なぜやりたいのか

matter.js というライブラリがあり、
それを使って、マウスで書いた線を Body に変換したかったのですが、
線を Body に直接することができなかったので、作ろうと思いました。

Constant とか使えばできそうですが、面白そうなのでやってみます。

やってみる

始点・終点

始点は1つ後の点から現在の点、終点は現在の点から1つ前の点を引いて、
逆三角関数でラジアンにし、それに垂直な方向になるように輪郭線を打ちます。

var lineWidth = 5;
var points = [
  { x: 30, y: 50 },
  { x: 120, y: 150 }
];
var outline1 = [];
var outline2 = [];

points.forEach((point, index) => {
  if (index === 0) {
    // 始点
    // Math.PI / 2 は degree で 90°
    var rad = Math.atan2(points[index + 1].y - point.y, points[index + 1].x - point.x) - Math.PI / 2;
    var sin = Math.sin(rad) * lineWidth;
    var cos = Math.cos(rad) * lineWidth;
    outline1.push({ x: point.x + cos, y: point.y + sin });
    outline2.push({ x: point.x - cos, y: point.y - sin });
  } else if (index === points.length - 1) {
    // 終点
    var rad = Math.atan2(point.y - points[index - 1].y, point.x - points[index - 1].x) - Math.PI / 2;
    var sin = Math.sin(rad) * lineWidth;
    var cos = Math.cos(rad) * lineWidth;
    outline1.push({ x: point.x + cos, y: point.y + sin });
    outline2.push({ x: point.x - cos, y: point.y - sin });
  }
});

See the Pen Drawing outlines of a line by shundroid (@shundroid) on CodePen.0

途中の点

いやー、これが難しかったです・・。
方法としては、輪郭線は角の二等分線上で交わるので、
そこで、sinθ = lineWidth となるときの cos をとってくる感じです。

求めた後、回転移動させるのですが、まずは移動させなくてもいいパターンでやってみます。

※省略部分は↑のコードと変わらない部分です。

var lineWidth = 5;
var points = [
  { x: 200, y: 100 },
  { x: 100, y: 100 },
  { x: 150, y: 50 }
];

// 省略

points.forEach((point, index) => {
  if (index === 0) {
    // 省略
  } else if (index === points.lenght - 1) {
    // 省略
  } else {
    var rad1 = Math.atan2(points[index - 1].y - point.y, points[index - 1].x - point.x);
    var rad2 = Math.atan2(points[index + 1].y - point.y, points[index + 1].x - point.x);
    var rad = (rad2 - rad1) / 2;
    var x = Math.cos(rad) * lineWidth / Math.sin(rad);
    var y = lineWidth;
    outline1.push({ x: point.x + x, y: point.y + y });
    outline2.push({ x: point.x - x, y: point.y - y });
  }
});

See the Pen Drawing outlines of a line 2 by shundroid (@shundroid) on CodePen.0

回転移動させる

これでできたっぽいですが、これは入ってくる線の角度が 0° だったときのみ動きます。
それ以外で動かすようにするには、いったん入ってくる線を基準にして求め、それを回転移動させる必要があります。

回転移動については下のサイトが詳しいです。参考にしてみてください。
http://www.geisya.or.jp/~mwm48961/kou2/linear_image3.html

rad1 を基準にしたので、 rad1 の分だけ回転させればいいのです。

var lineWidth = 5;
var points = [
  { x: 200, y: 100 },
  { x: 100, y: 100 },
  { x: 150, y: 50 }
];

// 省略

points.forEach((point, index) => {
  if (index === 0) {
    // 省略
  } else if (index === points.lenght - 1) {
    // 省略
  } else {
    var rad1 = Math.atan2(points[index - 1].y - point.y, points[index - 1].x - point.x);
    var rad2 = Math.atan2(points[index + 1].y - point.y, points[index + 1].x - point.x);
    var rad = (rad2 - rad1) / 2;
    var x = Math.cos(rad) * lineWidth / Math.sin(rad);
    var y = lineWidth;

    var rx = x * Math.cos(rad1) - y * Math.sin(rad1);
    var ry = x * Math.sin(rad1) + y * Math.cos(rad1);
    outline1.push({ x: point.x + rx, y: point.y + ry });
    outline2.push({ x: point.x - rx, y: point.y - ry });
  }
});

See the Pen Drawing outlines of a line 3 by shundroid (@shundroid) on CodePen.0

これでできました!間の点はいくつあってもできます。

See the Pen Drawing outlines of a line 4 by shundroid (@shundroid) on CodePen.0

random にやっているので、急カーブになった場合、輪郭がちょうとんがります。
これは↓でもある、miterLimit を早く実装して直したいです。

考えた別の方法

線を直線の式に変えて、連立方程式で交点を出す

中学2年生で習うことでできそうですが、
どの線とどの線が交わるのかを見つけるのが超難しそうだったのでやめました。

今後やりたいこと

lineCap、lineJoin、miterLimit を実装する

Canvas にある、これらのプロパティも実装してみたいです。
miterLimit は今がんばっています。