【JavaFX】Canvasを利用した描画
本稿はJavaFXのCanvasで利用できる機能を一通り確認しておきたいと思います。
JavaFXでもSwingと同じようにGraphicsクラスみたいなものを利用して描画できるような機能が用意されています。
こちらの方法のほうが好みだという方はこちらの方法で描画を実装するのもいいと思います。(2Dゲーム制作ならこちらのほうが使いやすいかもしれません)
もちろんswing、awtで利用していたGraphicsよりもはるかに使いやすくて、高機能です。
Canvasを使用する方法
CanvasはNodeクラスを継承しているため、普通にインスタンス化しオブジェクトの追加と同じように処理すればOKです。
new Canvas(横幅,縦幅)と指定します。
描画処理を実行するには、getGraphicsContext2Dメソッドを使用すると取得できるGraphicsContextオブジェクトのメソッドを呼び出して実行します。
それではサンプルをご覧ください。今回のサンプル群で使用するベースプログラムとしても使用し、drawメソッドを書き換えて描画処理を実行します。
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class FXTest2D extends Application {
public static void main(final String... args) {
launch(args);
}
GraphicsContext g;
@Override
public void start(final Stage stage) {
Group root = new Group();
//描画用キャンバスノードの作成
Canvas cvs = new Canvas(400, 300);
root.getChildren().add(cvs);
this.g = cvs.getGraphicsContext2D();
Scene scene = new Scene(root, 400, 300, Color.WHITE);
stage.setScene(scene);
stage.show();//ウィンドウの表示
draw();
}
private void draw() {
//ここに描画処理を記述
}
}
描画色の変更
線の描画色の変更はsetStroke(Colorオブジェクト)
塗りつぶしの色変更はsetFill(Colorオブジェクト)で指定できます。
private void draw() {
g.setStroke(Color.RED);//線を赤に変更
g.setFill(Color.BLUE);//塗りつぶしを青に変更
}
Colorクラスはawtと間違えないようにしてください。
線の描画
線の描画はstrokeLineメソッドかstrokePolylineを使用します。
strokeLine(始点X座標,始点Y座標,終点X座標,終点Y座標)と指定します。
private void draw() {
g.setStroke(Color.RED);
g.strokeLine(50, 100, 350, 200);
}

strokePolyline(x座標配列, y座標配列, 配列の長さ)と指定します。
private void draw() {
g.setStroke(Color.RED);
g.strokePolyline(new double[] {
50,100,350
}, new double[] {
150,200,150
}, 3);
}
SwingのdrawPolylineと同じですね。

矩形の描画
矩形の描画にはstrokeRect、fillRectを使用します。
引数はそれぞれ(x座標,y座標,横幅,縦幅)と指定します。
private void draw() {
g.setStroke(Color.RED);
g.strokeRect(250, 100, 100, 100);
g.setFill(Color.RED);
g.fillRect(50, 100, 100, 100);
}

楕円の描画
楕円を描画するにはstrokeOval、楕円の塗りつぶしを行うにはfillOvalを使用します。
引数はそれぞれ(x座標,y座標,横幅,縦幅)となります。
これはSwingと同じ仕様みたいですね。
private void draw() {
g.setStroke(Color.RED);
g.strokeOval(250, 100, 100, 100);
g.setFill(Color.RED);
g.fillOval(50, 100, 100, 100);
}

多角形の描画
多角形の描画をするにはstrokePolygon、fillPolygonメソッドを利用します。
引数はそれぞれ(x座標配列, y座標配列, 配列の長さ)
となります。こちらもSwingと同じ仕様みたいです。
private void draw() {
g.setFill(Color.RED);
g.fillPolygon(new double[] {
50,100,350
}, new double[] {
50,100,50
}, 3);
g.setStroke(Color.RED);
g.strokePolygon(new double[] {
50,100,350
}, new double[] {
150,200,150
}, 3);
}

文字列の描画
文字列を描画する場合はstrokeText、fillTextを使用します。
引数はそれぞれ(表示文字列,x座標,y座標)とします。
Swingでの描画と違い枠の描画もできるようになりました。
private void draw() {
g.setFont(new Font(50));
g.setStroke(Color.RED);
g.strokeText("テスト", 50, 100);
g.setFill(Color.RED);
g.fillText("テスト", 250, 100);
}

画像の描画
画像の表示をする場合はdrawImageを使用します。
引数はdrawImage(画像オブジェクト,x座標,y座標)とします。
private void draw() {
Image img = new Image(new File("latifa_dot.png").toURI().toString());
g.drawImage(img, 50, 50);
}

