アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

透過がサポートされたnode-webkit(NW.js)でガジェットを作ろう

こんにちは、古見澤(コミザワ)です。
今回は、前回記事にした node-webkit が透過をサポートし始めたので、HTML5JavascriptCSSなどのWeb系の言語でガジェットのようなモノを作ってみようという内容となります。

ツイッターから情報を取得して表示するだけの単純なものですが、こんな感じのものを作れます。

動画


node-webkitって何?という方は、前回の記事も合わせてお読みください。

HTML5+CSS3+JSでネイティブGUIアプリが作れる、node-webkitを触ってみる

※本エントリーはWindows環境(Win7 Professional x64)での話になります。
(透過機能はMacLinuxでも使えます、後述するマニュアルを参照ください。)
また、Windows環境では、デスクトップウィンドウマネージャーが有効である必要があるため、
Aero機能が使えない(もしくは無効化にしている)環境ではお試しいただくことができません。


準備



まずは試してみましょう。
前回Blog執筆時の最新バージョンは 0.10.3 でしたが、ウィンドウ透過は去年11月26日にリリースされた 0.11.2 でサポートされました。
起動の仕方などは前回の記事にも書いてありますので、初めて触る人はそちらもご参照ください。

node-webkit配布元Downloadsから目的の環境に合わせたファイルをダウンロードします。
私の場合はWindowsの64bitですね。
「v0.11.5」と「0.8.6」がありますが、後者はまだ透過をサポートしていないため、試す場合は前者をダウンロードするようにしてください。

【補足】1月15日に node-webkit は Node.js から io.js にマイグレートし、NW.jsという名称に変更されました。
公式サイト
それに伴い、最新版の v0.12.0 が公開されていますが、まだalphaなので今回は v0.11.5 を使います。
昨年末くらいから一部で話題になっているNode.jsとio.jsの問題に対応しただけで、基本的に中身は変わらないみたいです。


ダウンロードが終わったら解凍し、適当なパスに配置します。
わかりやすくフォルダ名を「node-webkit-v0.11.5-win-x64」から「node-webkit」にしておきます。

動作確認用に、シンプルに最小限のファイルを準備します。
node-webkitフォルダの中に資源を格納するために、適当な名前のフォルダを作ります。
ここでは「app」にしていますが何でも構いません。別の名前にした方は適宜読み替えて下さい。

作成したappフォルダの中に「index.html」と「package.json」の2ファイルを作成します。
それぞれのファイルの中身は以下の通りです。文字コードは「UTF-8」で保存します。

index.html



<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>透過テスト</title>
  <style>
    body {
      margin: 0;
      padding: 0;
    }
    .box {
      width: 100px;
      height: 100px;
      background-color: red;
    }
  </style>
</head>
<body>
  <div class="box">
    透過テスト
  </div>
</body>
</html>


package.json



{
  "name": "透過アプリ",
  "main": "index.html"
}


ここまで終了すると、ファイル構成は以下のような感じになります。



node-webkit
│  credits.html
│  d3dcompiler_46.dll
│  ffmpegsumo.dll
│  icudtl.dat
│  libEGL.dll
│  libGLESv2.dll
│  nw.exe
│  nw.pak
│  nwsnapshot.exe
│  pdf.dll
│
├─app
│      index.html
│      package.json
│  
└─locales
        am.pak
        ar.pak
         :
         :


では一旦ここでアプリを起動します。
appフォルダをnw.exeにドラッグ&ドロップしてもいいのですが、今回は実行時にパラメータを追加する必要が出てくるので、ショートカットを作成してそこから実行することにします。

nw.exeを右クリックして「ショートカットの作成」を選択。
更に作成したショートカットを右クリックして「プロパティ」を選択。
リンク先の内容の最後に、半角スペースで間を開けて先ほど作ったフォルダ名を追加。
(ショートカットの名前は何でも構いません、最悪そのままでも。ここではtransparentとします。)



ショートカットの準備ができたら実行します。
透過アプリという名前の、赤い正方形が描かれた白いウィンドウが出てくれば、まずは動作テスト完了です。



ウィンドウを透過させてみよう



ウィンドウを透過させる方法は、設定ファイルに記述するか、プログラムで動的に変えるかの二通りがありますが、今回は前者の紹介です。
package.jsonを、以下のように変更します。

package.json



{
  "name": "透過アプリ",
  "main": "index.html",
  "window": {
    "transparent": true
  }
}


透過設定(transparent)は、windowサブフィールドの項目の一つとして実装されています。
値はbooleanで、デフォルトは false(無効)なので true(有効)に設定します。
ファイル保存後、先ほどのテスト動作させたウィンドウが残っているなら閉じて、改めて起動してください。
赤い部分やタイトルバー、アドレス欄以外が透けたアプリが起動すれば成功です。

