CanvasDOM画像処理

【JavaScript】 Path2D:Canvasのパスを個別登録しておいて使いまわす

更新日:2023/01/30

JavaScriptのCanvasAPIで描画をするとき、頻繁に使用するパスはPath2Dを使用すると何度でも使いまわすことができます。
アニメーションなどを表現するなら、とても便利な機能です。

そこで今回は、Path2Dについてお伝えします。

 

Path2Dの使い方

Path2Dオブジェクトの取得

Path2Dオブジェクトは、Path2Dのコンストラクタから取得します。

const path = new Path2D();

Path2Dオブジェクトのコピー

既存のPath2Dオブジェクトを、Path2Dのコンストラクタに引数として与えることでコピーすることができます。

const path = new Path2D();

・・・pathを操作する

const newPath = = new Path2D( path );

Path2Dオブジェクトにパスを登録

Path2Dオブジェクトは、次のメソッドでパスを登録できます。
使用方法は、同名のCanvasAPIメソッドと同じです。

Path2D.closePath()
Path2D.moveTo()
Path2D.lineTo()
Path2D.bezierCurveTo()
Path2D.quadraticCurveTo()
Path2D.arc()
Path2D.arcTo()
Path2D.ellipse()
Path2D.rect()

Path2Dオブジェクトを描画

Path2Dオブジェクトに登録したパスの描画は、CanvasAPIのコンテキストのfill()またはstroke()メソッドに引数として渡します。

const context = canvas.getContext('2d');

const path = new Path2D();
path.moveTo( 10 , 10 );
path.lineTo( 100 , 10 );
path.lineTo( 100 , 100 );
path.closePath();
            
context.stroke( path );

Path2D 描画

Path2Dオブジェクトを別のPath2Dオブジェクトに追加

addPath()メソッドを使用することで、Path2Dオブジェクトを別のPath2Dオブジェクトに追加することができます。

const context = canvas.getContext('2d');

const path = new Path2D();
path.rect( 10 , 10 , 50 , 50);

const path2 = new Path2D();
path2.rect( 30 , 30 , 50 , 50);

path.addPath( path2);
            
context.stroke( path );

Path2d パスの追加

追加するPath2Dオブジェクトは、それ自体で完結している必要があります。

次の例は、Path2Dオブジェクトを描画のコードをPath2Dオブジェクトに分割しています。

const path = new Path2D();
path.moveTo( 10 , 10 );
path.lineTo( 100 , 10 );

const path2 = new Path2D();
path2.lineTo( 100 , 100 );
path2.closePath();
            
path.addPath( path2);
            
context.stroke( path );

Path2d addPath 失敗例

三角形が描画されるのを期待していましたが、描画されませんでした。

lineTo()メソッドは、moveTo()メソッドで始点を設定する必要があります。

上の例ではpath2に対して、moveTo()メソッドを使用していないためlineTo()で直線を登録できていません。
そのためpathにpath2を追加しても、期待通りの結果とならなかったのです。

 

Path2Dの具体的な使用例

異なる位置・色で描画する

fillStyleに色を、translateを使用して移動をおこないます。

const color = ["red","blue","green","pink"];
            
const path = new Path2D();
path.moveTo( 10 , 10 );
path.lineTo( 100 , 10 );
path.lineTo( 100 , 100 );
path.closePath();

color.forEach( (e,i)=>{
                context.save();
                context.fillStyle = e;
                context.translate( i * 100 , 0 );
                context.fill( path );
                context.restore();
});

Path2D 具体例

回転と拡大をおこなう

次はPath2Dを使用して、同じ図形の拡大と回転をおこなってみます。

まずはデモを見てください。
「実行/停止」を押すと、実際にJavaScriptで動作します。

コード

HTML

<div id="canvas_wrap">
<canvas id="canvas" height="350" width="600"></canvas>
</div>

CSS

#canvas_wrap{
    padding:0;
    margin:0;
    border:1px solid #000;
}
#canvas{
    width:100%;
}

JavaScript

#canvas_wrap{
    padding:0;
    margin:0;
    border:1px solid #000;
}
#canvas{
    width:100%;
}

window.addEventListener( "DOMContentLoaded" , ()=> {

            const canvas = document.getElementById( "canvas" );
            const context = canvas.getContext( "2d" );
            const [ canvasWidth , canvasHeight ] = [ canvas.width  , canvas.height ];
            const [ canvasWidthHalf , canvasHeightHalf ] = [ canvasWidth / 2 , canvasHeight / 2];
            
            context.fillStyle = "pink";
            context.strokeStyle = "blue";
            context.lineWidth = 3;
            
                // キャンバスの中心を原点(0,0)にセット
            context.translate( canvasWidthHalf , canvasHeightHalf );
            context.save();
         
            
                // Path2Dオブジェクト作成(ハート形)
            const path = new Path2D();
            path.moveTo( 0 , 43);
            path.quadraticCurveTo(70 , 3, 70  , -37);
            path.bezierCurveTo(70 , -77 , 20  , -77, 0 , -37);
            path.bezierCurveTo(-20 , -77, -70  , -77, -70 , -37);
            path.quadraticCurveTo(-70 , 3 , 0 , 43);

            
            let sc = 0.1, rd = 0, dir = 1;
            let x = -canvasWidthHalf , xStep = canvasWidthHalf / 20;
            let y = -canvasHeightHalf , yStep = canvasHeightHalf / 20;
            
                // インターバルセット
            setInterval(
                ()=>{
                        // キャンバスクリア
                    context.clearRect( -canvasWidthHalf , -canvasHeightHalf , canvasWidth , canvasHeight);
                    
                    context.save();
                    
                        // コンテキストで描画
                    context.beginPath();
                    context.moveTo( x , -canvasHeight);
                    context.lineTo( x , canvasHeight );
                    context.moveTo( -canvasWidthHalf , y);
                    context.lineTo( canvasWidthHalf , y );
                    context.stroke();
                    
                        // キャンバス座標の拡大と回転
                    context.scale(sc,sc);
                    context.rotate( dir * rd * Math.PI/180);

                        // Path2Dオブジェクトを描画
                    context.fill(path);
                    context.restore();
                    
                        // 各変数を更新
                    [ sc ,dir] = sc > 5.0 ? [ 0 , dir * -1] : [ sc + 0.1 , dir];
                    rd = ( rd > 350 ) ? 0 : rd + 10;
                    x = ( x > canvasWidthHalf - xStep ) ? -canvasWidthHalf : x + xStep;
                    y = ( y > canvasHeightHalf - yStep ) ? -canvasHeightHalf : y + yStep;
                },50
            );

});

最初に後ろの青いラインを、コンテキストで直接描画しています。

その後座標系の拡大と回転を行った後、pathを元に描画をおこなっています。

更新日:2023/01/30

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。