- トップ
- iOS10
- Lithium Ion のテーマを作る
- テーマの基本 その11(円弧描画編)
テーマの基本 その11
(線分描画編)
円弧を描画しましたので、次は線分を描画してみましょう。
順番としては、円弧より線分の方が先のように思えますが、考え方を理解することと、体験することを比べると、体験してみることを優先しました。
ここでは、線分を取り扱うことで、パスの考え方を理解しましょう。
線分を描画する
線分を描画することで、パスに関しても理解しましょう。
パスを知る
テーマの基本その2で、儀式のようなものとして紹介した「コンテキスト」ですが、キャンバスの描画機能を有効にすると説明しました。
描画機能が利用できるのですが、取得したコンテキスト上で図形を作成します。
作成した図形を実際に表示するのは、実は stroke() だったりするので、コンテキスト上で図形を作成するのは、頭の中に描いているような感じです。
そこで、パスの概念が必要になるのです。
パスとは、「path」と書きますが、簡単に言うと「道」です。
通路、進路、経路、方針、・・・などいろいろ意味がありますが、はっきり訳す必要はありません。
「道」ですので、まっすぐな道もあれば、曲がった道もあるかと思います。
ただ、その道は1つにつながっている必要があります。
道の両端が閉じていてもいなくても構いませんが、「道は1本」と覚えておいてください。
道は1本でも、まっすぐな区間もあれば、曲がっている区間もあるかと思います。
区間はいくつあっても構いませんが、とにかく「道は1本」です。
パスは0個以上のサブパスで構成され、この「サブパス」が、区間に相当します。
以下のようなイメージです。
閉じているか閉じていないかは、見ての通りです。
それぞれサブパスで1つのパスが構成されています。
閉じたパスの方には、円弧も描いてみました。
パスをリセットする
線分でも円弧でも、サブパスを追加してパスを作り上げていきます。
完成したパスは、stroke() で輪郭表示されますが、次に違う図形を作るためには、それまでのパスをリセットしなければなりません。
もし、リセットするのを忘れると、サブパスがどんどん追加されるので、思ってもいない図形になったりします。
何か図形を描く時は、その度にパスをリセットするようにしましょう。
パスをリセットするには、以下のように書きます。
ctx.beginPath();
これで、サブパスが0個になります。
これまで、四角や円弧を描いてきましたが、パスのリセットという考え方は紹介していませんでした。
それは、1つのパスしか描いていなかったからです。
前回の円弧表示の仕上げで、2つ目の円弧を描くときにやっと登場しました。
描くパスが1つしかなくても、パスをリセットしてから描き始めるようにした方が良いですね。
パスの開始位置を指定する
パスをリセットしたところで、線分を描く準備をしましょう。
線分を描くためには、「どこから」と「どこまで」を指定しなければなりません。
「どこから」は、以下の記述で指定します。
ctx.moveTo(x, y);
座標 (x,y) を指定するのですが、座標の基準はキャンバスの左上です。
線を引く
「どこから」を指定したので、「どこまで」を指定して線を引きましょう。
現在位置から線を引くには、以下のように書きます。
ctx.lineTo(x, y);
これで、直線のサブパスができました。
すでにサブパスがある場合、直前のサブパスにつながる直線となります。
続けて線を引けば、どんどん直線がつながっていくというわけです。
パスを閉じる
パスは閉じていても、閉じていなくても構いませんが、パスを閉じる簡単な方法があります。
スタートの座標を指定して線を引けば良いのですが、ある呪文を唱えると、直線で簡単にパスを閉じることができます。
パスを直線で簡単に閉じるには、以下のように書きます。
ctx.closePath();
これで、パスが閉じられました。
何らかの図形が出来上がっていることでしょう。
線を表示する
パスが出来上がったら、表示をしましょう。
パスの輪郭を表示するには、以下のように書きます。
ctx.stroke();
これで、作ったパスが表示されます。
円弧で初めて出てきましたが、円弧もサブパスなので、輪郭表示が必要だったのです。
直線で四角を描いてみる
では、基本その8で描いた四角を直線で描いてみましょう。
スタート位置を指定し、線を引きます。
四角を直線で描いたプログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {
var canvas = document.createElement("canvas");
canvas.height = height / 2;
canvas.width = height;
var ctx = canvas.getContext("2d");
ctx.strokeStyle="#000000";
// ctx.beginPath();
ctx.moveTo( 0, 0 );
ctx.lineTo( 0, height / 2 );
ctx.lineTo( height, height / 2 );
ctx.lineTo( height, 0 );
ctx.closePath();
ctx.stroke();
return canvas.toDataURL("image/png");
}
キャンバスの高さを半分にしました。
// の行はコメント扱いになりますので、beginPath() は実行されません。
スタート位置を原点(左上端)とし、左下→右下→右上の順に線を引き、closePath() でパスを閉じています。
直線で閉じられるので、一石二鳥ですね。
描かれたパスを stroke() で輪郭表示します。
結果は、こんな感じです。
まぁ、こんなもんでしょう。
ここから、いろいろ変化させましょう。
このサンプルのダウンロード ー> basic11_1.zip
バッテリー残量を反映させる
線で四角を描画けるようになりましたので、一気にバッテリー残量を反映させましょう。
バッテリー残量を反映させてみる
基本その8でやった仕上げの1つ目を再現します。
線幅と隙間を広げ、隙間に変数を用意したものです。
バッテリー残量を反映させたプログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {
var canvas = document.createElement("canvas");
canvas.height = height / 2;
canvas.width = height;
var ctx = canvas.getContext("2d");
ctx.strokeStyle="#000000";
// ctx.beginPath();
ctx.lineWidth = 3;
ctx.moveTo( 0, 0 );
ctx.lineTo( 0, height / 2 );
ctx.lineTo( height, height / 2 );
ctx.lineTo( height, 0 );
ctx.closePath();
ctx.stroke();
var s = 4;
var rectWidth = Math.round((height-s*2) * percent / 100);
ctx.fillStyle="orange";
ctx.beginPath();
ctx.lineWidth = 1;
ctx.moveTo( s, s );
ctx.lineTo( s, height/2-s );
ctx.lineTo( s+rectWidth, height/2-s );
ctx.lineTo( s+rectWidth, s );
ctx.closePath();
ctx.fill();
return canvas.toDataURL("image/png");
}
隙間の変数sを使って (s, s) をスタート位置とし、左下→右下→右上の順に線を引き、closePath() でパスを閉じています。
バッテリー残量は塗りつぶしますので、スタイルは fillStyle で指定し、fill で塗りつぶします。
すでに作ったプログラムを参考にしてありますので、いきなり完成形のように見えますが、strokuRect() や fillRect() の部分が書き換えられているだけです。
(バッテリー残量は、バグ取り後のやり方です)
結果は、こんな感じです。
仕上げの1つ目を再現していますので、隙間も再現されていますね。
このサンプルのダウンロード ー> basic11_2.zip
可変色を適用させる
基本その8でやった仕上げの2つ目を再現します。
何気に隙間を狭くし、可変色を適用させます。
バッテリー残量を反映させたプログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {
var canvas = document.createElement("canvas");
canvas.height = height / 2;
canvas.width = height;
var ctx = canvas.getContext("2d");
ctx.strokeStyle="rgb(" + b_color.slice(0, 3).join()+ ")";
// ctx.beginPath();
ctx.lineWidth = 3;
ctx.moveTo( 0, 0 );
ctx.lineTo( 0, height / 2 );
ctx.lineTo( height, height / 2 );
ctx.lineTo( height, 0 );
ctx.closePath();
ctx.stroke();
var s = 3;
var rectWidth = Math.round((height-s*2) * percent / 100);
ctx.fillStyle="rgb(" + m_color.join()+ ")";
ctx.beginPath();
ctx.lineWidth = 1;
ctx.moveTo( s, s );
ctx.lineTo( s, height/2-s );
ctx.lineTo( s+rectWidth, height/2-s );
ctx.lineTo( s+rectWidth, s );
ctx.closePath();
ctx.fill();
return canvas.toDataURL("image/png");
}
可変色の適用は、いつものやり方です。
隙間も3にしました。
こちらも、基本的には四角の描き方を書き換えただけです。
(バッテリー残量は、バグ取り後のやり方です)
結果は、こんな感じです。
このサンプルのダウンロード ー> basic11_3.zip
仕上げる
では、仕上げていきましょう。
ここで問題です。
これから一体何を仕上げようというのでしょう?
1:見た目を整える
2:バグを取る
3:プログラムをスマートにする
見た目は、これ以上何もいぢるつもりはありません。
バグ取りは、すでに対策したプログラムを参考にしたので、必要ありません。
というわけで、答えは「3」です。
プログラムをスマートにするとは、どういうことでしょう。
プログラムをもう一度見てみると、似たような部分が目立ちます。
一行で書いていた四角を数行で書いているので、余計に目立つのでしょう。
では、似たような部分をまとめて仕上げましょう。
似たような部分を確認する
似たような部分とは、どこのことなのでしょうか。
今回は、四角を描く部分なのですが、見て分かるように、似ている部分を色つけしました。
(height, percent, charge, low, lpm, m_color, b_color) {
var canvas = document.createElement("canvas");
canvas.height = height / 2;
canvas.width = height;
var ctx = canvas.getContext("2d");
ctx.strokeStyle="rgb(" + b_color.slice(0, 3).join()+ ")";
// ctx.beginPath();
ctx.lineWidth = 3;
ctx.moveTo( 0, 0 );
ctx.lineTo( 0, height / 2 );
ctx.lineTo( height, height / 2 );
ctx.lineTo( height, 0 );
ctx.closePath();
ctx.stroke();
var s = 3;
var rectWidth = Math.round((height-s*2) * percent / 100);
ctx.fillStyle="rgb(" + m_color.join()+ ")";
ctx.beginPath();
ctx.lineWidth = 1;
ctx.moveTo( s, s );
ctx.lineTo( s, height/2-s );
ctx.lineTo( s+rectWidth, height/2-s );
ctx.lineTo( s+rectWidth, s );
ctx.closePath();
ctx.fill();
return canvas.toDataURL("image/png");
}
描く対象が違うので、スタイルの指定も違いますし、指定する座標も違います。
ただ、座標が違うだけでやってることは同じですので、似たような部分となります。
関数を知る
では、似たような部分をまとめるために、関数を使います。
関数と言っても、数学的な「戻り値のある関数」ではなく、戻り値のないやっつけ仕事的な関数をここでは作ります。
やっつけ仕事というのも言い過ぎかもしれませんが、やらせたい処理をまとめておいて、必要な時に呼び出して仕事させるための関数です。
関数は以下のように記述します。
function 関数名() {
・
・
いろいろな処理
・
・
}
基本その2で、テーマの雛形を作りましたが、そもそもLithium Ion のテーマが関数なのがお分かりいただけると思います。
ここで作る関数は、ホントに勝手に作る関数ですので、何の制約もありません。
名前も勝手に付けてOKです。
次に、関数名の後ろにあるカッコ()です。
()内には、引数を用意します。
基本その2からずっと、height や percent などを使ってきました。
これは、すでに取得できているという前提でプログラミングをしてきたのですが、()内に記述することで値を受け渡しすることができます。
関数を呼び出す時に、関数内で使いたい(使って欲しい)ものを用意し、関数の中でそれを使うということができます。
今回の目的である「四角の描画」では、座標を引数にすれば良いことになります。
関数の呼び出しや、引数の受け渡しは、以下のように記述します。
(height, percent, charge, low, lpm, m_color, b_color) {
function 関数名(引数1用変数,引数2用変数,・・・) {
・
・
引数用変数を使ったいろいろな処理
・
・
}
・
・
関数名(引数1,引数2,・・・)
・
・
}
関数部分を赤くしておきました。
関数は「定義」ですので、サンプルでは最初に定義しておきましたが、実際には、どこで定義しても構いません。
呼び出し側は、関数名で呼び出します。
その時、必要な引数を()内に並べておきます。
関数側では、引数を受け取る変数を用意し、受け取った変数名で、その後の処理を進めます。
「入れ子状態・・・」というのが私の印象です。
関数を作る
では、関数を作っていきましょう。
先ずは、関数の名前を決めます。
できるだけ、1年後に自分が見て分かる名前にしましょう。
今回は、四角を描く関数を作るので、「drawRect」と命名することにしました。
四角を描くんだなぁということが分かる名前なら何でも良いです。
ちなみに、strokeRect は本家で使用済みですので、避けました。
次に、引数を決めます。
前述の通り、四角の座標を引数にします。
4つの点の座標を用意しようかと思いましたが、左上の座標と右下の座標で十分ですので、それぞれのXとYで4つの引数を必要とします。
それと、線幅も関数内で指定しますので、引数としておきます。
関数外で定義された変数は、そのまま関数内でも使えるため、コンテキストは引数として渡さなくても大丈夫です。
これを「グローバル変数」と呼びますが、詳しくはググってください。
作った関数は、以下の通りです。
function ( left, top, right, bottom, lineWidth ) {
ctx.beginPath();
ctx.lineWidth = lineWidth;
ctx.moveTo( left, top );
ctx.lineTo( left, bottom );
ctx.lineTo( right, bottom );
ctx.lineTo( right, top );
ctx.closePath();
}
受け取った引数を使って、四角を描いていることがお分かりいただけるかと思います。
プログラムをスリム化
では、実際に関数を呼び出して、プログラムをスリムにしましょう。
関数を使ったプログラムは、以下の通りです。
(height, percent, charge, low, lpm, m_color, b_color) {
function drawRect( left, top, right, bottom, line_width ) {
ctx.beginPath();
ctx.lineWidth = line_width;
ctx.moveTo( left, top );
ctx.lineTo( left, bottom );
ctx.lineTo( right, bottom );
ctx.lineTo( right, top );
ctx.closePath();
}
var canvas = document.createElement("canvas");
canvas.height = height / 2;
canvas.width = height;
var ctx = canvas.getContext("2d");
ctx.strokeStyle="rgb(" + b_color.slice(0, 3).join()+ ")";
drawRect( 0, 0, height, height/2, 3 ) ;
ctx.stroke();
var s = 3;
var rectWidth = Math.round((height-s*2) * percent / 100);
ctx.fillStyle="rgb(" + m_color.join()+ ")";
drawRect( s, s, s+rectWidth, height/2-s, 1 ) ;
ctx.fill();
return canvas.toDataURL("image/png");
}
似たような部分が関数にまとめられ、スリムになったと思いますが、いかがでしょうか。
このサンプルのダウンロード ー> basic11_4.zip
当サイトの更新状況を、アラートで表示するかどうかの設定をします。
保存する