カーソルを追いかけるイメージ その 2

昨日のプログラムはどうも納得がいきません。ちょっと動きが悪い。マウスを動かさないでいる時に、イメージが少しずつマウスカーソルに重なっていく方がいいですね。

なので、今日はイベントではなく、周期的にマウスカーソルの位置を取得して、それをもとにイメージを描画する方法でやってみました。

マウスカーソルの位置を取得するのは JavaFX にはなさそうなので、Java の AWT の MouseInfo を使っています。ただ、MouseInfo でマウスカーソルの位置を取得するのは、サンドボックスではできません。だから、この方法を使ったアプリケーションを Java Web StartApplet にした場合は署名が必要です。

周期的に位置を取得するのは Timeline でやってます。

var timeline = Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames : [
        KeyFrame {
            time : 50ms
            
            action: function() {                
                var point:PointerInfo = MouseInfo.getPointerInfo();
                var x: Number = point.getLocation().x;
                var y: Number = point.getLocation().y;
                
                x = x - frame.x - frame.window.getInsets().left;
                y = y - frame.y - frame.window.getInsets().top;
                
                insert x before previousX[0];
                delete previousX[4];
                
                insert y before previousY[0];
                delete previousY[4];
            }

        }
    ]
};

timeline.start();

こうすると、50ミリ秒ごとに action 関数がコールされます。

MouseInfo で取得できる位置はスクリーンの絶対座標なので、アプリケーションのローカル座標に変換しています。

Frame クラスの window アトリビュートは JavaFX の javafx.application.Window オブジェクトではなくて、AWT の java.awt.Window オブジェクトです。だから、Window クラスの getInsets とか呼べるわけです。

スクリーンショットは昨日と区別がつかないのでのせませんが、昨日のバージョンよりだんぜん動きがいいです。

一応、すべてのソースをのせておきます。

import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.input.MouseEvent;
import javafx.scene.geometry.Rectangle;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;

import java.awt.Dimension;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Toolkit;
import java.awt.geom.Point2D;
import java.lang.System;
import java.net.URL;
import javafx.scene.image.Image;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;

var cursor: Cursor  = Cursor {
    awtType: java.awt.Cursor.CUSTOM_CURSOR 
    awtCursor: {
        var toolkit:Toolkit = Toolkit.getDefaultToolkit();
        var image: java.awt.Image 
        = toolkit.getImage(new URL("{__DIR__}duke0.gif"));
        
        toolkit.createCustomCursor(image, new Point(8, 0), "DUKE");
    }
};

var previousX: Number[] = [-10000, -10000, -10000, -10000];
var previousY: Number[] = [-10000, -10000, -10000, -10000];

var images: ImageView[] = [
    ImageView {
        x: bind previousX[0]
        y: bind previousY[0]
        image: Image {
            url: "{__DIR__}duke1.gif"
        },
    },
    ImageView {    
        x: bind previousX[1]
        y: bind previousY[1]
        image: Image {
            url: "{__DIR__}duke2.gif"
        },
    },
    ImageView {    
        x: bind previousX[2]
        y: bind previousY[2]
        image: Image {
            url: "{__DIR__}duke3.gif"
        },
    },
    ImageView {    
        x: bind previousX[3]
        y: bind previousY[3]
        image: Image {
            url: "{__DIR__}duke4.gif"
        },
    },
];

var frame = Frame {
    title: "CursorSample"
    width: 200
    height: 200
    closeAction: function() { 
        java.lang.System.exit( 0 ); 
    }
    visible: true

    stage: Stage {
        content: [
            Rectangle {
                cursor: cursor
                x: 10, y: 10
                width: 160, height: 140
                fill: Color.AQUAMARINE
                
                onMouseExited: function(event: MouseEvent) {
                    for (images in images) {
                        images.visible = false;
                    } 
                }

                onMouseEntered: function(event: MouseEvent) {
                    for (images in images) {
                        images.visible = true;
                    } 
                }
            },
            images
        ]
    }
}

var timeline = Timeline {
    repeatCount: Timeline.INDEFINITE
    keyFrames : [
        KeyFrame {
            time : 50ms
            
            action: function() {                
                var point:PointerInfo = MouseInfo.getPointerInfo();
                var x: Number = point.getLocation().x;
                var y: Number = point.getLocation().y;
                
                x = x - frame.x - frame.window.getInsets().left;
                y = y - frame.y - frame.window.getInsets().top;
                
                insert x before previousX[0];
                delete previousX[4];
                
                insert y before previousY[0];
                delete previousY[4];
            }

        }
    ]
};

timeline.start();