構文エラーに注意してください。
間違ったまま起動するとエラー画面が表示されるので、エラーメッセージを読んでカンマの有無やtypo等、確認してみてください。

透過させるには、デスクトップウィンドウマネージャー(DWM)が有効になっている必要があります。
ここで最初のテスト動作と同じウィンドウが表示される場合は、OSの設定を確認してください。

よくわかんなければ、デスクトップを右クリックし「個人設定」を選ぶとテーマの一覧が出てくるので、Aeroテーマ(のどれか)を選択すると改善される場合があります。
Aeroは重くなるから普段は切っている、という方はまず間違いなくこれに該当すると思いますので、一時的にAeroテーマに切り替えてテストしてみてください。
なお、この辺りのOS設定に関するご質問は受けられませんのでご了承ください。

ウィンドウの余計な部分を削除する



さて、背景は透明になったものの、タイトルバーやウィンドウの枠が邪魔なので消しましょう。
ついでにリサイズを出来なくして、常に最前列に表示するようにします。
package.jsonに更に記述を追加します。

package.json



{
  "name": "透過アプリ",
  "main": "index.html",
  "window": {
    "transparent": true,
    "toolbar": false,       //ツールバーを非表示に
    "frame": false,         //フレームを非表示に
    "resizable": false,     //ウィンドウのリサイズを無効化
    "always-on-top": true   //常に最前列に表示させる
  }
}


この他の記述項目についても知りたい方はマニュアルをどうぞ。

そして起動する前にもう一つ。
ツールバーを消すと、そのままではウィンドウの移動が出来なくなってしまうので、
赤い四角をドラッグしてウィンドウ自体を動かせるよう、CSSに一行追加します。

index.html



<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>透過テスト</title>
  <style>
    body {
      margin: 0;
      padding: 0;
    }
    .box {
      width: 100px;
      height: 100px;
      background-color: red;
      -webkit-app-region: drag;  /* 追加 */
    }
  </style>
</head>
<body>
  <div class="box">
    透過テスト
  </div>
</body>
</html>


それでは起動してみましょう。
フレームやツールバー部分が消えて、ポツンと赤い四角だけ表示された状態になると思います。
そしてこの赤い四角をドラッグすることで動かせるようになっています。
-webkit-app-regionを設定する場所を考えれば、ウィンドウを動かすための「ツマミ」の実装もできます。

また、常に最前列で表示する設定にしたので、他のウィンドウを選択してもその上に表示されるようになったかと思います。

最初のサンプル動画では、透過色を設定したPNGを表示させています。
また、CSSで背景色を rgba(255, 0, 0, .5) のように指定すると半透明にも出来ます。

透過部分の背後にクリックが伝達するようにする



さて、ここまででもアレコレ作れそうな気もしますが、まだ行っていない大事なことがあります。
上のサンプルだとわかりにくいですが、実はウィンドウ自体が透明になっていても、透けて見える後ろ側をクリックすることが出来ません。
せっかく透明にしても、クリックできない領域が広がっていては使い勝手悪いですよね。

そんな透過部分のクリックを背後に伝達できる設定(click-through)が、去年の12/24にリリースされたバージョン 0.11.4 で可能となりました。
但し、この記事を書いている時点でサポートしているのはWindowsmacのみで、Linuxについては完全にサポートはされていません。

さて肝心のclick-throughのやり方ですが、これまでの設定ファイルに記述するものとは違い、今回は実行時にパラメータを指定します。



 --disable-gpu --force-cpu-draw


冒頭の方で作ったショートカットを編集すると、こんな感じになります。



また、click-throughは「フレーム無し」で「リサイズ不可」なウィンドウのみ、サポートしています。
また先ほど書きましたが、「現時点ではOSXWindowsのみ」のサポートとなっています。
Linux環境は、「環境や設定によっては動くかも知れない」としており、issueでディスカッションされています。
透過についてはここ数ヶ月の間に次々と対応されていることもあり、今後の動きに期待です。

冒頭のサンプル



ここまでの設定で、ガジェットのようなものを作る土台は大体揃ったのではないかと思います。
まずは透過設定を一度オフにして、ウィンドウの大きさを確認しながら作るとやりやすいかもしれません。

以下、Blog用に何か動くものをと、急ごしらえしたソースです。稚拙なコードですが、参考にどうぞ。
キャラクター表示しているdivを1個、吹き出し用のdivを1個置いただけのシンプルな内容です。

せっかくnodeが使えるので、twitをインストールして、TwitterのStreamingAPIを利用してみました。
単に文字が表示されるだけでは面白くないので、動きを出すためにキャラクターに喋らせてみました。
(キャラクター画像はキャラクターなんとか機をお借りしました。)
某デスクトップマスコットみたいですが、まぁこんなのも作れますよということで一つ。

index.html



