読者です 読者をやめる 読者になる 読者になる

曲線のアニメーション

JavaFX

このエントリーは、2013 年の JavaFX Advent Calendar の初日のエントリーです。

明日は tomo_taka01 さんです。

JavaFX で曲線というと CubicCurve か、QuadCurve です。これらのクラスをアニメーションさせることを考えてみます。

もちろん、トランジションでもいいのですが、PathTransition 以外はあまりおもしろくないので (おもしろくない = 簡単に使えるです)、今日は扱いません。

となると、Timeline か AnimationTimer でアニメーションさせます。せっかくだからグネグネ動くようなアニメーションがいいですね ^ ^;;

Timeline でも AnimationTimer でも、クラスのプロパティが定義してあれば、その値を時間で変化させるということが可能です。

CubicCurve などのクラスのプロパティといえば、端点と制御点です。これらのプロパティを変化させてあげれば、グネグネ動かすことも可能です!

ということで、ここでは制御点をアニメーションさせてみましょう。

ちなみに、制御点というのは端点の接線上にある点で、端点から離れていればいるほど曲線は接線に近づいていきます。

ITpro で以前、端点と制御点の関係を図に描いたので、それを参照してみてください。

まず、アニメーションさせる 2 本の CubicCurve を作っておきます。

        // ベジェ曲線 1本目
        final CubicCurve curve1 = new CubicCurve();
        curve1.setStartX(40.0); curve1.setStartY(100.0);
        curve1.setEndX(360.0); curve1.setEndY(100.0);
        curve1.setStroke(Color.BLACK);
        curve1.setStrokeWidth(10.0);
        curve1.setStrokeLineCap(StrokeLineCap.ROUND);
        curve1.setFill(null);
        parent.getChildren().add(curve1);
        
        // ベジェ曲線 2本目
        final CubicCurve curve2 = new CubicCurve();
        curve2.setStartX(40.0); curve2.setStartY(100.0);
        curve2.setEndX(360.0); curve2.setEndY(100.0);
        curve2.setStroke(Color.BLACK);
        curve2.setStrokeWidth(10.0);
        curve2.setStrokeLineCap(StrokeLineCap.ROUND);
        curve2.setFill(null);
        parent.getChildren().add(curve2);

アニメーションで制御点の値を変化させるので、この時点では設定していません。

では、制御点をアニメーションさせてみましょう!!

ここでは、AnimationTimer を使用して、制御点を円運動させてみます。まず、円運動させるためのサインとコサインを決めておきます。

        // ベジェ曲線の制御点を円運動させるアニメーション
        AnimationTimer timer = new AnimationTimer() {
            @Override
            public void handle(long t) {
                double sin1 = 150 * Math.sin(t/400_000_000.0);
                double cos1 = 150 * Math.cos(t/400_000_000.0);

                // もう一方の制御点は180度ずらす
                double sin2 = 150 * Math.sin(t/400_000_000.0 + Math.PI);
                double cos2 = 150 * Math.cos(t/400_000_000.0 + Math.PI);

端点の一方を sin1/cos1 にしたら、もう一方の端点を sin2/cos2 にすると 8 の字になります。2 つの端点をどちらも sin1/cos1 にすると楕円になります。もちろん、この場合、もう一方の CubicCurve を sin2/cos2 にします。

ここでは 8 の字にしてみます。

                double controlX11 = 40.0 + sin1; 
                double controlY11 = 100.0 + cos1;
                curve1.setControlX1(controlX11);
                curve1.setControlY1(controlY11);
                
                double controlX12 = 360.0 + sin2; 
                double controlY12 = 100.0 + cos2; 
                curve1.setControlX2(controlX12);
                curve1.setControlY2(controlY12);

(40, 100) と (360, 100) は端点の座標です。

もう一方の CubicCurve はこれと逆にします (同じだとくっついてしまうので)。

                double controlX21 = 40.0 + sin2; 
                double controlY21 = 100.0 + cos2;
                curve2.setControlX1(controlX21);
                curve2.setControlY1(controlY21);
                
                double controlX22 = 360.0 + sin1; 
                double controlY22 = 100.0 + cos1; 
                curve2.setControlX2(controlX22);
                curve2.setControlY2(controlY22);
            }
        };

後は AnimationTimer をスタートさせるだけです。

        timer.start();

これでグニグニ動きます。

これと同じ動きをトランジションでも作ることができます。それは円運動のアニメーションをするダミーのノードを作ります (このノードは表示させません)。円運動をさせるには PathAnimation で path に Circle を指定します。

そして、CubicCurve の端点の座標をこのノードの座標にバインドします。

こうすることで、トランジションでも同じ動きを実現できます。そこら辺も ITpro の連載で書いたので、参照してみてください。

ソースは gist にアップしてあります。


gist7729009