概要
その名も、HourTimer !
一時間を測るだけという、シンプルなデスクトップアプリです。
Vue.jsの勉強もかねて、、electron-vueを使ってみました。
日本語ドキュメント
こちらからダウンロードできますが、
配布サイトは適当に作ったので、クオリティは気にしないでくださいw
また、署名の問題でエラーが出てくるかもしれませんが、ウイルスなどはありません。
証拠に、ソースコードを公開しておきます。笑
アプリ概要
アプリの機能は、一時間を測るという単純なものです。
見た目はこんな感じ。
停止ボタンでタイマーはストップします。
一度ストップすると、アプリを閉じてもMacを閉じても、
残り時間は保持される機能も付きです!
リセットボタンで、残り時間が一時間に戻ります。
以下、ソースコードをもとに、
実装などを紹介していきます!
まず、electron-vueとは?
electronは、javascriptを使ってデスクトップアプリを開発するフレームワーク、
Vue.jsはjavascriptの便利なライブラリです。
では、electron-vueとは何でしょうか?
日本語ドキュメントから引用すると、以下のように書いてあります。
vue で構築された electron アプリケーションを作成するためのボイラープレートです(名前から分かるように)。
ということです(名前からわかるように)!
説明については、日本語ドキュメントや、他の方のQiita記事でわかりやすいものがあるので、詳しくはそちらを御覧ください!
2017年度版 electron-vueで始めるVue.js
また、electronやVue.js自体についても、公式のドキュメントが充実しています。
そちらをご覧くださいませ!
時間をカウントする実装
普段、javascriptを書いているのですが、時間を測る実装方法を知りませんでした。
そこでgoogleすると、以下のようなサイトがヒットしました!😀
Vue.js でタイマーを作る
Vue.js でシンプルなストップウォッチを作ってみた
どうやら、requestAnimationFrameというメソッドを使うのがよさげでしたので、使ってみました。
該当するファイルはTimer.vue
になります。
その中で,startTimer,stopTimerというメソッドが主です。
...
startTimer: function() {
let vm = this;
if (this.remainTime == 0) {
this.remainTime = 3600000;
}
vm.isCount = true;
vm.startedTime = Math.floor(performance.now() - vm.diffTime);
(function loop() {
let loopfps = vm.diffTime;
vm.nowTime = Math.floor(performance.now());
vm.diffTime = vm.nowTime - vm.startedTime;
vm.remainTime -= vm.diffTime - loopfps;
if (vm.remainTime % 10000 < 20) {
localStorage.setItem("remainTime", vm.remainTime);
}
if (vm.remainTime > 0) {
vm.animateFrame = requestAnimationFrame(loop);
} else {
vm.finishTimer();
}
})();
}
ここから、時間をカウントする部分だけ切り取ると、、、
(function loop() {
let preDiffTime = vm.diffTime;
vm.nowTime = Math.floor(performance.now());
vm.diffTime = vm.nowTime - vm.startedTime;
vm.remainTime -= vm.diffTime - preDiffTime;
if (vm.remainTime > 0) {
vm.timeCountFrame = requestAnimationFrame(loop);
} else {
vm.finishTimer();
}
})();
残り時間は vm.remainTime
で、これが0になるまでカウントを続けます。
stopTimerメソッドではcancelAnimationFrame(this.timeCountFrame)
を実行することで、カウントを止めています。
タイマーの見た目はどう実装する
できるだけ楽をしたかったので、円を描いているいい感じのライブラリを探しました。
そこでchart.js,vue-chartjsを見つけて
「円グラフを使えば楽にできるのでは・・・?🤔」
と思いつき、実装を試みたのですが・・・。
挫折しました😅
なので、素直にhtmlのcanvas要素を使いました!😆
canvasで困ったこと
canvasでタイマーを作成するにあたって困ったことがいくつか・・・。
canvasのレスポンシブ対応
単純にレスポンシブにすると、windowを大きくしたときにcanvas内がジャグジャグしてしまう問題にぶち当たりました。
解決策としては、あらかじめ大きく作ったcanvas要素を縮小・拡大することにしました。
<canvas id="timerLine" width="1000" height="1000"></canvas>
こんな感じ!
タイマーのアニメーション
canvasの中に円を描く事自体は簡単なのですが、canvasをどうやって動的に書き換えようか、少し悩みました。
結論としては、予め書いておいたベージュの円に上書きする形で、黒の円を描くことにしました。
TimerLine.vueが該当するファイルです。
...
mounted: function() {
const timerLine = document.getElementById("timerLine");
this.ctx = timerLine.getContext("2d");
let ctx = this.ctx;
ctx.beginPath();
ctx.lineWidth = 50;
ctx.strokeStyle = "#F5E5B3";
ctx.arc(500, 500, 450, 0 * Math.PI / 180, 360 * Math.PI / 180);
ctx.stroke();
ctx.closePath();
let img = new Image();
img.src = require("../assets/pencil.png");
img.onload = function() {
ctx.drawImage(img, 250, 250, 500, 500);
};
ctx.lineWidth = 51;
ctx.strokeStyle = "#1D1C22";
ctx.beginPath();
let remainAngle = (60 - this.remainMin) * 6 - 90;
ctx.arc(500, 500, 450, -90 * Math.PI / 180, remainAngle * Math.PI / 180);
ctx.stroke();
ctx.closePath();
},
updated: function() {
if (this.remainMin == 60) {
this.initTimer();
return;
}
let ctx = this.ctx;
ctx.strokeStyle = "#1D1C22";
ctx.beginPath();
let remainAngle = (60 - this.remainMin) * 6 - 90;
ctx.arc(500, 500, 450, -90 * Math.PI / 180, remainAngle * Math.PI / 180);
ctx.stroke();
ctx.closePath();
},
残り時間が一分減るごとに、6度分長い黒線を描いています。
vueのライフサイクルフックであるmounted,updatedを使用することで、canvasを動的に描くことは割と簡単に実装できました😚
アプリを閉じても残り時間を保持する
個人的に、これが一番欲しかった機能です。
しかし、実装はかなり単純で、localstorageを使うだけです。
stopTimer: function() {
cancelAnimationFrame(this.timeCountFrame);
this.isCount = false;
localStorage.setItem("remainTime", this.remainTime);
}
この三行目で,残り時間を
localStorageに保存しています。
あとの詳細については、実際のコードを見るのが早いと思います!
質問やissueなどしてくださると,泣いて喜びます😂
とりあえず作ってみた感想
いつもweb触っている人なら、electronを使えば簡単に、いつもと変わらない開発方法で
デスクトップアプリが開発できると実感しました!
今回のレベルだと、わざわざvueを使う必要がなかった気もしますが・・・
まあ、とりあえずよしとします!笑
人に見せられるレベルまで作れてよかったです!
今後実装してみたい機能
今回は、electronのAPIは叩いていないので、それらを使う機能を加えてみたい!
加えやすいのは、「通知を出す」とかかなあ・・・。
また、もともと「一日一時間頑張る」ために使うことを想定していたので、
毎日の頑張った時間を記録できる機能がつけれたらいいなあ、と思っています。