【JavaScript】 Canvasの使い方まとめ
更新日:2023/02/06
JavaScriptのCanvasAPIを利用すると、canvas要素に描画をおこなうことができます。
CanvasAPIの使い方と、利用できるメソッドについてまとめてみました。
Canvasとは
Canvasを使用すると、ブラウザ上にJavaScriptを利用して線や円などの図形を描画できます。
またCanvasの消去と再描画を繰り返すことで、アニメーションを作成することができます。
例:アナログ時計
描画の手順
Canvasに描画するための手順は、次のとおりです。
- DOM内にCanvas要素を作成する。
- Canvas要素を取得する。
- Canvas要素のサイズを設定する。
- Canvas要素からコンテキストを取得する。
- コンテキストを使用して線色などを指定する。
- コンテキストを使用して描画をおこなう。
DOM内にCanvas要素を作成する
Canvas要素はhtmlタグ、またはJavaScript上で作成します。
htmlタグで作成
html
<canvas id="canvas" width="300" height="300" ></canvas>
div要素内にJavaScriptで追加
html
<div id="canvas_outer"></div>
JavaScript
// DOM構築まで待つ
window.addEventListener("DOMContentLoaded",()=> {
// div要素を取得
const viewElm = document.getElementById( "canvas_outer" );
// キャンバスを作成
const cvs = document.createElement("canvas");
// キャンバスをdiv要素に追加
viewElm.appendChild( cvs );
});
※DOM要素の操作は、DOMが構築されるのを待つ必要があります。
Canvas要素を取得する。
タグで作成したCanvas要素は、次の方法で取得できます。
idで作成した要素
html
<canvas id="canvas" width="300" height="300" ></canvas>
JavaScript
// DOM構築まで待つ
window.addEventListener("DOMContentLoaded",()=> {
// div要素を取得
const cvs = document.getElementById( "canvas" );
});
cvsがCanvas要素です。
classで作成した要素
html
<canvas class="canvas" width="300" height="300" ></canvas>
JavaScript
// DOM構築まで待つ
window.addEventListener("DOMContentLoaded",()=> {
// div要素を取得
const cvs = document.getElementsByClassName( "canvas" );
for( let i = 0 ; i < cvs.length ; i ++ ){
const canvas = cvs[i];
}
});
cvsがCanvas要素ですが、idでの取得と異なり、要素の配列で取得されます。
Canvasのサイズ
Canvasには描画サイズと表示サイズの2種類あります。
描画サイズと表示サイズが異なる場合、表示サイズに合わせて拡大/縮小されます。
参考記事:【JavaScript】 Canvasのサイズが難解で困る
描画サイズの設定
描画サイズは、canvasタグのwidthとheightまたは、JavaScriptで指定します。
タグで指定
html
<canvas class="canvas" width="300" height="300" ></canvas>
上のタグは、描画サイズを幅300px、高さ300pxに指定しています。
タグで指定しないときのサイズは、幅300px、高さ150pxになります。
JavaScriptで指定
JavaScript
const cvs = document.getElementById( "canvas" );
// キャンバスの描画サイズセット
cvs.setAttribute( "width" , "300" );
cvs.setAttribute( "height" , "300" );
次のコードでも設定可能です。
cvs.width = 300;
cvs.heighth = 300;
ただし、次項の表示サイズの設定と同じような形式のため、記述ミスをするケースが増えますし、デバッグ時にミスに気が付きにくいです。
そのため、setAttributeを推奨します。
表示サイズの設定
表示サイズはcssのwidthとheightまたは、JavaScriptで指定します。
cssで指定
css
#canvas{
width : 300px;
height : 300px;
}
JavaScriptで指定
JavaScript
const cvs = document.getElementById( "canvas" );
// キャンバスの表示サイズセット
cvs.style.width = "300px";
cvs.style.height = "300px";
単位(px)を忘れずに付加してください。
Canvas要素からコンテキストを取得する
キャンバスへの描画は、コンテキスト(CanvasRenderingContext2Dインスタンス)経由でおこないます。
次の手順で取得します。
JavaScript
const cvs = document.getElementById( "canvas" );
// キャンバスのコンテキストを取得
const context = cvs.getContext( "2d" );
線色などを指定する
取得したコンテキストを使用して、線色や塗りつぶし線種(破線)などを設定します。
設定できる値は、線種・線幅・色・グラデーション・影の設定・設定の保存/復元を見てください。
描画をおこなう
CanvasAPIの描画方法には、2種類あります。
- 直接描画
- パスに登録してから描画
四角(矩形)と文字列は、コンテキストが持っているメソッドで直接描画することができます。
矩形(四角)の描画と文字列の描画を見てください。
その他の図形は、コンテキストに線のつながりを(パス)を登録して、最後にまとめて描画します。
(矩形はパスを使用して描画することもできます。)
パスを使用した描画を見てください。
描画例
描画例1:
次の例は、パスを使って矩形を塗りつぶし枠線を描画しています。
const cvs = document.getElementById("canvas");
const context = cvs.getContext('2d');
context.beginPath(); // パスの初期化
context.fillStyle = "red"; // 塗りつぶしをセット
context.strokeStyle = "blue"; // 線色をセット
context.lineWidth = 3; // 線幅をセット
context.rect(10 , 10 , 300 , 100); // 矩形の座標をパスにセット
context.fill(); // パスの情報をもとに塗りつぶし
context.stroke(); // パスの情報をもとに線を描画
描画例2:アニメーション
キャンバスを使ったアニメーション例です。
const cvs = document.getElementById("canvas");
const context = cvs.getContext('2d');
context.fillStyle = "red";
setInterval(
()=>{
context.clearRect( 0 , 0 , cvs.width, cvs.height);
context.fillRect( x , 10 , 50 , 50 );
x = ( x + 50 > cvs.width ) ? 0 : x + 50;
},1000
);
キャンバスを使ったアニメーションは、キャンバス全体を塗りつぶしてから再描画します。
複雑な図形を描画する場合、レイヤー分けをすることで必要な部分だけ再描画することが可能です。
参考記事:【JavaScript】 Canvasでレイヤーを表現する
線種・線幅・色・グラデーション・影の設定・設定の保存/復元
図形を描画する前に、コンテキストに線種等を設定します。
線幅の指定:lineWidth
線幅はlineWidthで指定します。
初期値は、1.0です。
JavaScript
context.lineWidth = 3;
描画時の座標は、線の中心となります。
破線の指定:setLineDash( )
破線のパターン(長さ)を配列で指定します。
JavaScript
context.setLineDash( [ 4 , 4 ] );
例:
■setLineDash( [ 4 , 4 ] )
■setLineDash( [ 10 , 15 ] )
■setLineDash( [ 10 , 2 , 10 , 5 ] )
■setLineDash( [ ] ) (リセット)
破線の開始位置:lineDashOffset
破線の開始位置を指定します。
JavaScript
context.lineDashOffset = 10;
例:setLineDash( [ 10 , 2 , 10 , 5 ] )のとき
■lineDashOffset = 10
■lineDashOffset = 20
■lineDashOffset = 30
現在の破線パターン取得:getLineDash( )
現在の破線パターンを取得します。
JavaScript
context.setLineDash( [ 4 , 4 ] );
console.log( context.getLineDash() ); // [ 4 , 4 ]
終端形状の設定:lineCap
線の終端の形状を設定します。
JavaScript
context.lineCap = "butt";
設定できる値は、3種類あります。
■lineCap = "butt"
切り取り(デフォルト)
■lineCap = "round"
半円
■lineCap = "square"
長方形(線幅×線幅の2分の一)
線の接続形状の設定:lineJoin
線と線の接続形状を指定します。
JavaScript
context.lineJoin = "miter";
設定できる値は、3種類あります。
■lineJoin = "miter"
ひし形で塗りつぶし(デフォルト)
■lineJoin = "bevel"
各辺の頂点同士で塗りつぶし
■lineJoin = "round"
円弧で塗りつぶし
miter接続の最大値設定:miterLimit
lineJoinを"miter"に指定(デフォルト)したとき、"bevel"に切り替える最大値を指定。
JavaScript
context.miterLimit = "100";
線と線を鋭角で接続すると、塗りつぶし範囲が非常に長くなる問題があります。
そのため、飛び出した部分が線幅の2分の一 × miterLimitを超えたとき、"bevel"に切り替えて塗りつぶしをおこないます。
例:
飛び出した部分が線幅の20倍とします。
このとき、miterLimitを40以下にすると、"bevel"に切り替わります。
※正確な数値ではありません。
■miterLimit = "39
線の色の指定:strokeStyle
ストローク(線描画)系メソッドで描画される、線の色を指定します。
JavaScript
context.strokeStyle = "#F00";
context.strokeRect( 10 , 10 , 300 , 50);
色は、次の形式で指定します。
色指定 | 例 | 結果 |
---|---|---|
カラーコード | context.strokeStyle = "#F00"; | |
カラー名 | context.strokeStyle = "red"; | |
rgb | context.strokeStyle = "rgb( 255 , 0 , 0 )"; | |
rgba | context.strokeStyle = "rgba( 255 , 0 , 0 , .5 )"; | |
グラデーション | グラデーションの指定を参照 |
塗りつぶし色の指定:fillStyle
フィル(塗りつぶし)系メソッドで描画される、塗りつぶし色を指定します。
JavaScript
context.fillStyle = "#F00";
指定できる色の形式は、線の色の指定:strokeStyleと同じです。
画像パターンの指定:createPattern ()
線の描画及び、塗りつぶしで色を指定するかわりに画像を指定します。
HTML
<img id ="image" src="…">
const image = document.getElementById("image");
const imgPattern = context.createPattern( image ,"repeat");
context.strokeStyle = imgPattern;
context.lineWidth = 30;
context.beginPath();
context.moveTo(10,10);
context.lineTo( 200 , 40);
context.lineTo( 50 , 200 );
context.lineTo( 260 , 180 );
context.stroke( );
context.createPatternの2番目の引数は、画像の繰り返し方法を指定します。
"repeat" : キャンバスいっぱいに繰り返し
"repeat-x" : 横方向に繰り返し
"repeat-y" : 縦方向に繰り返し
"no-repeat" : 繰り返さない
createPatternは、描画する図形を画像パターンで描くのではなくて、図形パターンで塗りつぶした下地を図形で切り抜くイメージです。
関連記事:【JavaScript】 画像を動的に読み込んでhtml(DOM)やCanvasで使用する
グラデーションの指定:createLinearGradient () / createRadialGradient() / addColorStop (pos, color)
線の描画及び、塗りつぶしで色を指定するかわりに、グラデーションを指定できます。
JavaScript
const grad = context.createLinearGradient( 0 , 0 , 200 , 200 ) ;
grad.addColorStop(0.0 , "red");
grad.addColorStop(0.5 , "green");
grad.addColorStop(1.0 , "blue");
context.fillStyle = grad;
context.fillRect( 0 , 0 , 200 , 200);
詳しくは、こちらの記事をご覧ください。
参考記事:【JavaScript】 Canvasにグラデーションを描画しよう
影の指定:shadowOffsetX / shadowOffsetY / shadowBlur / shadowColor
影を指定します。
値をセット後に描画した図形に適用されます。
JavaScript
const image = document.getElementById("image");
const imgPattern = context.createPattern( image ,"repeat");
context.strokeStyle = imgPattern;
context.lineWidth = 30;
context.shadowOffsetX = 10; // 影の横方向へのズレ
context.shadowOffsetY = 10; // 影の縦方向へのズレ
context.shadowColor = "red"; // 影の色
context.shadowBlur = 10; // 影のぼかし度
context.beginPath();
context.moveTo(10,10);
context.lineTo( 200 , 40);
context.lineTo( 50 , 200 );
context.lineTo( 260 , 180 );
context.stroke( );
縦横のズレはピクセル単位。
色はカラーコード、カラー名、rgb、rgbaで指定します。グラデーションは指定できません。
ぼかし度は、数値で指定します。数値が大きいほどぼけて描画されます。
透明度の指定:globalAlpha
透明度を指定します。
値をセット後に描画した図形に適用されます。
透明度は 0.0 (透明) ~ 1.0 (不透明)で指定します。
JavaScript
context.fillStyle = "blue";
context.globalAlpha = 1.0;
context.fillRect( 0 , 0 , 200 , 200);
context.globalAlpha = 0.5;
context.fillRect( 250 , 0 , 200 , 200);
重なり部分の合成方法の指定:globalCompositeOperation
JavaScript
context.fillStyle = "blue";
context.fillRect( 0 , 0 , 100 , 100);
context.globalCompositeOperation = "destination-over";
context.fillStyle = "red";
context.fillRect( 50 , 20 , 100 , 100);
context.globalCompositeOperation = "lighter";
context.fillStyle = "green";
context.fillRect( 70 , 40 , 100 , 100);
合成処理の詳細は、次のページを参考にしてください。
参考記事:【JavaScript】 Canvas描画で特殊な合成をおこなうglobalCompositeOperation
設定の保存/復元 : save() / restore()
save()メソッドは、コンテキストの現在の設定を保存します。
restore()メソッドは、保存された設定を復元します。
context.save();
context.strokeStyle = imgPattern;
context.lineWidth = 30;
context.shadowOffsetX = 10;
context.shadowOffsetY = 10;
context.shadowColor = "red";
context.shadowBlur = 10.7;
context.restore();
最初にsave()を実行することで、restore()により初期状態に戻すことができます。
※save()は実行するたびに、状態をスタックします。
そのためrestore()を呼び出すごとに、save()で保存した情報をさかのぼって復元します。
const cvs = document.getElementById("canvas");
const context = cvs.getContext('2d');
context.font = "25px sans-serif";
context.save();
context.fillStyle = "red";
context.save();
context.fillStyle = "blue";
context.save();
context.save();
context.fillStyle = "green";
context.fillText( context.fillStyle , 10 , 30);
context.restore();
context.fillText( context.fillStyle , 10 , 60);
context.restore();
context.fillText( context.fillStyle , 10 , 90);
context.restore();
context.fillText( context.fillStyle , 10 , 120);
context.restore();
context.fillText( context.fillStyle , 10 , 150);
※save()とrestore()は、描画設定をリセットするものです。
次々項のパスをリセットすることはできません。
矩形(四角)の描画
Canvasを背景色(透明)で初期化(塗りつぶし):clearRect()
キャンバスを透明色で初期化する場合、clearRect()を使用します。
const cvs = document.getElementById("canvas");
const context = cvs.getContext('2d');
context.clearRect( 0 , 0 , cvs.clientWidth , cvs.clientHeight);
clearRect( x , y , w , h )
始点( x , y ) から幅w、高さhの矩形を透明色で初期化します。
キャンバス全体をクリアするときは、上の例のように始点( 0 ,0 )と、キャンバスの幅と高さを指定します。
幅と高さは、widthとheightを使用します。
clientWidth及びclientHeightはcssのwidthとheightを元に計算されるので、キャンバスサイズとズレていることがあります。
参考記事:【JavaScript】 Canvasのサイズが難解で困る
矩形を描画:strokeRect()
矩形の描画は、strokeRect()メソッドを使用します。
context.strokeStyle = "red";
context.lineWidth = 10;
context.strokeRect( 0 , 0 , 300 , 100);
context.strokeStyle = "blue";
context.lineWidth = 5;
context.strokeRect( 100 , 30 , 170 , 60);
strokeRect()の引数は、clearRect()と同じです。
色の指定は、strokeStyleで行います。
また、線は座標を中心として描画される点に注意してください。。
(線幅の指定:lineWidth参照)
上の例では( 0 , 0 )を始点として描画しているため、上線/左線が、下線/右線よりも細くなっています。
幅と高さ内に収まるように描画するなら、線幅を考慮した引数指定が必要です。
線幅を考慮した引数指定
const lWidth = context.lineWidth;
const hosei = lWidth / 2;
context.strokeRect( 0 + hosei , 0 + hosei , 300 - lWidth , 100 -lWidth);
塗りつぶした矩形を描画: fillRect()
塗りつぶした矩形の描画は、fillRect()メソッドを使用します。
context.fillStyle = "red";
context.fillRect( 10 , 10 , 300 , 100);
context.fillStyle = "blue";
context.fillRect( 100 , 30 , 170 , 60);
fillRect()の引数は、clearRect()と同じです。
色の指定は、fillStyleで行います。
補足:矩形の枠線を書いて塗りつぶし
Canvas APIには、枠線を書いて塗りつぶしする機能はありません。
そのため、塗りつぶしてから枠線を描画します。
context.fillStyle = "red";
context.fillRect( 10 , 10 , 300 , 100);
context.strokeStyle = "blue";
context.lineWidth = 3;
context.strokeRect( 10 , 10 , 300 , 100);
パスを使用した描画
矩形以外を描画する場合、線のつながりを(パス)を登録して、最後に描画します。
context.strokeStyle = "blue";
context.fillStyle = "red";
context.lineWidth = 3;
context.beginPath(); // パスの初期化
context.moveTo(50,50); // 始点の移動
context.lineTo(100,10); // 始点から指定した点までの直線をパスに登録
context.lineTo(150,100);
context.closePath(); // 現在の始点と最初の点を結んで、パスを閉じる
context.fill(); // パス情報をもとに塗りつぶし
context.stroke(); // パス情報をもとに線を描画
パスの初期化:beginPath()
beginPath()は登録されているパス情報をリセットして、新規でパスを登録できるようにします。
context.beginPath();
引数はありません。
※beginPath()は、パスに登録した情報をリセットするものです。
前項の描画状態の設定をリセットすることはできません。
始点の移動:moveTo()
moveTo()は、描画の開始点を移動します。
context.moveTo( x ,y );
( x ,y )は、キャンバス上の座標です。
直線の登録:lineTo()
lineTo()は、現在の開始点から引数で指定した座標までの直線をパスに登録します。
現在の開始点が、引数で指定した座標に置き換わります。
context.lineTo( x ,y );
( x ,y )は、キャンバス上の座標です。
パスを閉じる:closePath()
closePath()は、現在の開始点から最初の開始点までの直線をパスに登録します。
context.closePath( );
引数はありません。
パスを元に塗りつぶし:fill()
fill()は、パス情報をもとに塗りつぶし処理をおこないます。
context.fill( fillRule );
または
context.fill( path2D , fillRule );
path2DおよびfillRuleは省略可能です。
引数fillRuleは、次の二つの値を指定できます。
"nonzero" : デフォルト。パス内全て塗りつぶす。
"evenodd" : パスが偶数回重なっている場合塗りつぶさない。
context.fillStyle = "red";
context.fillRect( 0 , 0 , 400 , 200);
context.fillStyle = "black";
context.beginPath();
context.rect( 10 , 10 , 100 , 100 );
context.rect( 50 , 50 , 100 , 100 );
context.rect( 80 , 80 , 20 , 20 );
context.fill();
context.beginPath();
context.rect( 210 , 10 , 100 , 100 );
context.rect( 250 , 50 , 100 , 100 );
context.rect( 280 , 80 , 20 , 20 );
context.fill("evenodd");
引数path2Dは、コンテキスト外に保存したパスです。
引数を指定しない場合は、コンテキストに登録したパスで描画されます。
【JavaScript】 Path2D:パスを個別登録しておいて使いまわす
closePath()で閉じていなくても、塗りつぶされます。
塗りつぶし色は、context.fillStyleで設定します。
パスを元に線を描画:stroke()
stroke()は、パス情報をもとに線を描画します。
context.stroke( path2D );
path2Dは省略可能です。
引数path2Dは、コンテキスト外に保存したパスです。
引数を指定しない場合は、コンテキストに登録したパスで描画されます。
path2Dについては次のページを見てください。
【JavaScript】 Path2D:パスを個別登録しておいて使いまわす
線色は、context.fillStyleで設定します。
補足:パスの色や線幅などの指定について
パスの設定値は、描画時に参照されます。
そのため、パス登録途中に設定を変更しても意図したように描画されません。。
青と赤の線を描画したい:失敗
context.lineWidth = 3;
context.beginPath();
context.moveTo(50,50);
context.strokeStyle = "blue";
context.lineTo(100,10);
context.strokeStyle = "red";
context.lineTo(150,100);
context.stroke();
stroke()やfill()で描画したあと、beginPath()でパスをリセット。
その後、改めて描画をします。
青と赤の線を描画したい:成功
context.lineWidth = 3;
context.beginPath();
context.moveTo(50,50);
context.strokeStyle = "blue";
context.lineTo(100,10);
context.stroke();
context.beginPath();
context.moveTo(100,10);
context.strokeStyle = "red";
context.lineTo(150,100);
context.stroke();
2本目のパス登録はパスがリセットされているので、moveTo()で目的の位置まで移動する必要があります。
パスに矩形を登録:rect()
rect()はパスに矩形を登録します。
context.beginPath();
context.fillStyle = "red";
context.strokeStyle = "blue";
context.lineWidth = 3;
context.rect(10 , 10 , 300 , 100);
context.fill();
context.stroke();
rect( x , y , w , h )
始点( x , y ) から幅w、高さhの矩形がパスに登録されます。
fillRect() / strokeRect() でも同じ図形を描画できます。
パスに円弧を登録:arc()
arc()はパスに円弧を登録します。
context.lineWidth = 3;
context.beginPath();
context.strokeStyle = "blue";
context.arc(100 , 100 , 50 , 0 , Math.PI / 2);
context.stroke();
context.beginPath();
context.strokeStyle = "red";
context.arc(100 , 100 , 50 , 0 , Math.PI / 2 , true);
context.stroke();
arc( x , y , r , start , end , flg = false );
中心( x ,y )、半径r で角度startから角度endまでの円弧を描画します。
角度は、時計の3時の位置が0で、9時の位置がπ(Math.PI)となります。
完全な円を描画する場合、始点0から終点2π(Math.PI * 2)を指定します。
flgは、円を描画する方向です。
trueの場合、反時計回りに描画します。
指定しない場合はfalseです。
図形に接続する円弧を登録:arcTo()
arcTo()は、図形と図形をつなぐのに最適な円弧をパスに登録します。
context.lineWidth = 3;
context.beginPath();
context.strokeStyle = "blue";
context.moveTo( 50 , 50 );
context.lineTo( 80 , 80 );
context.arcTo( 100 , 100 , 150 , 50 , 20);
context.lineTo( 150 , 50 );
context.stroke();
arcTo( x1 , y1 , x2 , y2 , r );
現在の開始点と( x1 , y1 )を結ぶ直線と、( x1 , y1 )と( x2 , y2 )を結ぶ直線の両方に、半径rで接する円弧を登録します。
楕円を登録:ellipse()
ellipse()は、パスに楕円を登録します。
context.lineWidth = 4;
context.strokeStyle = "blue";
context.fillStyle = "pink";
context.beginPath();
context.ellipse(150, 150, 100, 30, Math.PI / 3 , 0, Math.PI / 3 * 2 );
context.fill();
context.stroke();
context.beginPath();
context.strokeStyle = "red";
context.fillStyle = "lightblue";
context.beginPath();
context.ellipse(150, 150, 100, 30, Math.PI / 3 , 0, Math.PI / 3 * 2 , true);
context.fill();
context.stroke();
ellipse( x , y , rw , rh , rotate , stat , end , flg = false )
中心( x ,y )。x方向の半径rw。y方向の半径rh。
傾きrotate で角度startから角度endまでの円弧を描画します。
傾きは時計回りで90度が2分のπ(Math.PI / 2)です。
角度は、時計の3時の位置が0で、9時の位置がπ(Math.PI)となります。
完全な楕円を描画する場合、始点0から終点2π(Math.PI * 2)を指定します。
flgは、楕円を描画する方向です。
trueの場合、反時計回りに描画します。
指定しない場合はfalseです。
2次ベジュ曲線を登録:quadraticCurveTo()
quadraticCurveTo()は、パスに2次ベジュ曲線を登録します。
context.lineWidth = 3;
context.beginPath();
context.strokeStyle = "blue";
context.moveTo( 50 , 50 );
context.quadraticCurveTo(70 , 100 , 150 , 50);
context.stroke();
quadraticCurveTo( x1 , y1 , x2 , y2)
( x1 , y1 )を制御点として、現在の開始点と( x2 , y2 )を結ぶ2次ベジュ曲線を作成します。
3次ベジュ曲線を登録:bezierCurveTo()
bezierCurveTo()は、パスに3次ベジュ曲線を登録します。
context.lineWidth = 3;
context.beginPath();
context.strokeStyle = "blue";
context.moveTo( 50 , 50 );
context.bezierCurveTo(70 , 100 , 120 , 100 , 150 , 50);
context.stroke();
bezierCurveTo( x1 , y1 , x2 , y2 , x3 , y3 )
( x1 , y1 )と( x2 , y2 )を制御点として、現在の開始点と( x3 , y3 )を結ぶ3次ベジュ曲線を作成します。
マスクをかける:clip()
clip()は、パスに登録された図形の外側にマスクをかけて、図形内だけ描画できるようにします。
context.save();
context.beginPath();
context.rotate( 30 * Math.PI / 180); // 30度回転
// ハートのパスを作成
context.moveTo( 150 , 150 );
context.quadraticCurveTo(220 , 110 , 220 , 70 );
context.bezierCurveTo(220 , 30 , 170 , 30 , 150 , 70);
context.bezierCurveTo(130 , 30 , 80 , 30 , 80 , 70);
context.quadraticCurveTo(80 , 110 , 150 , 150 );
// ハートのパスでクリップ(マスク)
context.clip();
// 画像で塗りつぶし
const imgPattern = context.createPattern( image ,"repeat");
context.fillStyle = imgPattern;
context.fillRect( 0 , 0 , 400 , 400);
context.restore();
パスに登録した図形をマスクするのではなくて、その外側をマスクすることに注意してください。
clip()を実行した後、beginPath()を実行してもマスクは解除されません。
clip()を実行する前に、save()で初期状態を保存しておき、マスクが必要なくなったらrestore()で初期状態を復元してください。
指定した座標がパス内に含まれるかチェックする:isPointInPath ( )
isPointInPath()メソッドは、引数で指定された座標がパス内に含まれるかチェックします。
context.beginPath();
context.rect( 100 , 100 , 200 , 200 );
console.log( context.isPointInPath(150,150) ); // true
console.log( context.isPointInPath(10,10) ); // false
isPointInPath( x , y )
( x , y )はキャンバス上の座標です。
キャンバスサイズと表示サイズが同一でない場合、注意が必要です。
具体的な使用例はこちら。
参考記事:【JavaScript】 Canvas上でクリックした図形を塗りつぶす
指定した座標が線上に含まれるかチェックする:isPointInStroke ( )
isPointInStroke()メソッドは、引数で指定された座標が線上に含まれるかチェックします。
context.beginPath();
context.lineWidth = 15;
context.rect( 100 , 100 , 200 , 200 );
console.log( context.isPointInStroke( 150 , 100 ) ); // true
console.log( context.isPointInStroke( 150 , 150 ) ); // false
isPointInPath( x , y )
( x , y )はキャンバス上の座標です。
キャンバスサイズと表示サイズが同一でない場合、注意が必要です。
具体的な使用例はこちら。
参考記事:【JavaScript】 Canvas上でクリックした線の色変更と移動
文字列の描画
フォント・サイズの指定:font
描画をおこなう文字列のフォントとサイズは、fontで指定します。
context.font = "サイズ フォント名" で指定します。
初期値は"10px sans-serif"です。
context.font = "25px serif";
context.fillText( context.font , 10 , 30 );
context.font = "25px sans-serif";
context.fillText( context.font , 10 , 60 );
context.font = "25px cursive";
context.fillText( context.font , 10 , 90 );
※ブラウザにより結果が異なります
文字列の描画:fillText()
fillText()は文字列の描画をおこないます。
context.font = "45px serif";
context.fillStyle = "red";
context.fillText( context.font , 10 , 40 );
context.fillText( context.font , 10 , 80 , 100 );
fillText( text , x , y , maxWidth )
textで指定された文字列を基準点( x , y )に描画します。
maxWidthは最大幅です。指定された場合は、最大幅に収まるように調整されます。
色はfillStyleで指定します。
文字列の枠線の描画:strokeText()
strokeText()は文字列の枠線を描画します。
context.font = "45px serif";
context.strokeStyle = "red";
context.strokeText( context.font , 10 , 40 );
context.strokeText( context.font , 10 , 80 , 100);
strokeText( text , x , y , maxWidth )
textで指定された文字列の枠線を、基準点( x , y )に描画します。
maxWidthは最大幅です。指定された場合は、最大幅に収まるように調整されます。
色はstrokeStyleで指定します。
文字列の水平基準の設定:textAlign
textAlignは、文字列描画時の水平方向の揃え位置を指定します。
context.moveTo( 200 , 0 );
context.lineTo( 200 , 300 );
context.stroke();
context.font = "45px serif";
context.fillStyle = "red";
const align = [ "left" , "right" , "center" , "start" , "end" ];
align.forEach( (e,i)=>{
context.textAlign = e;
context.fillText( context.textAlign , 200 , 40 * (i+1) );
});
textAlign = 揃え位置
■揃え位置に指定できる値
"left" : 左揃え。基準位置(fillTextまたはstrokeTextで指定した座標)から文字列を描画します。
"right" : 右揃え。基準位置で終わるように文字列を描画します。
"center" : 中央揃え。文字列の中心が基準位置にくるように描画します。
"start" : 文字の方向が左から右の場合"left"。右から左の場合"right"に相当します。
"end" : 文字の方向が左から右の場合"right"。右から左の場合"left"に相当します。
文字列の垂直基準の設定:textBaseline
textBaselineは、文字列描画時の垂直方向の揃え位置を指定します。
context.font = "25px serif";
context.fillStyle = "red";
const align = ["top","hanging","middle","alphabetic","ideographic","bottom"];
align.forEach( (e,i)=>{
const baseLine = [ i % 2 * 250 , 80 * ( Math.floor( i / 2 ) + 1)];
context.beginPath();
context.moveTo( baseLine[0] , baseLine[1] );
context.lineTo( baseLine[0] + 250 , baseLine[1] );
context.stroke();
context.textBaseline = e;
context.fillText( context.textBaseline + "で揃える" , baseLine[0] + 10 , baseLine[1]);
});
textBaseline = 揃え位置
■揃え位置に指定できる値
"top" : 文字列の上端で揃えます。
"hanging" : 文字列の上端で揃えます。"top"よりも少し上にズレます。
"middle" : 文字列の中央で揃えます。
"alphabetict" : アルファベットのベースラインで揃えます。
"ideographic" : 漢字等のベースラインで揃えます。
"bottom" : 文字列の下端で揃えます。
文字列の大きさに関する情報を取得:measureText()
measureText()は、文字列の大きさに関する情報を取得します。
取得した情報は、textBaselineとtextAlignの影響を受けています。
座標の変換
移動・回転・拡大・縮小・変形などは、座標の変換によりおこないます。
それぞれに対応するメソッドを実行すると変換バッファのセットされます。
fillRect()などの描画系メソッドやlineTo()などのパス登録系メソッドを実行時に、変換バッファを適用して座標が計算されます。
そのため座標の変換メソッドは、描画やパス登録の前に実行する必要があります。
またパスセット後に実行された座標の変換メソッドは、これまでに登録されている座標の変換とともに、その後に実行された描画やパス登録に適用されます。
バッファのクリアを行うには、save()とrestore()を使用します。
座標を移動する:translate()
translate()は、描画やパス登録の前に原点( 0, 0 )を、指定された値だけ移動します。
window.addEventListener( "DOMContentLoaded" , ()=> {
const line = ( ctx , sx , sy ,ex , ey ) =>{ctx.moveTo(sx,sy);ctx.lineTo(ex,ey);};
const cvs = document.getElementById("canvas");
const context = cvs.getContext('2d');
// 中心に移動
context.translate( cvs.width / 2 , cvs.height / 2 );
const tr = [ [0,0] , [-50 , -50] , [50 , -50] , [-50 , 50] , [ 50 , 50] ];
for( let i = 0 ; i < 5 ; i ++){
context.save();
context.translate( tr[i][0] , tr[i][1] );
context.beginPath();
line( context , -10 , -10 , 10 , 10 );
line( context , 10 , -10 , -10 , 10 );
context.fillText( `( ${tr[i][0]} , ${tr[i][1]} )` , 10 , 5);
context.stroke();
context.restore();
}
});
パス登録時に次の二つの座標変換をおこなっています。
- 原点をキャンバスの左上から、キャンバスの高さ/幅の2分の一の座標に移動
- 1の座標に、二つ目のtranslateで与えられた値を加算した座標に原点を移動
1を実行することで、キャンバスの中心座標を( 0 , 0 )にセットしています。
キャンバスの中心を( 0 , 0 )にするコード
context.translate( cvs.width / 2 , cvs.height / 2 );
context.save( );
座標を回転する:rotate( )
rotate( )は、原点を中心として指定した角度だけ回転をします。
角度はラジアンで、次の計算式で求めることができます。
ラジアン = 度 * Math.PI / 180;
window.addEventListener( "DOMContentLoaded" , ()=> {
const line = ( ctx , sx , sy ,ex , ey ) =>{ctx.moveTo(sx,sy);ctx.lineTo(ex,ey);};
const cvs = document.getElementById("canvas");
const context = cvs.getContext('2d');
// 中心に移動
context.translate( cvs.width / 2 , cvs.height / 2 );
context.save();
context.beginPath();
const ang = (360 / 12) * Math.PI / 180;
for( let i = 0 ; i < 12 ; i ++){
if( i !== 0 ) context.rotate( ang );
line( context , 0 , -50 , 0 , -10 );
context.fillText( (i ===0 ? 12 : i).toString() , 0 , -65);
}
context.stroke();
});
一度のrotate( )で、一周の12分の一だけ回転しています。
しかしrestore()をしていないので、前回のrotate( )に角度が加算されています。
次のようにsave()とrestore()を使用して描画することもできます。
for( let i = 0 ; i < 12 ; i ++){
context.save();
context.rotate( ang * i );
line( context , 0 , -50 , 0 , -10 );
context.fillText( (i ===0 ? 12 : i).toString() , 0 , -65);
context.restore();
}
座標を拡大/縮小する:scale( )
scale( )は、座標系を拡大/縮小します。
window.addEventListener( "DOMContentLoaded" , ()=> {
const cvs = document.getElementById("canvas");
const context = cvs.getContext('2d');
for( let i = 0 ; i < 4 ; i ++){
context.save();
const scl = ( i === 0 ) ? 1 : i * 1.5;
context.translate( 0 , 60*i);
context.scale( scl , scl );
context.fillRect( 0 , 10 , 100 , 10 );
context.fillText( "( 100 , 10)" , 100 , 10 );
context.fillText( `scale ${scl}` , 100 , 20 );
context.restore();
}
});
このメソッドは、キャンバス全体を拡大するために使用するべきではありません。
キャンバス全体を拡大するときは、次の方法も検討してみてください。
参考記事:【JavaScript】 マウス操作でCanvasを拡大/縮小する
変換マトリックスを使用して座標を変換する:transform()
transform()は、座標の変換マトリクスを登録します。
fillRect()などの描画系メソッドやlineTo()などのパス登録系メソッドを実行時に、マトリクスに従って座標が変換されます。
context.fillRect( 10 , 10 , 50 , 50 );
context.transform( 1.5 , 0 , 0 , 1 , 0 , 0);
context.transform( 1 , 0 , 0 , 1.5 , 0 , 0);
context.fillRect( 100 , 10 , 50 , 50 );
transform( a , b , c , d, e , f )
与えられた引数は、次のマトリクスを形成します。
a c e
b d f
0 0 1
マトリクス変換後の座標は、次のように計算されます。
x y 1
a c e
b d f
0 0 1
=
変換後x: x*a + y*c + 1*e
変換後y: x*b + y*d + 1*f
x*0 + y*0 + 1*1
これから、各引数は次のような意味を持つことになります。
引数 | 意味 | 例 | |
---|---|---|---|
- | 変形なし | (1, 0, 0, 1, 0, 0) | |
a | 水平方向の拡大率 | (1.5, 0, 0, 1, 0, 0) | |
b | 垂直方向の傾斜率 | (1, 0.5, 0, 1, 0, 0) | |
c | 水平方向の傾斜率 | (1, 0, 0.5, 1, 0, 0) | |
d | 垂直方向の拡大率 | (1, 0, 0, 1.5, 0, 0) | |
e | 水平方向の移動距離 | (1, 0, 0, 1, -50, 0) | |
e | 垂直方向の移動距離 | (1, 0, 0, 1, 0,-5 0) |
変換マトリックスをリセットして指定したマトリクスをセット:setTransform()
setTransform()は、transform()メソッドで登録したマトリクスをリセットして、指定したマトリクスをセットします。
context.save();
context.transform( 1.5 , 0 , 0 , 1 , 0 , 0);
context.transform( 1 , 0 , 0 , 1.5 , 0 , 0);
context.fillRect( 10 , 10 , 50 , 50 );
context.restore();
context.save();
context.transform( 1.5 , 0 , 0 , 1 , 0 , 0);
context.setTransform( 1 , 0 , 0 , 1.5 , 0 , 0);
context.fillRect( 200 , 10 , 50 , 50 );
context.restore();
画像の取り扱いに関するメソッド
画像を描画する:drawImage()
drawImage()は、imgタグやImageオブジェクト上の画像をキャンバスに描画します。
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.drawImage( img , 50 , 50 );
// 横に2倍して表示
context.drawImage( img , 100 + img.naturalWidth , 50 , img.naturalWidth * 2 , img.naturalHeight );
context.textBaseline = "top";
context.fillText( "0,0" , 0 , 0 );
drawImage()メソッドは、引数の数の違いで次の3つの機能を持っています。
- 引数3つ:Canvasの指定した位置に画像を描画(貼り付け)
drawImage( image sx , sy )
image: 貼り付けるイメージ
sx: 貼り付け先のキャンバス座標x
sy: 貼り付け先のキャンバス座標y - 引数5つ:Canvasの指定した位置に拡大・縮小して画像を描画(貼り付け)
drawImage( image sx , sy , sw , sh )
image: 貼り付けるイメージ
sx: 貼り付け先のキャンバス座標x
sy: 貼り付け先のキャンバス座標y
sw: 貼り付け先の幅
sh: 貼り付け先の高さ - 引数7つ:元画像の一部をキャンバスの指定した位置に拡大・縮小して描画(貼り付け)
drawImage( image sx , sy , sw , sh , sx2 , sy2 , sw2 , sh2)
image: 貼り付けるイメージ
sx: 元画像の切り出し位置x
sy: 元画像の切り出し位置y
sw: 元画像の切り出し幅
sh: 元画像の切り出し高さ
sx2: 貼り付け先のキャンバス座標x
sy2: 貼り付け先のキャンバス座標y
sw2: 貼り付け先の幅
sh2: 貼り付け先の高さ
drawImage()については、次の記事も参考にしてください。
参考記事:【JavaScript】 Canvasにサーバー上の画像を貼り付ける
空の画像データを作成/複製: createImageData()
createImageData()は、空の画像データ(ImageDataオブジェクト)を作成、または画像データを複製します。
ImageDataオブジェクトは、次のプロパティを持っています。
ImageData{
width : 画像の幅
height: 画像の高さ
data : 画像データ
}
dataプロパティは左上のピクセルの色を、次のように配列4つで表します。
[0]…赤、[1]…緑、[2]…青、[3]…透明度
各値は、0から255の範囲の数値です。
その右隣のピクセルはインデックス4から同じように配置されます。
以降繰り返され、最終ピクセルまで格納されます。
window.addEventListener( "DOMContentLoaded" , ()=> {
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
// 空のImageDataオブジェクトを作成
const imgData = context.createImageData( 250 , 50 );
const lineWidth = 10; // 線の太さ
// 上線のスタート位置を取得
const topLineIndex = 0;
// 下線のスタート位置を取得
const bottomLineIndex = imgData.width * (imgData.height - lineWidth) * 4;
const data = imgData.data;
for( let i = 0 ; i < imgData.width * lineWidth; i ++ ){
const index1 = topLineIndex + i * 4;
data[index1] = 255;
data[index1 + 1 ] = 60;
data[index1 + 2 ] = 120;
data[index1 + 3 ] = 255;
const index2 = bottomLineIndex + i * 4;
data[index2] = 255;
data[index2 + 1 ] = 60;
data[index2 + 2 ] = 120;
data[index2 + 3 ] = 120;
}
context.putImageData(imgData,10,10);
});
createImageData( w , h )
または
createImageData( imagedata )
w : 作成するイメージデータの幅
h : 作成するイメージデータの高さ
imagedata : 複製するImageDataオブジェクト
返り値: 作成または複製したImageDataオブジェクト
キャンバスから画像データを取得:getImageData()
getImageData()は、キャンバスから指定した範囲のImageDataオブジェクトを取得します。
ただしgetImageData()は、translate()などの座標変換系メソッドの影響をうけません。
そのため常に左上を( 0 , 0 )として、考える必要があります。
window.addEventListener( "DOMContentLoaded" , ()=> {
const img = new Image();
img.onload = ()=>{
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
// 原点を移動
context.translate( 100 , 0 );
context.drawImage( img , 0 , 0 );
// getImageDataは原点移動の影響を受けないことを考慮して座標を指定
const imgData = context.getImageData( 100 , 0 , img.naturalWidth , img.naturalHeight);
context.putImageData( imgData , 150 + img.naturalWidth , 0 );
};
img.src = "https://xxx.com/xxx.jpg";
});
getImageData( x , y , w , h )
x : キャンバス上の切り出し位置x
y : キャンバス上の切り出し位置y
w : 切り出す幅
h : 切り出す高さ
なお、上のコードをローカルパソコンで実行するとエラーがでます。
詳しくは次のページをご覧ください。
参考記事:【JavaScript】 CanvasでgetImageData()したらSecurityErrorがでた
ImageDataオブジェクトを描画:putImageData()
putImageData()はImageDataオブジェクトをキャンバス上に描画します。
window.addEventListener( "DOMContentLoaded" , ()=> {
const img = new Image();
img.onload = ()=>{
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
context.drawImage( img , 0 , 0 );
const imgData = context.getImageData( 0 , 0 , img.naturalWidth , img.naturalHeight);
const length = imgData.data.length;
// 透明度を100にする
for( let i = 3 ; i < length ; i += 4 ) imgData.data[i] = 100;
// そのまま描画
context.putImageData( imgData , 50 + img.naturalWidth , 0 );
// 一部だけ描画
context.putImageData( imgData , 100 + img.naturalWidth*2 , 0 , 20 , 20 , 30 , 30 );
};
img.src = "https://xxx.com/xxx.jpg";
});
putImageData( imagedata, x, y )
または
putImageData( imagedata, x, y , sx , sy , sw , sh )
imagedata : 描画するImageDataオブジェクト
x : キャンバスの位置x
y : キャンバスの位置y
sx : ImageDataオブジェクトの切り出し位置x
sy : ImageDataオブジェクトの切り出し位置y
sw : ImageDataオブジェクトの切り出し幅
sh : ImageDataオブジェクトの切り出し高さ
xとyのみ指定した場合は、そのままのサイズで描画されます。
なお、上のコードをローカルパソコンで実行するとエラーがでます。
詳しくは次のページをご覧ください。
参考記事:【JavaScript】 CanvasでgetImageData()したらSecurityErrorがでた
更新日:2023/02/06
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。