1. ホーム
  2. Web制作
  3. html5

canvas 学習のまとめ III - パスの描画 - ラインセグメント

2022-01-31 03:57:53

Canvasの描画環境には、即時描画方式のものと、パスを使った描画方式があります。

即時描画メソッドはstrokeRect(), fillRect()の2つだけで、strokezText(), fillText()も即時描画メソッドですが、テキストは描画とみなされません。

パスベースの描画方式

SVG(Scalable Verctor Graphics)、Adobe Illustratorなど、ほとんどの描画システムはパスベースである。

これらの描画システムを使用する場合、パスを定義してからストロークやフィルを行う必要がありますが、ストロークやフィルを行うことで描画が表示されるようになります。

Canvasの3種類の描画

<イグ

線分を描画する

Canvasの描画環境では、線分もリニアパスと呼ばれるパスを元に描画されます。リニアパスを作成するメソッドは moveTO() と lineTo() で、パスを作成した後に stroke() メソッドを呼び出すことで、Canvas に線分を描画することができます。

これは、先ほど説明したパスを使った描画方法で、描画や塗りつぶしが必要です。

通常、2つの点が線で結ばれているので、線分を描くのはとても簡単です。moveTO()で線の始点を指定し、lineTo()で別の点まで移動します。

function drawLine(){
    cxt.moveTo(50, 50);
    cxt.lineTo(100, 100);
}

しかし、この方法ではキャンバス内の線分を見ることができません。先ほど、ストロークやフィルを使用しなければならないパスベースの描画メソッドについて説明しましたが、このメソッドでは線分を見ることができません。そのため、結果を見るためにはstroke()メソッドも使用しなければなりません。

そこで、線分を描くために、このようなメソッドに変更します。

function drawLine(){
    cxt.moveTo(50, 50);
    cxt.lineTo(200, 200);
    cxt.stroke();
}

lineTo() だけを使って canvas に線を描くこともできるので、上のコードを次のように変更しても効果は同じです。

function drawLine(){
    cxt.lineTo(50, 50);
    cxt.lineTo(200, 200);
    cxt.stroke();
}

moveTo()とlineTo()の使い分けをまとめると

  • moveTo(x,y)。ストロークを指定された座標x,yに移動し、現在のパスにサブパスを追加します。
  • lineTo(x,y): 現在のパスにサブパスが存在しない場合、このメソッドは moveTo() と同様に動作する。現在のパスにサブパスがある場合、このメソッドは指定された点をサブパスに追加します。

線分のスタイルを変更する

線分の幅を変更する

function= 14;
    cxt.lineTo(50, 50);
    cxt.lineTo(200, 200);
    cxt.stroke();
}

線分の色を変更する

function drawLine(){
    cxt.lineWidth = 14;
    cxt.strokeStyle = 'green';
    cxt.lineTo(50, 50);
    cxt.lineTo(200, 200);
    cxt.stroke();
}

また、CanvasGradientオブジェクトやCanvasPatternオブジェクトを使って、線分にグラデーションの色やパターンを追加することもできます。

function drawLine(){
    cxt.lineWidth = 14;
    var gradient = cxt.createLinearGradient(0, 0, canvas.width/2, canvas.height/2);
    gradient.addColorStop(0, 'blue');
    gradient.addColorStop(0.5, 'purple');
    gradient.addColorStop(1, 'yellow');
    cxt.strokeStyle = gradient;
    cxt.lineTo(50, 50);
    cxt.lineTo(200, 200);
    cxt.stroke();
}

 beginPath()とclosePath()

上記の3つのcanvasでの描画方法から、2行目の円弧のパスがオープンパスで、最後の行の円弧がクローズドパスであることがわかります。では、閉じたパスはどのように実現されているのだろうか。

キャンバスのパス描画において、より重要な2つの方法について見てみましょう。

  • beginPath()。現在の子パスをすべてクリアし、現在のパスをリセットしてパスを再描画します。
  • closePath(): 開いているパスの一部を閉じる。グラフがすでに閉じられている場合、つまり、現在の点が始点である場合、この関数は何もしないので、必須ではない。

まずダッシュ線を引く

function drawLine(){
    cxt.strokeStyle = 'green';
    cxt.lineWidth = 2;
    cxt.moveTo(50, 50);
    cxt.lineTo(50, 150);
    cxt.lineTo(150, 150);
    cxt.stroke();
}

上の例のコードを修正して、beginPath()メソッドとclosePath()メソッドを追加します。

function drawLine(){
    //stroke triangle
    cxt.strokeStyle = 'green';
    cxt.lineWidth = 2;
    cxt.beginPath();
    cxt.moveTo(50, 50);
    cxt.lineTo(50, 150);
    cxt.stroke();
    cxt.beginPath();
    cxt.lineTo(150, 150);
    cxt.lineTo(150, 250);
    cxt.stroke();
  cxt.closePath();
}


キャンバスに2つのパスが描かれているのがわかると思います。

注意:beginPath()を呼び出した後、あるいはキャンバスを最初に構築したとき、最初のパス構築コマンドは通常moveTo()と見なされます。そのため、描画を行う際にはまずbeginPath()を使用する必要があります。

では、コードを修正してみましょう。

