JavaFXでマウスホイールを使ってのズームインズームアウトは可能か?
Twitter で面白そうなネタを見つけたので JavaFX で可能か?
どうすればいいのか?
MouseWheelEventクラスってあったけ?と疑問に思いつぶやいたところお忙しい中 @skrb さんから可能だと教えてもらいました。
もともとは「MouseWheelEventクラスってあったけ?」と大ボケをかましていたので話にならないのですが・・・ (^_^;)
JavaFXでマウスホイールを使ってのズームインズームアウトは可能か?スクロールペインのバーをCSSなりで見えなくするんだろうか? MouseWheelEventクラスってあったけ?なんか面白そう。
— Yucchi (@Yucchi_jp) 2014, 11月 26
そこで ImageView で試してみました。
ちなみに ScrollEvent を使うので MouseWheelEventクラスなんてものは存在しません。
ただし、タッチインタフェースのジェスチャーにも対応しているとのことで先月頭を悩ませた問題が再び襲ってくるのかと一抹の不安が脳裏を横切りました。
これも後で試してみます。
まずは ImageView で!
TilePane にImageView を二つ並べてマウスホイールで左だけ拡大縮小できるようにしてみました。
Ctrl キー、 Shift キーそれぞれを押しながらホイール操作した場合は拡大縮小比率を変更するようなおまけも付加しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
package jp.yucchi.mousewheelevent; import java.util.stream.IntStream; import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.image.ImageView; import javafx.scene.layout.TilePane; import javafx.stage.Stage; public class MouseWheelEvent extends Application { private final boolean DEBUG = true; private double scale = 1.0; @Override public void start(Stage primaryStage) { TilePane tiles = new TilePane(); tiles.setPadding(new Insets(5, 0, 5, 0)); tiles.setVgap(5); tiles.setPrefColumns(2); tiles.setSnapToPixel(false); ImageView imageView[] = new ImageView[2]; IntStream.range(0, imageView.length) .forEach(i -> { imageView[i] = new ImageView(this.getClass().getResource("duke_" + i + ".jpg").toExternalForm()); tiles.getChildren().add(imageView[i]); }); Scene scene = new Scene(tiles, 1_000, 600); primaryStage.setScene(scene); tiles.prefTileWidthProperty().bind(scene.widthProperty().divide(2)); tiles.prefTileHeightProperty().bind(scene.heightProperty()); tiles.setStyle("-fx-background-color: aliceblue"); imageView[0].setOnScroll(sc -> { double x = sc.getX(); double y = sc.getY(); double scaleBase = sc.isControlDown() ? 10.0 : sc.isShiftDown() ? 5.0 : 1.0; scale += (sc.getDeltaY() / 1_000) * scaleBase; if (DEBUG) { System.out.println(sc.getDeltaX() + " : " + sc.getDeltaY()); System.out.println(sc.isControlDown() + " : " + sc.isShiftDown()); System.out.println(scaleBase); System.out.println(scale); } imageView[0].setScaleX(scale); imageView[0].setScaleY(scale); }); primaryStage.setTitle("MouseWheelEvent"); primaryStage.setScene(scene); primaryStage.show(); } /** * @param args the command line arguments */ public static void main(String[] args) { launch(args); } } |
このプログラムを実行すると下図のようにホイールで操作が可能となりました。
ただ、不可解なことに Shift キーを押しながらの処理が機能しませんでした。
Ctrl キーは問題なく機能します。
随分悩んだのですが違う PC で実行すると何の問題もない。
キーボードの設定か何かだろうか?
ちなみにスクロール量は OS 側のマウスの設定で変わってきます。
スクロールの向きも OS 側の設定になるようです。(当たり前か・・)
ではこれからが本題です。
BOX 3D オブジェクトを表示させるプログラムを組んでみます。
マウスドラッグで BOX 3D オブジェクトをグリグリさせるようにします。
そこで BOX 3D オブジェクト とカメラの距離をマウスホイールで変更させます。(カメラを移動)
ここで大きな問題が発生します。
今時の PC はタッチパネル対応の OS が搭載されてきてます。
JavaFX も Swing では無かったタッチインタフェースが標準で用意されています。
タッチドラッグによって BOX 3D オブジェクトをグリグリ動かすことができます。
つまり、マウスホイールだけでなくタッチによるスクロールまで発生してしまいます。
タッチによるスクロール、タッチによるドラッグ あれれ、これじゃグリグリしながら拡大縮小してしまうじゃん。
どうしたものか悩んだ末に次のようなプログラムを組みました。
これでいいのかどうかは解りません。
だってタッチインタフェース使った記事って少ないしましてや 3D となると尚更ですから。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
package jp.yucchi.mousewheelevent2; import javafx.application.Application; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; import javafx.scene.AmbientLight; import javafx.scene.Group; import javafx.scene.PerspectiveCamera; import javafx.scene.PointLight; import javafx.scene.Scene; import javafx.scene.SceneAntialiasing; import javafx.scene.image.Image; import javafx.scene.paint.Color; import javafx.scene.paint.PhongMaterial; import javafx.scene.shape.Box; import javafx.scene.shape.DrawMode; import javafx.scene.transform.Rotate; import javafx.stage.Stage; /** * * @author Yucchi */ public class MouseWheelEvent2 extends Application { private final DoubleProperty zPos = new SimpleDoubleProperty(-500.0d); private double anchorAngleX; private double anchorAngleY; private final DoubleProperty angleX = new SimpleDoubleProperty(30d); private final DoubleProperty angleY = new SimpleDoubleProperty(30d); private final DoubleProperty angleZ = new SimpleDoubleProperty(0d); private double anchorX; private double anchorY; private double scale = 1.0; @Override public void start(Stage primaryStage) { final Group root = new Group(); final Box box = new Box(100.0d, 100.0d, 100.0d); // フォンシェーディングを設定 final PhongMaterial boxMaterial = new PhongMaterial(); // DiffuseMap boxMaterial.setDiffuseMap( new Image(this.getClass().getResource("resources/duke_0.jpg").toExternalForm(), 8192 / 2d, 4092 / 2d, true, true ) ); boxMaterial.setSpecularColor(Color.BLUE); boxMaterial.setSpecularPower(3.0d); box.setMaterial(boxMaterial); box.setDrawMode(DrawMode.FILL); // 透視投影カメラ final PerspectiveCamera cam = new PerspectiveCamera(true); // Field of View cam.setFieldOfView(45.5d); // Clipping Planes cam.setNearClip(1.0d); cam.setFarClip(1_000_000.0d); // カメラを zPos 後退させる。( cam.setTranslateZ(zPos.doubleValue()); // アンビエントライト AmbientLight ambient = new AmbientLight(); ambient.setColor(Color.rgb(90, 90, 90, 0.6)); // ポイントライト PointLight point = new PointLight(); point.setColor(Color.WHITE); // ポイントライトを移動 point.setTranslateX(-1_800.0d); point.setTranslateY(-1_300.0d); point.setTranslateZ(-1_800.0d); root.getChildren().addAll(box, ambient, point); Scene scene = new Scene(root, 1_024, 768, true, SceneAntialiasing.BALANCED); scene.setFill(Color.BLACK); scene.setCamera(cam); Rotate xRotate; Rotate yRotate; Rotate zRotate; box.getTransforms().setAll( xRotate = new Rotate(0, Rotate.X_AXIS), yRotate = new Rotate(0, Rotate.Y_AXIS), zRotate = new Rotate(0, Rotate.Z_AXIS) ); xRotate.angleProperty().bind(angleX); yRotate.angleProperty().bind(angleY); zRotate.angleProperty().bind(angleZ); box.setOnMousePressed(event -> { anchorX = event.getSceneX(); anchorY = event.getSceneY(); anchorAngleX = angleX.get(); anchorAngleY = angleY.get(); }); box.setOnMouseDragged(event -> { angleX.set(anchorAngleX - (anchorY - event.getSceneY())); angleY.set(anchorAngleY + anchorX - event.getSceneX()); }); box.setOnScroll(sc -> { if (sc.getTouchCount() == 0 && !sc.isInertia()) { double x = sc.getX(); double y = sc.getY(); double scaleBase = sc.isControlDown() ? 10.0 : sc.isShiftDown() ? 5.0 : 1.0; scale += (sc.getDeltaY() / 1_000) * scaleBase; } cam.setTranslateZ(zPos.doubleValue() * scale); }); primaryStage.setTitle("MouseWheelEvent2"); primaryStage.setScene(scene); primaryStage.show(); } /** * @param args the command line arguments */ public static void main(String[] args) { launch(args); } } |
タッチカウントと慣性スクロールイベントで拡大縮小処理を行うかどうか判断させています。
box.setOnScroll(sc -> {
if (sc.getTouchCount() == 0 && !sc.isInertia()) {
double x = sc.getX();
double y = sc.getY();
double scaleBase = sc.isControlDown() ? 10.0 : sc.isShiftDown() ? 5.0 : 1.0;
scale += (sc.getDeltaY() / 1_000) * scaleBase;
}
cam.setTranslateZ(zPos.doubleValue() * scale);
});
この方法しか思いつかなかった。(>_<。)
正しい方法をご存じでしたら教えてください!
ちなみにこのプログラムの実行結果は次のようになります。
とりあえずマウスホイール使えてめでたし!めでたし!
JavaFX おもしろいね!
TAGS: JavaFX | 2014年11月28日7:10 PM | Comments : 2