テーマの基本 その10
(円弧描画編)

ここまでは四角形の線と塗りつぶしでバッテリーを表現してきましたが、HTML5のCANVASには、もっといろいろな描画ができます。
ここでは、円弧でバッテリーを表現してみましょう。

円弧を描画する

最終的にバッテリーを表現しますので、円弧自体をバッテリー残量とすることを目的とします。

円弧を描く

以下のような記述で、円弧を描くことができます。
ctx.arc(中心のX座標, 中心のY座標, 半径, 開始角, 終了角, 描画方向); 図解すると、以下のようになります。

描画方向を「true」に指定すると、開始角から終了角までのオレンジ部分の円弧を描くことができます。
角度の単位は「ラジアン」ですので、角度の指定に気をつけましょう。
そして、もっと気をつけなければならないのは、「角度の向き」です。
図では、円の中心を通る水平線(X軸)に対して上向きに矢印が描かれていますが、角度の向きは下向きが「プラス」ですので、図における矢印方向の角度を指定する場合はマイナスになります。

では、上記の円弧を描いてみます。
開始角を-45°、終了角を-135°とします。

では、高さを半分にしないキャンバスに、円弧を描いてみましょう。
プログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {

 var canvas = document.createElement("canvas");
 canvas.height = height ;
 canvas.width = height;
 var ctx = canvas.getContext("2d");

 var r = height/2;
 ctx.strokeStyle="orange";
 ctx.arc(r, r, r, -1/4*Math.PI, -3/4*Math.PI, true);
 ctx.stroke();

 return canvas.toDataURL("image/png");
}
半径を r とし、高さの半分を格納しました。(radius の r です)
こうすることで、中心座標は、( r, r ) となります。
これまでの strokeRect は閉じられていたのでそのままでも描画できましたが、今回の arc は閉じられておらず、線分の指定だけですので描画まではしてくれません。
そこで、stroke()により、それまで線分を描画します。

結果は、こんな感じです。

円弧は描けましたね。
この円弧を使って、バッテリー残量を表現することを考えていきましょう。


このサンプルのダウンロード ー> basic10_1.zip

円を描く

では、円弧を使ったバッテリー残量の表現を考えます。
虹のようなイメージで、円弧だけで表現しようとすると、横幅を取りますし、残量が少なくなった時に見にくくなってしまいます。
ここは単純に考えて、円周をバッテリー残量の0〜100%とする方がスマートかと思います。

そこで、100%となる円を描いてみましょう。
円は、円弧が1周つながった状態ですので、開始角0°で終了角360°の円弧と考えます。
そう考えると、以下のような記述で、円を描くことができます。
ctx.arc(中心のX座標, 中心のY座標, 半径, 0, 2π, false); 角度の単位はラジアンですので、360°は2πとなります。
角度をプラスの数値にしたので、描画方向は角度の正方向と同じ false にしておきました。

では、円を描いてみましょう。
プログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {

 var canvas = document.createElement("canvas");
 canvas.height = height ;
 canvas.width = height;
 var ctx = canvas.getContext("2d");

 var c = height/2;
 var r = Math.round( c * 0.8 );
 ctx.strokeStyle="orange";
 ctx.arc(c, c, r, 0, 2*Math.PI);
 ctx.stroke();

 return canvas.toDataURL("image/png");
}
中心座標となる高さの半分を c としました。(center の c です)
半径 r は、少し小さくするために、8割にしました。念のため、数値を丸めてあります。
ここで注目して欲しいのは、描画方向の false がなくなっていることです。
描画方向は省略可能で、省略した場合、無条件で false となりますので、それを紹介したいがために、角度正方向プラスの向きで進めております。

結果は、こんな感じです。

円になりました。


このサンプルのダウンロード ー> basic10_2.zip

バッテリー残量を反映させる

円弧の描画ができるようになりましたので、バッテリー残量を反映させて行きましょう。

バッテリー残量を反映させてみる

これまでの四角形では、最大幅を残量100%としました。
今回は円弧ですので、1周を100%とします。

バッテリー残量との組み合わせプログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {

 var canvas = document.createElement("canvas");
 canvas.height = height ;
 canvas.width = height;
 var ctx = canvas.getContext("2d");

 var c = height/2;
 var r = Math.round( c * 0.8 );
 var a = Math.round( 2*Math.PI * percent / 100 );
 ctx.strokeStyle="orange";
 ctx.arc(c, c, r, 0, a);
 ctx.stroke();

 return canvas.toDataURL("image/png");
}
反映させる計算はこれまでと同じで、残量(%)を掛けるだけです。
計算結果を変数 a に格納しました。(angle の a です)
そのため、円弧描画の記述がシンプルになっています。