<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>sample app for node-webkit</title>
  <script src="http://code.jquery.com/jquery-1.11.2.min.js"></script>
  <style>
    body {
      overflow  : hidden;
      margin  : 0;
      padding  : 0;
      height  : 100%;
    }
    #character {
      -webkit-app-region: drag;
      position  : absolute;
      right  : 0;
      top  : 0;
      width  : 300px;
      height  : 400px;
    }
    .default {
      background  : url(character/default.png) no-repeat right bottom;
    }
    .angry {
      background  : url(character/angry.png) no-repeat right bottom;
    }
    .talk {
      background  : url(character/talk.png) no-repeat right bottom;
    }
    #comment {
      font-family  : "メイリオ";
      position  : absolute;
      left  : 10px;
      top  : 100px;
      width  : 300px;
      padding  : 10px;
      background-color  : rgba(255,255,200,1);
      border  : solid 2px #660000;
      border-radius: 10px;
    }
    #comment:before {
      content: '';
      position: absolute;
      border-left: solid 20px #660000;
      border-top: solid 13px transparent;
      border-right: solid 10px transparent;
      border-bottom: solid 7px transparent;
      top: 20px;
      right: -30px;
    }
    #comment:after {
      content: '';
      position: absolute;
      border-left: solid 18px rgba(255,255,200,1);
      border-top: solid 11px transparent;
      border-right: solid 8px transparent;
      border-bottom: solid 5px transparent;
      top: 22px;
      right: -26px;
    }
  </style>
  
  <script>
    //ウィンドウを右下に配置(マルチディスプレイは今回考慮せず)
    var gui = require('nw.gui');
    var win = gui.Window.get();
    win.moveTo(
      window.screen.availWidth - win.width,
      window.screen.availHeight - win.height
    );
    
    var commentTimer = null;
    
    // TwitterのStreamingAPIの利用部分。
    var Twit = require('twit');
    var T = new Twit({
        consumer_key:         '(この部分はご自分のアプリケーション設定に置き換えます)'
      , consumer_secret:      '(この部分はご自分のアプリケーション設定に置き換えます)'
      , access_token:         '(この部分はご自分のアプリケーション設定に置き換えます)'
      , access_token_secret:  '(この部分はご自分のアプリケーション設定に置き換えます)'
    });
    var stream = T.stream('statuses/filter', { track: 'ニュース' }); //ひとまず「ニュース」をキーワードに設定
    
    stream.on('tweet', function (tweet) {
      var data = tweet.text;
      $('#character').attr('class', 'talk');
      $('#comment').text(data).fadeIn();
      
      if (commentTimer) {
        clearTimeout(commentTimer);
        commentTimer = null;
      }
      commentTimer = setTimeout(function(){
        $('#comment').fadeOut();
        $('#character').attr('class', 'default');
      }, 3000);
    });
  </script>
</head>
<body>
  <div id="character" class="default"></div>
  <div id="comment" style="display:none;"></div>
</body>
</html>


package.json



{
  "name": "sample-app",
  "main": "index.html",
  "single-instance": true,
  "window": {
    "transparent": true,
    "toolbar": false,
    "frame": false,
    "resizable": false,
    "always-on-top": true,
    "width": 600,
    "height": 400
  }
}


フォルダ構成はこんな感じになってます。



node-webkit
│  index.html
│  package.json
│  
├─character (キャラ毎にフォルダ作ればスキン変更ができそう)
│      angry.png
│      default.png
│      talk.png
│      
└─node_modules
    └─twit
        │
         :(以下、twitインストールで格納されるファイル)


なお、画像等の素材や、npmでインストールするファイルはご自分でご用意ください。

HTMLやJavascriptは知ってるけどnodeとか触った事ない……という人がこのサンプルでつまづきそうな事を列挙しておきます。
長くなってしまうのでここでは詳しく解説しませんが、どういう事柄を調べればいいのか参考にしてください。

・node.jsやnpmの導入のやり方、npm installのやり方 等
・TwitterAPIを使うための、Twitter Developersへの登録 トークンの作成等
・twitの使い方
・サーバサイドスクリプトの書き方

最後に



今回の透過に関する設定などは、公式のドキュメントからでも見る事ができます。
最近よく更新されているみたいなので、時々目を通すといいかもしれません。

node-webkitでは、HTMLやJavascriptといったWeb開発の技術がそのまま使える事に加え、node.jsで割と突っ込んだコードを書けるため、アイデア次第でとてもいいものが作れるのではないかと思っています。
特に今回紹介した、透過に関する機能がサポートされてグンと化けた感じがします。
特別なアプリをインストールする必要も無く、作り終えたアプリもソースを圧縮すれば簡単に手渡せる気楽さもあるので、意欲がある方は自作アプリを作って公開などしてみてはいかがでしょうか。