function drawLine(){
    //stroke triangle
    cxt.strokeStyle = 'green';
    cxt.lineWidth = 2;
    cxt.beginPath();
    cxt.moveTo(50, 50);
    cxt.lineTo(50, 150);
    cxt.lineTo(150, 150);
    cxt.closePath();
    cxt.stroke();
    //fold lines
    cxt.translate(150, 0);
    cxt.strokeStyle = 'red';
    cxt.lineWidth = 2;
    cxt.beginPath();
    cxt.moveTo(50, 50);
    cxt.lineTo(50, 150);
    cxt.lineTo(150, 150);
    cxt.stroke();
    cxt.closePath();
    //green filled triangle
    cxt.translate(150, 0);
    cxt.fillStyle = 'green';
    cxt.lineWidth = 2;
    cxt.beginPath();
    cxt.moveTo(50, 50);
    cxt.lineTo(50, 150);
    cxt.lineTo(150, 150);
    cxt.fill();
    cxt.closePath();
    //red filled triangle
    cxt.translate(150, 0);
    cxt.fillStyle = 'red';
    cxt.lineWidth = 2;
    cxt.beginPath();
    cxt.moveTo(50, 50);
    cxt.lineTo(50, 150);
    cxt.lineTo(150, 150);
    cxt.closePath();
    cxt.fill();
}

上記の例から、closePath()の位置が変化し、グラフィックに影響を与えることがわかります。

注:fill()関数を呼び出すと、閉じられていない図形はすべて自動的に閉じられるので、この時点ではclosePath()関数は必要ありません。

しかし、stroke()を呼び出します。stroke()メソッドの前にclosePath()だけを使用すると、閉じたパスを形成することになります。stroke()メソッドの後にclosePath()メソッドを呼び出すと、すでに図形が描かれており、現在描かれているパスは閉じているので、closePath()メソッドは機能しません。

線分と画素の境界線

まずは例題から

function drawLine(){
    //stroke triangle
    cxt.lineWidth = 1;
    cxt.beginPath();
    cxt.moveTo(50, 50);
    cxt.lineTo(450, 50);
    cxt.stroke();
    cxt.beginPath();
    cxt.moveTo(50.5, 150.5);
    cxt.lineTo(450.5, 150.5);
    cxt.stroke();
}

図から、両方の線分のlineWidthを1ピクセルに設定していますが、上の線分は2ピクセルで描画されていることが確認できます。 

ある2ピクセルの境界線に幅1ピクセルの線分を描くと、その線分は実際には2ピクセルの幅を占めることになります。

なぜなら、ピクセル境界に幅1ピクセルの垂直線分を描くと、canvasの描画環境オブジェクトは、境界の中間線から右側に半分、境界の中間線から左側に残りの半分のピクセルを描こうとするためです。

しかし、1ピクセル内に半ピクセル幅の線分を描くことはできないので、半ピクセルを左右に1ピクセルずつ拡大して描画する。

一方、2ピクセル間に描画する場合、中心線の左右両端の半ピクセルは伸びず、両者を合わせてちょうど1ピクセルの幅を占めることになる。つまり、本当に1ピクセル幅の線分を描くには、その線分をある2つのピクセルの間に描かなければならないのです

グリッドの描画

さて、真の1ピクセルの線分を描く方法がわかったので、グリッドの描画を開始しましょう。

function drawLine(stepx, stepy){
    cxt.lineWidth = 0.5;
    cxt.strokeStyle = 'green';
    // Draw vertical line
    for(var i= stepx + 0.5; i< cxt.canvas.width; i+= stepx){
        cxt.beginPath();
        cxt.moveTo(i, 0);
        cxt.lineTo(i, cxt.canvas.height);
        cxt.stroke();
    }
    // Draw horizontal lines
    for(var i= stepy + 0.5; i< cxt.canvas.height; i+= stepy){
        cxt.beginPath();
        cxt.moveTo(0, i);
        cxt.lineTo(cxt.canvas.width, i);
        cxt.stroke();
    }
}
drawLine(10, 10);

上の例では、2つのピクセルの間のピクセルに線分を描きましたが、結果としてできる線分は幅0.5ピクセルだけです。

canvas の仕様には明示されていませんが、Canvas のすべてのブラウザの実装では、quot;アンチエイリアシングを使用して、quot;サブピクセルの線分を作成します。

要約すると

ここでは、主にmoveTo()で始点を、lineTo()で終点を、stroke()で現在のパスを描画する方法について説明します。これら3つのメソッドは、線分を描画します。

canvasでパスを描くための重要なメソッドとして、beginPath()とclosePath()があります。グラフを描画する前にbeginPath()を呼び出すことは、複数のグラフを描画するために必要なステップです。

closePath()はfill()を使用する場合は省略可能で、closePath()メソッドが呼び出される場所に注意します。

線を描くとき、線の幅を変えるにはlineWidthを、線の色を変えるにはstrokeStyleを使うことができます。

線分のピクセル境界を把握し、真の1ピクセル幅の線を描けるようにします。

キャンバスでのグラフィック描画に興味のある方は、今後の更新にご期待ください。また、間違っている場合はご指摘、シェアしてください。

この記事が皆様のお役に立てれば幸いです。また、スクリプトハウスを応援して頂ければ幸いです。