Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Java FXグラフィックスとアニメーション入門(JJUG CCC 2015 Spring G-7)

Presentation of JavaFX graphics and animation programming for dummies, with example of an analog clock.

  • Be the first to comment

Java FXグラフィックスとアニメーション入門(JJUG CCC 2015 Spring G-7)

  1. 1. JavaFXグラフィックスと アニメーション入門 デスクトップにアナログ時計を表示しよう 高橋 徹(Java読書会BOF)
  2. 2. アジェンダ  目的  自己紹介  JavaFX駆け足紹介  アナログ時計の作り方  時計を描画するグラフィックス 3種類(画像、Java API、SVG)  針の動きをアニメーション
  3. 3. 本セッションの目的  JavaFXに興味あるが「敷居が高い」と 感じている人  JavaFXを使っているがグラフィックス やアニメーションを使ってない人  JavaFXプログラムの超基本構造  簡単な例としてアナログ時計を作成 を対象に を紹介します
  4. 4. 自己紹介  高橋 徹  Java読書会BOF代表 http://www.javareading.com/bof/  日本JavaFXユーザーグループ参加 https://groups.google.com/forum/#!forum/javafx-ja  ブログ等  ブログ http://d.hatena.ne.jp/torutk/  Twitter @boochnich
  5. 5. JavaFX駆け足紹介  Java標準搭載GUIライブラリ 種類 搭載年 搭載版 (Oracle JDK) 備考 AWT 1995年 JDK Alpha/Beta Swing 1998年 JDK 1.2 JavaFX 2011年 JDK 7u2 同梱形態 2014年 JDK 8
  6. 6. JavaFX駆け足紹介  JavaFXの提供機能  ボタン、ラベル、グラフ等のUI部品  2Dグラフィックス(ベクトル、画像)  3Dグラフィックス(ポリゴン)  エフェクト(特殊効果)  アニメーション  サウンド  動画  Web表示 「Java SE Development Kit 8u40 Demos and Samples」に含まれる Ensemble を動かしてみると一通り 確認できます。
  7. 7. JavaFX駆け足紹介  JavaFXの構成要素 1. Javaソースコード  JavaFX APIを呼ぶコードを記述 2. FXML(XML)  レイアウト・属性を記述 3. CSS  見栄えの記述
  8. 8. JavaFX駆け足紹介  JavaFXプログラムの配布形態 1. Javaデスクトップアプリケーション  javaコマンド  JARファイル 2. ブラウザーから起動(Web Start)  リンクからダウンロードおよび実行 3. Webページ内で実行  Webページを開くと実行(Applet) 4. ネイティブバンドル  EXE、MSI、RPM、DMG
  9. 9. JavaFX駆け足紹介  Hello worldを作成  ステップ1 空のウィンドウを表示  ステップ2 ウィンドウ内に文字列を表示
  10. 10. JavaFX駆け足紹介  Hello worldを作成(ステップ1) package hello; import javafx.application.Application; import javafx.stage.Stage; public class HelloJavaFx extends Application { @Override public void start(Stage stage) { stage.show(); } }
  11. 11. JavaFX駆け足紹介  Hello worldを作成(ステップ1) package hello; import javafx.application.Application; import javafx.stage.Stage; public class HelloJavaFx extends Application { @Override public void start(Stage stage) { stage.show(); } } 使うクラスは ・Application ・Stage の2つ
  12. 12. JavaFX駆け足紹介  Hello worldを作成(ステップ1) package hello; import javafx.application.Application; import javafx.stage.Stage; public class HelloJavaFx extends Application { @Override public void start(Stage stage) { stage.show(); } } Applicationクラスは、 JavaFXアプリケー ションの制御を司る。 Application抽象クラ スをサブクラス化し、 抽象メソッドstartを オーバーライドする
  13. 13. JavaFX駆け足紹介  Hello worldを作成(ステップ1) package hello; import javafx.application.Application; import javafx.stage.Stage; public class HelloJavaFx extends Application { @Override public void start(Stage stage) { stage.show(); } } Stageクラスは、トップ レベルウィンドウを表す。 startメソッドで受け取る Stageインスタンスはデ フォルト非表示であり、 showメソッドを呼んで 可視とする。
  14. 14. JavaFX駆け足紹介  Hello worldを作成(ステップ1) package hello; import javafx.application.Application; import javafx.stage.Stage; public class HelloJavaFx extends Application { @Override public void start(Stage stage) { stage.show(); } } mainメソッドが なくてもOK
  15. 15. JavaFX駆け足紹介  Hello world(ステップ1)の実演
  16. 16. JavaFX駆け足紹介  Hello worldを作成(ステップ2)  シーングラフの構築 Stage Scene Node Node… Node Node… シーングラフと 呼ぶツリー構造 Parent Node
  17. 17. JavaFX駆け足紹介 Stage Scene Parent Node  Hello worldを作成(ステップ2)  シーングラフの構築 今回構築する シーングラフ
  18. 18. JavaFX駆け足紹介 Stage Scene Group Label  Hello worldを作成(ステップ2)  シーングラフの構築 今回構築する シーングラフ
  19. 19. JavaFX駆け足紹介 Stage Scene Group Label  Hello worldを作成(ステップ2)  シーングラフの構築 Group root = new Group(); Label hello = new Label(...); root.getChildren().add(hello); Scene scene = new Scene(root); stage.setScene(scene);
  20. 20. JavaFX駆け足紹介 Stage Scene Group Label  Hello worldを作成(ステップ2)  シーングラフの構築 Group root = new Group(); Label hello = new Label(...); root.getChildren().add(hello); Scene scene = new Scene(root); stage.setScene(scene);
  21. 21. JavaFX駆け足紹介 Stage Scene Group Label  Hello worldを作成(ステップ2)  シーングラフの構築 Group root = new Group(); Label hello = new Label(...); root.getChildren().add(hello); Scene scene = new Scene(root); stage.setScene(scene);
  22. 22. JavaFX駆け足紹介 Stage Group Label  Hello worldを作成(ステップ2)  シーングラフの構築 Group root = new Group(); Label hello = new Label(...); root.getChildren().add(hello); Scene scene = new Scene(root); stage.setScene(scene); Scene
  23. 23. JavaFX駆け足紹介  Hello worldを作成(ステップ2)  ソースコード(1/2) package com.torutk.hellofx; import javafx.application.Application; import javafx.stage.Stage; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Label;
  24. 24. JavaFX駆け足紹介  Hello worldを作成(ステップ2)  ソースコード(2/2) public class HelloJavaFx extends Application { @Override public void start(Stage stage) { Group root = new Group(); Label helloLabel = new Label("Hello, JavaFX world"); root.getChildren().add(helloLabel); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } }
  25. 25. JavaFX駆け足紹介  Hello world(ステップ2)の実演
  26. 26. アナログ時計の作り方  きっかけ  Windows 7のガジェット 機能でアナログ時計を表 示  Windows 8でガジェット が廃止 ならば、自分で作ってみよう!
  27. 27. アナログ時計の作り方  アナログ時計の要素  時計を描画する  針を時間の経過に応じて動かす
  28. 28. アナログ時計の作り方  アナログ時計の要素  時計を描画する 時計盤 短針 長針 秒針 中心
  29. 29. アナログ時計の作り方  アナログ時計の要素  針を時間の経過に応じて動かす  短針: 12時間で1回転  長針: 60分間で1回転  秒針: 1分間(60秒間)で1回転
  30. 30. アナログ時計の作り方  時計の描画 画像を重ねて描画 JavaのAPI(2Dグラフィッ クス)で描画 SVGデータで描画
  31. 31. アナログ時計の作り方  画像を重ねて描画 時計盤: clockDial.png 短針 : clockHourHand.png 長針 : clockMinuteHand.png 秒針 : clockSecondsHand.png 中心 : clockCenterPoint.png
  32. 32. アナログ時計の作り方  画像を重ねて描画  シーングラフの構築 Stage Scene Node Node… Node Node… シーングラフ Parent Node
  33. 33. アナログ時計の作り方  画像を重ねて描画  シーングラフの構築 Stage Scene ImageView (時計盤) StackPane ImageView (短針) ImageView (長針) ImageView (秒針) ImageView (中心)
  34. 34. アナログ時計の作り方  画像を重ねて描画  ソースコード public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); }
  35. 35. アナログ時計の作り方  画像を重ねて描画  ソースコード public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); }
  36. 36. アナログ時計の作り方  画像を重ねて描画 public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } Stage Scene ImageView (時計盤) StackPane ImageView (短針) ImageView (長針) ImageView (秒針) ImageView (中心)
  37. 37. アナログ時計の作り方  画像を重ねて描画 public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } Stage Scene ImageView (時計盤) StackPane ImageView (短針) ImageView (長針) ImageView (秒針) ImageView (中心)
  38. 38. アナログ時計の作り方  画像を重ねて描画 public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } Stage Scene ImageView (時計盤) StackPane ImageView (短針) ImageView (長針) ImageView (秒針) ImageView (中心)
  39. 39. アナログ時計の作り方  画像を重ねて描画 public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } Stage Scene ImageView (時計盤) StackPane ImageView (短針) ImageView (長針) ImageView (秒針) ImageView (中心)
  40. 40. アナログ時計の作り方  画像を重ねて描画 public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } Stage Scene ImageView (時計盤) StackPane ImageView (短針) ImageView (長針) ImageView (秒針) ImageView (中心)
  41. 41. アナログ時計の作り方  画像を重ねて描画 public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } Stage Scene ImageView (時計盤) StackPane ImageView (短針) ImageView (長針) ImageView (秒針) ImageView (中心)
  42. 42. アナログ時計の作り方  画像を重ねて描画 public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } Stage Scene ImageView (時計盤) StackPane ImageView (短針) ImageView (長針) ImageView (秒針) ImageView (中心)
  43. 43. アナログ時計の作り方  画像を重ねて描画 public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } Stage Scene ImageView (時計盤) StackPane ImageView (短針) ImageView (長針) ImageView (秒針) ImageView (中心)
  44. 44. アナログ時計の作り方  画像を重ねて描画  ソースコード public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); }
  45. 45. アナログ時計の作り方  画像を重ねて描画  ソースコード public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } Scene StackPane
  46. 46. アナログ時計の作り方  画像を重ねて描画  ソースコード public void start(Stage stage) { StackPane root = new StackPane(); ImageView clockDial = new ImageView(new Image("file:clockDial.png")); ImageView hourHand = new ImageView(new Image("file:clockHourHand.png")); ImageView minuteHand = new ImageView(new Image("file:clockMinuteHand.png")); ImageView secondsHand = new ImageView(new Image("file:clockSecondsHand.png")); ImageView centerPoint = new ImageView(new Image("file:clockCenterPoint.png")); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondsHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } Stage Scene
  47. 47. アナログ時計の作り方  画像を重ねて描画の実演
  48. 48. アナログ時計の作り方  針を時間の経過に応じて動かす  短針: 12時間で1回転  長針: 60分間で1回転  秒針: 1分間(60秒間)で1回転  アニメーション  javafx.animation.RotateTransition Nodeをお手軽にアニメーションする便利クラ スTransitionのひとつで、回転を扱う Transitionでは物足りないアニメーション を作成するには、Timelineを使う
  49. 49. アナログ時計の作り方  針を動かすアニメーション  針の回転を表すRotateTransitionを生成 private RotateTransition createRotateTransition( Duration duration, Node node, double startAngle ) { RotateTransition rt = new RotateTransition(duration, node); rt.setFromAngle(startAngle); rt.setByAngle(360); rt.setCycleCount(Animation.INDEFINITE); rt.setInterpolator(Interpolator.LINEAR); return rt; }
  50. 50. アナログ時計の作り方  針を動かすアニメーション  針の回転を表すRotateTransitionを生成 private RotateTransition createRotateTransition( Duration duration, Node node, double startAngle ) { RotateTransition rt = new RotateTransition(duration, node); rt.setFromAngle(startAngle); rt.setByAngle(360); rt.setCycleCount(Animation.INDEFINITE); rt.setInterpolator(Interpolator.LINEAR); return rt; } ・1回転に要する時間(duration)、 ・回転するNode ・回転開始角度(startAngle) を指定し、RotateTransitionを生成する createRotateTransitionメソッド
  51. 51. アナログ時計の作り方  針を動かすアニメーション  針の回転を表すRotateTransitionを生成 private RotateTransition createRotateTransition( Duration duration, Node node, double startAngle ) { RotateTransition rt = new RotateTransition(duration, node); rt.setFromAngle(startAngle); rt.setByAngle(360); rt.setCycleCount(Animation.INDEFINITE); rt.setInterpolator(Interpolator.LINEAR); return rt; } 1回転に要する時間と 回転するNodeを指定し、 RotateTransitionをインスタンス化
  52. 52. アナログ時計の作り方  針を動かすアニメーション  針の回転を表すRotateTransitionを生成 private RotateTransition createRotateTransition( Duration duration, Node node, double startAngle ) { RotateTransition rt = new RotateTransition(duration, node); rt.setFromAngle(startAngle); rt.setByAngle(360); rt.setCycleCount(Animation.INDEFINITE); rt.setInterpolator(Interpolator.LINEAR); return rt; } RotateTransitionインスタンスに 回転開始角度を設定
  53. 53. アナログ時計の作り方  針を動かすアニメーション  針の回転を表すRotateTransitionを生成 private RotateTransition createRotateTransition( Duration duration, Node node, double startAngle ) { RotateTransition rt = new RotateTransition(duration, node); rt.setFromAngle(startAngle); rt.setByAngle(360); rt.setCycleCount(Animation.INDEFINITE); rt.setInterpolator(Interpolator.LINEAR); return rt; } RotateTransitionインスタンスに 回転角度を設定 (開始角度からの増分)
  54. 54. アナログ時計の作り方  針を動かすアニメーション private RotateTransition createRotateTransition( Duration duration, Node node, double startAngle ) { RotateTransition rt = new RotateTransition(duration, node); rt.setFromAngle(startAngle); rt.setByAngle(360); rt.setCycleCount(Animation.INDEFINITE); rt.setInterpolator(Interpolator.LINEAR); return rt; } x y 開始角度 (fromAngle) 回転角度 (byAngle)
  55. 55. アナログ時計の作り方  針を動かすアニメーション  針の回転を表すRotateTransitionを生成 private RotateTransition createRotateTransition( Duration duration, Node node, double startAngle ) { RotateTransition rt = new RotateTransition(duration, node); rt.setFromAngle(startAngle); rt.setByAngle(360); rt.setCycleCount(Animation.INDEFINITE); rt.setInterpolator(Interpolator.LINEAR); return rt; } RotateTransitionインスタンスに 繰り返し回数を設定 (INDEFINITE は無限)
  56. 56. アナログ時計の作り方  針を動かすアニメーション  針の回転を表すRotateTransitionを生成 private RotateTransition createRotateTransition( Duration duration, Node node, double startAngle ) { RotateTransition rt = new RotateTransition(duration, node); rt.setFromAngle(startAngle); rt.setByAngle(360); rt.setCycleCount(Animation.INDEFINITE); rt.setInterpolator(Interpolator.LINEAR); return rt; } RotateTransitionインスタンスに アニメーション加減速を設定 (LINEAR は等速)
  57. 57. アナログ時計の作り方  針を動かすアニメーション  秒針:1分間(60秒間)で1回転  createRotateTransitionメソッドを呼ぶ RotateTransition secondsHandTransition = createRotateTransition( Duration.seconds(60), secondsHand, getAngleOfSeconds(LocalTime.now()) ); secondsHandTransition.play(); private static double getAngleOfSeconds(LocalTime time) { return time.getSecond() * 360 / 60; }
  58. 58. アナログ時計の作り方  針を動かすアニメーション  秒針:1分間(60秒間)で1回転  createRotateTransitionメソッドを呼ぶ RotateTransition secondsHandTransition = createRotateTransition( Duration.seconds(60), secondsHand, getAngleOfSeconds(LocalTime.now()) ); secondsHandTransition.play(); private static double getAngleOfSeconds(LocalTime time) { return time.getSecond() * 360 / 60; } 1回転の期間を javafx.util.Duration で指定
  59. 59. アナログ時計の作り方  針を動かすアニメーション  秒針:1分間(60秒間)で1回転  createRotateTransitionメソッドを呼ぶ RotateTransition secondsHandTransition = createRotateTransition( Duration.seconds(60), secondsHand, getAngleOfSeconds(LocalTime.now()) ); secondsHandTransition.play(); private static double getAngleOfSeconds(LocalTime time) { return time.getSecond() * 360 / 60; } 回転するNodeを指定 (秒針のImageView)
  60. 60. アナログ時計の作り方  針を動かすアニメーション  秒針:1分間(60秒間)で1回転  createRotateTransitionメソッドを呼ぶ RotateTransition secondsHandTransition = createRotateTransition( Duration.seconds(60), secondsHand, getAngleOfSeconds(LocalTime.now()) ); secondsHandTransition.play(); private static double getAngleOfSeconds(LocalTime time) { return time.getSecond() * 360 / 60; } 現在時刻から秒針の回転開始角度を 計算
  61. 61. アナログ時計の作り方  針を動かすアニメーション  秒針:1分間(60秒間)で1回転  createRotateTransitionメソッドを呼ぶ RotateTransition secondsHandTransition = createRotateTransition( Duration.seconds(60), secondsHand, getAngleOfSeconds(LocalTime.now()) ); secondsHandTransition.play(); private static double getAngleOfSeconds(LocalTime time) { return time.getSecond() * 360 / 60; } アニメーションの開始
  62. 62. アナログ時計の作り方  針を動かすアニメーション  長針:60分間で1回転  createRotateTransitionメソッドを呼ぶ RotateTransition minuteTransition = createRotateTransition( Duration.minutes(60), minuteHand, getAngleOfMinute(LocalTime.now()) ); minuteTransition.play(); private static double getAngleOfMinute(LocalTime time) { return (time.getMinute() + time.getSecond() / 60d) * 360 / 60; } 1回転は60分間 現在時刻から短針の回転開始角度を計算 短針のImageView
  63. 63. アナログ時計の作り方  針を動かすアニメーション  短針:12時間で1回転  createRotateTransitionメソッドを呼ぶ RotateTransition hourTranslation = createRotateTransition( Duration.hours(12), hourHand, getAngleOfHour(LocalTime.now()) ); hourTranslation.play(); private static double getAngleOfHour(LocalTime time) { return (time.getHour() % 12 + time.getMinute() / 60d + time.getSecond() / (60d * 60d)) * 360 / 12; } 1回転は12時間 現在時刻から長針の回転開始角度を計算 長針のImageView
  64. 64. アナログ時計の作り方  画像を重ねて描画+針の回転の実演
  65. 65. アナログ時計の作り方  アナログ時計の描画方法 画像を重ねて描画 JavaのAPI(2Dグラフィッ クス)で描画 SVGデータで描画
  66. 66. アナログ時計の作り方  JavaのAPIで描画 時計盤: Circle, Line, RadialGradient, Rotate 短針 : 長針 : 秒針 : Line 中心 : Circle Path, MoveTo, LineTo
  67. 67. アナログ時計の作り方  JavaのAPIで描画  シーングラフの構築 Stage Scene Node Node… Node Node… シーングラフ Parent Node
  68. 68. アナログ時計の作り方  JavaのAPIで描画  シーングラフの構築 Stage Scene Pane (時計盤) StackPane Pane (短針) Pane (長針) Pane (秒針) Pane (中心)
  69. 69. アナログ時計の作り方  JavaのAPIで描画 public void start(Stage stage) { StackPane root = new StackPane(); Node clockDial = createClockDial(); Node hourHand = createHourHand(); Node minuteHand = createMinuteHand(); Node secondHand = createSecondHand(); Node centerPoint = createCenter(); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); }
  70. 70. アナログ時計の作り方  JavaのAPIで描画  シーングラフの構築 Stage Scene Pane (時計盤) StackPane Pane (短針) Pane (長針) Pane (秒針) Pane (中心)
  71. 71. アナログ時計の作り方  JavaのAPIで描画  シーングラフの構築 Circle Pane(時計盤) Group Line Line Line・・・ 60個のライン
  72. 72. アナログ時計の作り方  JavaのAPIで描画  時計盤(Circle) Node createCircle() { RadialGradient gradient = new RadialGradient( 0, 0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE, new Stop(0.8, Color.WHITE), new Stop(0.9, Color.BLACK), new Stop(0.95, Color.WHITE), new Stop(1.0, Color.BLACK) ); Circle circle = new Circle( 100, 100, 100, gradient ); return circle; } (0,0) (200,200) (100,100)
  73. 73. アナログ時計の作り方  JavaのAPIで描画  時計盤(Line) Node createTickMark(int n) { Line line; if (n % 5 == 0) { line = new Line(100, 100 * 0.12, 100, 100 * 0.2); } else { line = new Line(100, 100 * 0.15, 100, 100 * 0.16); } line.getTransforms().add(new Rotate(360 / 60 * n, 100, 100)); line.setStrokeWidth(2); return line; } 60個で1周する回転
  74. 74. アナログ時計の作り方  JavaのAPIで描画  時計盤(Group) Node createTickMarks() { Group tickMarkGroup = new Group(); List<Node> tickMarks = IntStream.range(0, 60) .mapToObj(this::createTickMark) .collect(toList()); tickMarkGroup.getChildren().addAll(tickMarks); return tickMarkGroup; }
  75. 75. アナログ時計の作り方  JavaのAPIで描画  短針/長針共通の描画 Node createHourOrMinuteHand( double stretchRelativeToRim, Color color ) { Path path = new Path( new MoveTo(100, 100), new LineTo(100 * 0.9, 100 * 0.9), new LineTo(100, stretchRelativeToRim), new LineTo(100 * 1.1, 100 * 0.9), new LineTo(100, 100) ); path.setFill(color); path.setStroke(Color.TRANSPARENT); return path; } ① ① ② ② ③ ③ ④ ④ ① 塗りつぶし
  76. 76. アナログ時計の作り方  JavaのAPIで描画  秒針 Node createSecondHand() { Pane pane = new Pane(); pane.setPrefSize(100 * 2, 100 * 2); Line line = new Line(100, 100 * 1.1, 100, 100 * 0.2); pane.getChildren().add(line); return pane; }
  77. 77. アナログ時計の作り方  JavaのAPIで描画の実演
  78. 78. アナログ時計の作り方  針を動かすアニメーション  画像を重ねて描画におけるアニメーショ ンと実装は同一
  79. 79. アナログ時計の作り方  アナログ時計の描画方法 画像を重ねて描画 JavaのAPI(2Dグラフィッ クス)で描画 SVGデータで描画
  80. 80. アナログ時計の作り方  SVGデータについて Scalable Vector Graphics  javafx.scene.shape.SVGPath SVG 1.1仕様書(2版)の8章 Paths 日本語訳 http://www.hcn.zaq.ne.jp/___/SVG11-2nd/paths.html
  81. 81. アナログ時計の作り方  SVGパスデータについて  MoveTo, LineTo  “M 100 100 L 90 90”  円弧  “A 100 100 0 1 1 99 0” MoveTo(100, 100); LineTo(90, 90) Circle(100, 100, 100) に近似
  82. 82. アナログ時計の作り方  SVGパスデータについて  円を定義するコマンドがない  “A 100 100 0 1 1 99 0” Circle(100, 100, 100) に近似
  83. 83. アナログ時計の作り方  SVGデータで描画 public void start(Stage stage) { StackPane root = new StackPane(); Node clockDial = createClockDial(); Node hourHand = createHourHand(); Node minuteHand = createMinuteHand(); Node secondHand = createSecondHand(); Node centerPoint = createCenter(); root.getChildren().addAll( clockDial, hourHand, minuteHand, secondHand, centerPoint ); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); }
  84. 84. アナログ時計の作り方  SVGデータで描画  時計盤(枠) Node createCircle() { RadialGradient gradient = new RadialGradient( 0, 0, 0.5, 0.5, 0.5, true, CycleMethod.NO_CYCLE, new Stop(0.8, Color.WHITE), new Stop(0.9, Color.BLACK), new Stop(0.95, Color.WHITE), new Stop(1.0, Color.BLACK) ); SVGPath svg = new SVGPath(); svg.setContent( “M 100 0 A 100 100 0 1 1 99 0” ); svg.setFill(gradient); return circle; (0,0) (200,200) (100,100) JavaのAPIで描画から変更した部分
  85. 85. アナログ時計の作り方  SVGデータで描画  時計盤(分刻み) Node createTickMarks() { SVGPath svg = new SVG(); svg.setContent( “M 100,12 L 100,20 M 108.9,15.5 ...” ); svg.setStroke(Color.BLACK); svg.setStrokeWidth(2); return svg; } 刻み1つ毎に M nn,nn L nn,nn を記述
  86. 86. アナログ時計の作り方  SVGデータで描画  短針の描画 Node createHourHand() { SVGPath svg = new SVGPath(); svg.setContent(“M 100,100 L 90,90, L 100,40 L 110,90 Z”); Pane pane = new Pane(svg); pane.setPrefSize(100*2, 100*2); return pane; }
  87. 87. アナログ時計の作り方  SVGデータで描画  長針の描画 Node createMinuteHand() { SVGPath svg = new SVGPath(); svg.setContent(“M 100,100 L 90,90, L 100,20 L 110,90 Z”); Pane pane = new Pane(svg); pane.setPrefSize(100*2, 100*2); return pane; }
  88. 88. アナログ時計の作り方  SVGデータで描画  秒針の描画 Node createSecondHand() { SVGPath svg = new SVGPath(); svg.setContent(“M 100,110 L 100,20”); svg.setStroke(Color.GLAY); Pane pane = new Pane(svg); pane.setPrefSize(100 * 2, 100 * 2); return pane; }
  89. 89. アナログ時計の作り方  SVGデータで描画の実演
  90. 90. アナログ時計の作り方  針を動かすアニメーション  画像を重ねて描画におけるアニメーショ ンと実装は同一
  91. 91. アナログ時計の作り方  ウィンドウ枠の非表示 public void start(Stage stage) { : Scene scene = new Scene(root, Color.TRANSPARENT); stage.initStyle(StageStyle.TRANSPARENT); : }
  92. 92. 最後に(1/2)  JavaFXの最小限のプログラムとして Hello worldを紹介  アナログ時計を題材に  時計のグラフィックス表示  3種類(画像を重ねる、Java API、SVG)  時計の針の回転をアニメーション  デモのコードは次からダウンロード https://github.com/torutk/jjugccc2015spring-javafx
  93. 93. 最後に(2/2)  本セッションの元ネタ http://www.torutk.com/projects/swe/wiki/JavaFX から、「JavaFXとアナログ時計」のリンクへ  Timelineを使った回転のアニメーション  SceneBuilderでSVGPathやRadialGradientの取 り扱い  マウスドラッグによる時計の移動  マウスホイール、ピンチ操作での時計の拡大縮小  ポップアップメニュー
  94. 94. Q&A
  95. 95. JavaFXアニメーション(補足)  JavaFX関連のスレッド  JavaFX Applicationスレッド  入力、pulse、ユーザータスクに応じて シーングラフを更新  renderコマンド作成  QuantumRenderスレッド  renderコマンドを実行し描画  JavaFX pulse  アニメーション中は、16msタイマーで発生(最大 60FPS) (参考)JavaOne SF 2014 CON2262 “Be in Control of Your JavaFX Mission”
  96. 96. JavaFXアニメーション(補足)  JavaFX pulse JavaFX Render Timer アニメーション処理、 シーングラフ更新、 renderコマンド作成 render実行、描画 16msタイマー
  97. 97. JavaFXアニメーション(補足)  JavaFX pulseをFlight Recorderで調査
  98. 98. JavaFXにmain不要  javafx.application.Applicationを継 承したクラスを指定してjavaコマン ドで起動するとき、mainメソッドが なくてもよい 「JavaFXアプリケーションクラスにmainメソッド がなくてもよい訳」 http://d.hatena.ne.jp/torutk/20150402/p1
  99. 99. ラスター v.s. ベクター  拡大縮小への対応  拡大時の表示具合 画像(ラスター)で実現 SVG(ベクター)で実現
  100. 100. JavaFXの主な特徴  Java API  FXMLとGUIデザインツール(Scene Builder)  WebView(WebKitベース)  Swing相互互換性  組込みUI部品とCSS  テーマ(Modena)  3Dグラフィックス  Canvas API  印刷API  リッチテキスト  マルチタッチ  Hi-DPI  ハードウェアアクセラレートグラフィックス  メディアエンジン  JRE内蔵アプリケーション配布

×