結果は、こんな感じです。

おやっ?と一瞬思いましたが、円弧にバッテリー残量が反映されています。
X軸から下向きに描画するので、これで良いのです。
ここから、いろいろ変化させましょう。


このサンプルのダウンロード ー> basic10_3.zip

90°回転させる

X軸スタートでは、一瞬おやっ?と思ってしまったので、受け入れ難い形なのでしょう。
そこで、反時計回りに90°回転させてみましょう。
そうすることで、馴染みのある「時計」と同じY軸スタートになります。
しかも、円弧描画方向を省略すると、時計と同じ回転方向になりますので、ちょうど良いですね。

反時計回りに90°回転させるには、開始角にも終了角にも、それぞれ-90°加えるだけで済みます。

90°回転させたプログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {

 var canvas = document.createElement("canvas");
 canvas.height = height ;
 canvas.width = height;
 var ctx = canvas.getContext("2d");

 var c = height/2;
 var r = Math.round( c * 0.8 );
 var start = Math.round( 0 - 0.5*Math.PI);
 var end = Math.round( 2*Math.PI * percent / 100 - 0.5*Math.PI);
 ctx.strokeStyle="orange";
 ctx.arc(c, c, r, start, end);
 ctx.stroke();

 return canvas.toDataURL("image/png");
}
単純に、0.5π引いただけなのですが、開始角も終了角も変数を用意しました。
それぞれ、start と end です。

結果は、こんな感じです。

お〜
良いではないですか!!

バッテリー残量というだけあって、基本的にカウントダウンであることは良いのですが、時計の向きにカウントダウンする方がしっくりくると思います。


このサンプルのダウンロード ー> basic10_4.zip

描画方向を変える

時計の向きにカウントダウンさせたいので、描画方向を逆にしましょう。

描画方向を逆にしたプログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {

 var canvas = document.createElement("canvas");
 canvas.height = height ;
 canvas.width = height;
 var ctx = canvas.getContext("2d");

 var c = height/2;
 var r = Math.round( c * 0.8 );
 var start = Math.round( 0 - 0.5*Math.PI);
 var end = Math.round( -2*Math.PI * percent / 100 - 0.5*Math.PI);
 ctx.strokeStyle="orange";
 ctx.arc(c, c, r, start, end, true);
 ctx.stroke();

 return canvas.toDataURL("image/png");
}
描画方向を逆にすると、角度の指定も考えなければなりません。
向きが反対になりますので、これまで2πだった1周が -2πとなります。

結果は、こんな感じです。

やっとイメージ通りの表現ができました。


このサンプルのダウンロード ー> basic10_5.zip

仕上げる

では、仕上げていきましょう。

線幅を広げる

動作はイメージ通りなのですが、線が細すぎて、よく見ないと分かりません。
仕上げの手始めに、線幅を広げましょう。

線幅を広げたプログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {

 var canvas = document.createElement("canvas");
 canvas.height = height ;
 canvas.width = height;
 var ctx = canvas.getContext("2d");

 var c = height/2;
 var r = Math.round( c * 0.8 );
 var start = Math.round( 0 - 0.5*Math.PI);
 var end = Math.round( -2*Math.PI * percent / 100 - 0.5*Math.PI);
 ctx.strokeStyle="orange";
 ctx.lineWidth = Math.round( height / 20 );
 ctx.arc(c, c, r, start, end, true);
 ctx.stroke();

 return canvas.toDataURL("image/png");
}
lineWidth で線幅を指定できますが、デバイス毎の差をなくすため、割合で指定しました。
確認デバイスはiPhone6Plusですので、高さを20で割った「3」で試しました。

結果は、こんな感じです。

見やすくなりました。


このサンプルのダウンロード ー> basic10_6.zip

可変色を適用させる

次は、可変色を適用させましょう。
メインカラーをバッテリー残量に割り当てるのは定番なのですが、ベースカラーの出番がありません。
そこで、ベースカラーにも出番を用意することにします。

しばらく考えたのですが、描画した形が形なので、ベースカラーに用意する舞台は円しかありませんね。