Swingの時と同じように拡大縮小や値を負の数にすることで反転させることができます。
drawImage(画像オブジェクト
, 画像のキャプチャ開始X座標, 画像のキャプチャ開始Y座標
, 画像のキャプチャ終了X座標, 画像のキャプチャ終了Y座標
, 描画先の開始X座標, 描画先の開始Y座標
, 描画先の終了X座標, 描画先の終了Y座標)
と指定します。Swingの場合とは引数の順番が変わっているため、騙されないようにしてください。
private void draw() {
Image img = new Image(new File("latifa_dot.png").toURI().toString());
g.drawImage(img, 0, 0, img.getWidth(), img.getHeight(), 350, 50, -300, 200);
}

パス機能を利用した描画
パスの描画はbeginPathで開始し、stroke、またはfillメソッドでパスを描きます。
beginPathでパスの開始
closePathでパスを始点に結合
moveToで描画位置の移動
lineToで線の描画
quadraticCurveToで二次ベジェ曲線
bezierCurveToで三次ベジェ曲線
他にもrectやSVGを取り込む機能も実装されているようです。
それではサンプルをご覧ください。
private void draw() {
g.beginPath();
g.moveTo(50, 50);
g.lineTo(100, 50);
g.quadraticCurveTo(300, 100, 100, 150);
g.lineTo(50, 150);
g.closePath();
g.fill();
}

透過処理
透過処理を実装するにはsetGlobalAlphaメソッドを使用します。
setGlobalAlpha(不透明度)と指定します。
不透明度は0.0~1.0の間で指定します。
private void draw() {
Image img = new Image(new File("latifa_dot.png").toURI().toString());
g.setGlobalAlpha(0.5);
g.drawImage(img, 50, 50);
}

Swingの時よりもかなり簡単ですね。
描画線を破線に変更
描画線を破線に変更するにはsetLineDashesを利用します。
setLineDashes(描画の長さ、または描画感覚…)
と指定します。
可変引数になっており、描画の長さ、描画感覚の長さが交互に指定され、指定した順番で適用されます。
private void draw() {
g.setLineDashes(5);
g.strokeLine(50, 50, 350, 250);
}

描画線の太さを変更
線の太さはsetLineWidthメソッドで変更できます。
setLineWidth(太さ)と指定します。
private void draw() {
g.setLineWidth(5);
g.strokeLine(50, 50, 350, 250);
}

グラデーションの適用
色変更メソッドにグラデーションオブジェクトを渡すことで適用できます。
ここでは線形グラデーションを利用します。
private void draw() {
LinearGradient lg = new LinearGradient(0, 0, 400, 300, false, CycleMethod.NO_CYCLE
,new Stop(0, Color.RED)
,new Stop(0.5, Color.BLUE)
,new Stop(1, Color.GREEN));
g.setFill(lg);
g.fillRect(0, 0, 400, 300);
}

アフィン変換
アフィン変換を行うにはAffineクラスをインスタンス化します。
Affineクラスには
appendScaleで拡大縮小
appendTranslation平行移動
appendShear傾斜
appendRotation回転
setToIdentity元に戻す
等が使用でき、これらで回転などを実装できます。
ここでは最も重要そうな回転のサンプルをご覧ください。
appendRotation(回転角度,中心点X,中心点Y)と指定します。
private void draw() {
Image img = new Image(new File("latifa_dot.png").toURI().toString());
Affine af = new Affine();
//回転させる
af.appendRotation(150, 80, 80);
g.setTransform(af);
g.drawImage(img, 50, 50);
//元に戻す
af.setToIdentity();
g.setTransform(af);
g.drawImage(img, 250, 50);
}

ちなみにAffineクラスを使用せずともrotateメソッドやtranslateメソッドがGraphicsContextに定義されているため、そちらを使用しても構いません。
今回は中心点を指定したいのでクラスを利用しました。
エフェクト効果の適用
うれしいことにエフェクトの適用も可能です。これでぼかしなどのフィルタ機能が使えます。
setEffect(エフェクトオブジェクト)で以降の描画にエフェクトが適用されます。
applyEffect(エフェクトオブジェクト)で現在描画済みのデータに対してエフェクトを適用します。
ここでは画像に対して明度を変更してみたいと思います。
private void draw() {
//わかりやすいようにグレーに変更
g.setFill(Color.GRAY);
g.fillRect(0, 0, 400, 300);
Image img = new Image(new File("latifa_dot.png").toURI().toString());
//色変換エフェクト
ColorAdjust colorAdjust = new ColorAdjust();
//明度を最小に下げる
colorAdjust.setBrightness(-1.0);
g.setEffect(colorAdjust);
g.drawImage(img, 50, 50);
//明度を最大まで上げる
colorAdjust.setBrightness(1.0);
g.setEffect(colorAdjust);
g.drawImage(img, 200, 50);
//もとに戻す
g.setEffect(null);
g.drawImage(img, 180, 150);
}

エフェクトのサンプルは下記の記事でも紹介しています。











ディスカッション
コメント一覧
まだ、コメントがありません