2019年9月25日水曜日

Canvas の幅・高さと style 指定

 参考図として図を描き、その書き方(記述例)などを、少々説明したいと思います。

 ✥ ここで用意した参考図の最初のタグでは、
 <canvas id="moon_model"></canvas>
と、記述しています。タグでは Canvas の幅と高さを指定していません。指定しなければ、初期値が使われます。
⇒ 初期値は、
 width="300" height="150"
です。
 ● もしもスタイルシートで幅 (width)・高さ (height) の値を変更すると、見た目の座標そのもの(ピクセル間隔)の基準値が変わってしまいます。
 ◈ 高さか幅、どちらか片方の値をスタイルシートで指定した場合には、縦横の比率を保ったまま、拡大・縮小されるのですが、両方の値を指定すると、x 軸と y 軸で拡大・縮小率が異なることにもなります。
 ⇒ それに応じて Canvas 全体が拡大・縮小されるので、そうなると、円を描いたとき楕円に見え、弧や半円、すべての図形がその比率に応じた形になるのです。
 これを技として、幅か高さのどちらかだけを指定することで、図の全体を拡大したり、縮小したりできるのですけれど、調子に乗ると、拡大でラインの境界が荒くなって、図全体が荒れた感じになることがあります。
#moon_model {
  margin-left: 20px;
  width:  px;
  height:  px;
  background-color: darkcyan;
}
 ◆ この見本図では変更操作が可能なように、プログラムの記述で、スタイルを指定しています。
var cvs = document.getElementById('moon_model');
var pen = cvs.getContext('2d');
var x = cvs.width/2 + 3;
var y = 125;
var z = 10;
var theta = 75*Math.PI/180;
var r = 80;
var px = r*Math.cos(Math.PI/3);
var py = -r*Math.sin(Math.PI/3);
var w = document.getElementById('cvsW').selectedIndex + 150;
var h = document.getElementById('cvsH').selectedIndex + 150;
  cvs.style.width = w + 'px';
  cvs.style.height = h + 'px';
  // Canvas 全体の画像を消去してから ペン軸の座標基準点を移動させる
  pen.clearRect(0, 0, cvs.width, cvs.height);
  pen.translate(x, y);
―― この冒頭、
  cvs.style.width = w + 'px';
  cvs.style.height = h + 'px';
の部分で、スタイルの再指定を実行しているのです。
 ◈ 同じ画像を再描写しているだけなのですけれども、その都度 Canvas 全体の画像を消してから描画しているのは、このように重ねて描画し続けると描線が荒れていくので、それを避ける目的があります。
  // ペン軸の座標基準点を移動させる
  pen.translate(x, y);
というのは、本来 Canvas の左上が、x 軸と y 軸の基準点 (0, 0) となっているので、作図の計算に都合のいい座標を原点 (0, 0) にしてしまう方法なのです。
 ● これを実行した際には、最後に必ず、ペン軸の座標をもとの位置に戻しておきましょう。
  // ペン軸をリセットする
  pen.translate(-x, -y);

 ◈ ちなみに、移動させた (x, y) 座標から、ペン軸の基準点をリセットする前に、―― つまりペン軸の基準を移動させたままの状態で ―― Canvas 全体の画像を消すには、次のように記述します。
  pen.clearRect(-x, -y, cvs.width, cvs.height);

 ◈ 今回の作図で、最も計算が難しかったのが、直角の記号を描くラインでした。
var z = 10;
var theta = 75*Math.PI/180;
  // 直角の記号
  pen.strokeStyle = 'green';
  pen.lineWidth = 1;
  pen.beginPath();
  pen.moveTo(-px + z*Math.cos(Math.PI/6), py + z*Math.sin(Math.PI/6));
  pen.lineTo(-px + z*Math.sqrt(2)*Math.cos(theta), py + z*Math.sqrt(2)*Math.sin(theta));
  pen.lineTo(-px - z*Math.cos(Math.PI/3), py + z*Math.sin(Math.PI/3));
  pen.stroke();

 記号用のラインの長さを z = 10 (px) として、斜めになった 10px の、x 座標と y 座標を求めています。
 ► theta = 75 * Math.PI / 180 というのは、直角三角形の斜めの角度 30° に、直角二等辺三角形の角度 45° を足した 75° のラジアン単位です。
  pen.lineTo(-px + z*Math.sqrt(2)*Math.cos(theta), py + z*Math.sqrt(2)*Math.sin(theta));
 ► 75° に対して、直角二等辺三角形の辺の比率 (1 : 1 : √2) の長さを基準に座標計算しています。
 ◈ z * Math.sqrt(2) が、10 × √ 2  の、計算になります。
  こういう複雑な計算が苦手なので、問題の頂点の座標へと、もう一度ペン軸の原点 (0, 0) を移動させてから、さらにペン軸の座標方向そのものを回転させてしまうという必殺技を多用しています。
 ⛞ ヒポクラテスの月の作図例 ⛞ (「古代の《暦》」のページ版)で使っている手法が、それなのです。
 ◆ その記述方法の説明に代え、例として主要な部分だけを抜粋しておきましょう。
var x = 370;
var y = 250;
var z = 16;
var r = 150;
var m1 = r * Math.cos(Math.PI/6);
  // ペン軸の座標基準点を移動させ 反時計回りに 150° 回転
  pen.translate(x, y);
  pen.rotate(-Math.PI/6*5);
  // 直角の記号を描き加える
  pen.strokeStyle = 'darkgoldenrod';
  pen.lineWidth = 0.5;
  pen.beginPath();
  pen.moveTo(m1*2, -z);
  pen.lineTo(m1*2-z, -z);
  pen.lineTo(m1*2-z, 0);
  pen.stroke();
  // ペン軸の座標をリセットして タイトルの記入
  pen.rotate(Math.PI/6*5);
  pen.translate(-x, -y);
  pen.fillStyle = 'darkgoldenrod';
  pen.font = '17px sans-serif';
  pen.fillText('ヒポクラテスの月', 20, 30);
 ► ペン軸の基準をリセットする際には、変更した逆向きの順番で戻していかなければなりません。
 ● ちゃんと、問題なく、ペン座標がもとに戻っているかの確認用という意味も含めて、タイトルを一番最後に記述するようにしています。
 そこにあるべきタイトルがもし書かれてない場合に、どこか途中でプログラムが停止していると知ることのできる、わかりやすい信号にもなっていますし。
  ⛞ ヒポクラテスの月の作図例 ⛞ (「古代の《暦》」のページ版)
へのリンク
 このリンクの場所にも、上と同じ(ただし大きさの変わらない)参考図がありますが、同じページの末尾近く〈ヒポクラテスの月 の 構図〉に、この参考図のための説明と、見本図およびプログラムの本文を掲載しています。

 ◆ JavaScript での書式(具体的な書き方)などは、リンクしたページで、実際のスクリプトを参照してみてください。

―― 前回分と合わせ、コピペしてそのまま使える、JavaScript の見本をテキスト化して掲載したページを、すでに説明したところの、以下のサイトで公開しています。

Canvas に描くヒポクラテスの月
http://theendoftakechan.web.fc2.com/eII/hitsuge/arcus/Hippokrates.html

0 件のコメント:

コメントを投稿