可変色適用プログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {

 var canvas = document.createElement("canvas");
 canvas.height = height ;
 canvas.width = height;
 var ctx = canvas.getContext("2d");

 var c = height/2;
 var r = Math.round( c * 0.8 );
 ctx.strokeStyle="rgba(" + b_color.join()+ ")";
 ctx.arc(c, c, r, 0, 2*Math.PI);
 ctx.stroke();

 var start = Math.round( 0 - 0.5*Math.PI);
 var end = Math.round( -2*Math.PI * percent / 100 - 0.5*Math.PI);

 ctx.beginPath();
 ctx.strokeStyle="rgb(" + m_color.join()+ ")";
 ctx.lineWidth = Math.round( height / 20 );
 ctx.arc(c, c, r, start, end, true);
 ctx.stroke();

 return canvas.toDataURL("image/png");
}
1つ目の円弧は、1周描画しているので、円になります。
スタイルは、透過色まで含めたベースカラーです。
2つ目の円弧が、バッテリー残量を反映させた円弧ですので、メインカラーを指定しています。
今回のポイントなのですが、1つ目の円弧を描画したため、2つ目の円弧を描画するために、線分をリセットしなければならないということです。
そのために、beginPath()を使い、2つ目の円弧を新たに書き始めているというわけです。

結果は、こんな感じです。

ベースカラーも太くして良さそうですね。

このサンプルのダウンロード ー> basic10_7.zip

テキストも表示させる

ここまでやっておいて何なのですが、円の内側が無駄に思えてきましたので、テキストも表示させちゃいましょう。
このテキストには、ベースカラーを割り当てます。

テキストも表示させるプログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {

 var canvas = document.createElement("canvas");
 canvas.height = height ;
 canvas.width = height;
 var ctx = canvas.getContext("2d");

 var c = height/2;
 var r = Math.round( c * 0.8 );
 ctx.strokeStyle="rgba(" + b_color.join()+ ")";
 ctx.lineWidth = Math.round( height / 20 );
 ctx.arc(c, c, r, 0, 2*Math.PI);
 ctx.stroke();

 var start = Math.round( 0 - 0.5*Math.PI);
 var end = Math.round( -2*Math.PI * percent / 100 - 0.5*Math.PI);

 ctx.beginPath();
 ctx.strokeStyle="rgb(" + m_color.join()+ ")";
 ctx.arc(c, c, r, start, end, true);
 ctx.stroke();

 var fs = Math.round( height * 0.5 );
 ctx.font = fs + "px Helvetica Neue";
 ctx.textAlign = "center";
 ctx.textBaseline="middle";
 ctx.fillStyle="rgb(" + b_color.slice(0,3).join()+ ")";
 ctx.fillText(percent, c, c);

 return canvas.toDataURL("image/png");
}
テキストのベースカラーは、透過率を省いています。
lineWidthの場所を変更し、ベース円の線幅も太くしてみました。
テキストのフォントサイズに変数を用意しました。
スッポリ入る程度として、高さの半分をフォントサイズとしました。

結果は、こんな感じです。

こうやってみてみると、ベース円の線幅は細くても良さそうですね。
逆に、メインカラーで描画されるバッテリー残量の円弧の線幅を、もう少し太くしても良いかもしれません。


このサンプルのダウンロード ー> basic10_8.zip

バグを取る

さぁ、やってまいりました。バグ取りのお時間です。
しばらく使っていましたが、不都合がありました。

Lithium Ion のテーマを作るようになって、スクリーンショットを撮る機会が多くなりました。
おかげで、満充電することがほとんどなくなりました。
ある時、充電したまま子供と遊んだり、庭の草取りなどしていたら、満充電になっていました。
そこで、気づいたのですが、以下のような表示になっていたのです。

満充電の時、テキストの文字数が増えるため、円弧に被ってしまったのですね。
逆に目立たせるために、円弧を思いっきり飛び出す「100」でも面白いのですが、シンプルさを優先するために、円弧内に収める方向で考えました。

バグ取り後のプログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {

 var canvas = document.createElement("canvas");
 canvas.height = height ;
 canvas.width = height;
 var ctx = canvas.getContext("2d");

 var c = height/2;
 var r = Math.round( c * 0.8 );
 ctx.strokeStyle="rgba(" + b_color.join()+ ")";
 ctx.lineWidth = Math.round( height / 20 );
 ctx.arc(c, c, r, 0, 2*Math.PI);
 ctx.stroke();

 var start = Math.round( 0 - 0.5*Math.PI);
 var end = Math.round( -2*Math.PI * percent / 100 - 0.5*Math.PI);

 ctx.beginPath();
 ctx.strokeStyle="rgb(" + m_color.join()+ ")";
 ctx.arc(c, c, r, start, end, true);
 ctx.stroke();

 var fs = Math.round( height * 0.5 );
 100==percent&&( fs = Math.round( fs * 0.85 ) );
 ctx.font = fs + "px Helvetica Neue";
 ctx.textAlign = "center";
 ctx.textBaseline="middle";
 ctx.fillStyle="rgb(" + b_color.slice(0,3).join()+ ")";
 ctx.fillText(percent, c, c);

 return canvas.toDataURL("image/png");
}
フォントサイズを指定し、満充電の場合のみフォントサイズを書き換えるようにしています。
if 文で書こうと思ったのですが、分岐編で Lithium Ion 用の書き方を紹介したので、いきなりこの書き方で書きました。
満充電の場合は、そこからさらに15%小さくしています。


結果は、こんな感じです。

円の中に収まりました。


このサンプルのダウンロード ー> basic10_9.zip

バグを取る パート2

さぁ、やってまいりました。バグ取りパート2のお時間です。
今回のパート2は、違うバグではなく、同じバグを違う方法で取ります。

フォントサイズを小さくするのは、対処にはなりますが十分ではありません。
そもそも、もう少し大きくしたいと思っていたフォントサイズをなので、それを小さくしてまで表示させなければならないことに対し、私は納得できませんでした。
しかし、放っておくこともできません。
フォントサイズをそのままに、3文字になって幅が広くなったテキストを、如何に表示させるか考えましたが、以下の2通りしかありません。
・テキストの幅を狭くする
・円弧の幅を広くする


ここまでやってきて正円を崩すのは、方向性が変わってしまうので、テキストの幅を狭くすることで、フォントサイズをそのままに正円に収めることにしましょう。
幅を狭くするには、以下のような記述で実現できます。
コンテキスト.scale( 横方向の拡大率, 縦方向の拡大率 ) 幅を狭くするので、横方向の拡大率を1未満にすることによって、文字を潰すことができます。
ただ、実際に拡大(縮小)されるのは、文字ではなくコンテキストです。
さらに、左上端を基準に拡大(縮小)されるので、単に拡大(縮小)するだけでは位置もズレてしまいます。
中央表示を保たなければなりませんので、ズレた分だけ移動させなければなりません。
移動させるには、以下のような記述で実現できます。
コンテキスト.translate( 右方向への移動量, 下方向への移動量 ) 今回は、横方向のみのズレなので、縦方向の移動量は0です。

テキストの幅を狭くしたプログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {

 var canvas = document.createElement("canvas");
 canvas.height = height ;
 canvas.width = height;
 var ctx = canvas.getContext("2d");

 var c = height/2;
 var r = Math.round( c * 0.8 );
 ctx.strokeStyle="rgba(" + b_color.join()+ ")";
 ctx.lineWidth = Math.round( height / 20 );
 ctx.arc(c, c, r, 0, 2*Math.PI);
 ctx.stroke();

 var start = Math.round( 0 - 0.5*Math.PI);
 var end = Math.round( -2*Math.PI * percent / 100 - 0.5*Math.PI);

 ctx.beginPath();
 ctx.strokeStyle="rgb(" + m_color.join()+ ")";
 ctx.arc(c, c, r, start, end, true);
 ctx.stroke();

 var fs = Math.round( height * 0.5 );
 100==percent&&( ctx.scale( 0.8, 1 ), ctx.translate(height*0.1, 0) );
 ctx.font = fs + "px Helvetica Neue";
 ctx.textAlign = "center";
 ctx.textBaseline="middle";
 ctx.fillStyle="rgb(" + b_color.slice(0,3).join()+ ")";
 ctx.fillText(percent, c, c);

 return canvas.toDataURL("image/png");
}
scaleで80%の大きさにし、translateで10%移動させています。
少数を含む数値の計算ですが、移動量に関しては省きました。
割合の基準は幅なのですが、ステータスバーの高さをキャンバスの幅としているため、高さ基準でも同じです。
今回のバグ取りで、拡大と移動をまとめて記述していますが、Lithium Ion 用の書き方では、コンマ(,)で区切って並べます
セミコロン(;)ではありません。

結果は、こんな感じです。

フォントの大きさを変えずに、円の中に収まりました。
Lithium Ion の設定で指定してある満充電時の色で表示されていますね。
何となく、文字が縦長になったのがお分かりいただけますでしょうか?


このサンプルのダウンロード ー> basic10_10.zip



当サイトの更新状況を、アラートで表示するかどうかの設定をします。


保存する

その機能で月額1500円もするのですか??
その機能なら年額1500円で手に入りますよ!

当サイトもこちらのレンタルサーバーを利用しています。

Copyright (C) 2007 Bokechans.net All Rights Reserved.