bindの謎 ふたたび
JavaFXでグラデーションを使ったアニメーションを作ろうとしてbindの罠にはまった.こんなアニメーションをつくりたくて,LinearGradationのオフセットを制御して実現しようとしたのだが,どうにもうまく行かない.いろいろ調べてちょっと分かったので書いておく.作りたかったアニメーションはこんなやつ.床屋の看板 (^_^).
http://video.google.com/videoplay?docid=7868435584785123309
bindの位置と挙動
Pointという属性xを持つクラスをつくり,属性xに x0をbindすることを考える.
public class Point { attribute x: Number; } var x0:Number = 10.0;
この場合,次の3通りの書き方が考えられる.
var p0:Point = bind Point{x: x0 }; var p1:Point = bind Point{x: bind x0 }; var p2:Point = Point{x: bind x0 };
x0を変更して,それぞれの属性xを参照してみると,どの書き方でもちゃんと更新されていることが確認できる.
x0 = 20.0; System.out.println(p0.x); System.out.println(p1.x); System.out.println(p2.x);
とやると
20.0 20.0 20.0
となる.
なので,一見どの書き方でも挙動は同じであるように思えてしまう.
実は挙動が違う
実際に起こっていることは全然違っていたりする.x0の変更前後でPointオブジェクト本体を書き出してみる.toStringメソッドを定義していないので,オブジェクトのポインタが書き出される.
System.out.println(p0); System.out.println(p1); System.out.println(p2); x0 = 20.0 System.out.println(""); System.out.println(p0); System.out.println(p1); System.out.println(p2);
結果は以下のようになる.
mytest.bindTest$Point@215f7107 <--- mytest.bindTest$Point@f593af mytest.bindTest$Point@7ab2c6a6 mytest.bindTest$Point@6f0ffb38 <--- mytest.bindTest$Point@f593af mytest.bindTest$Point@7ab2c6a6
p0だけはオブジェクト本体が更新されている.つまり,別のオブジェクトが作られ,古いのは破棄されているのだ.この動作は,オブジェクトがどんどん使い捨てられてしまうので,あまりのぞましくない.しかし,描画オブジェクトのグラデーションや,フォントを変更する際には,オブジェクト本体を更新しなければ,変更が描画に反映されないようなのだ.これが,私がグラデーションをつかったアニメーションではまった原因.
ややこしいのは,RectangleやらLineやらの場合は,オブジェクト本体を更新する必要は無い,ということ.なんか,納得いかないが,javafx.scene.NodeのサブクラスはOKということなのか?
まとめ
var p0:Point = bind Point{x: x0 }; <- オブジェクト書き変わる var p1:Point = bind Point{x: bind x0 }; <- 変わらない var p2:Point = Point{x: bind x0 }; <- 変わらない
p1 の書き方で変わらないというのがなんか納得いかないのだけど,..
グラデーションによるアニメーション
グラデーションで作ったアニメーションのサンプルを.グラデーションのなかのオフセットを書き換えることでアニメーションさせている.このとき,一フレームごとにLinearGradationのObjectをガンガン使い捨てているわけだ.なんだかなあ.いいんだろうか..
ソースコード
package mytest; import javafx.scene.*; import javafx.scene.paint.*; import javafx.scene.geometry.*; import javafx.input.*; import javafx.scene.transform.*; import javafx.scene.text.*; import javafx.ext.swing.*; import java.lang.*; import javafx.animation.Timeline; import javafx.animation.KeyFrame; import javafx.animation.Interpolator; /** * @author nakada */ public class Barber extends CustomNode { attribute x: Integer; attribute y: Integer; attribute len: Number; attribute width: Number; attribute baseColor1: Color; attribute baseColor2: Color; attribute offset: Number = 0.0; attribute animate = Timeline { repeatCount: Timeline.INDEFINITE keyFrames : [ at (0s) {offset => 0.0}, at (1s) {offset => 30.0 tween Interpolator.LINEAR} ] }; function start() { animate.start(); } function create(): Node { return Rectangle { x: bind x y: bind y width: bind width height: bind len arcWidth: bind width; arcHeight: bind width; fill: bind LinearGradient { startX: offset startY: offset endX: 30.0 + offset endY: 30.0 + offset proportional: false cycleMethod: CycleMethod.REPEAT; stops: [ Stop { offset: 0.0 color: Color.WHITE }, Stop { offset: 0.25 color: baseColor1 }, Stop { offset: 0.5 color: Color.WHITE }, Stop { offset: 0.75 color: baseColor2 }, Stop { offset: 1.0 color: Color.WHITE } ] }; } } } var f = SwingFrame { content: Canvas { content: [ ] } background: Color.WHITE visible: true width: 230 height: 500 closeAction: function() { System.exit(0); } } var barber = Barber { x: 100 y: 100 len: 300 width: 30 baseColor1: Color.RED baseColor2: Color.BLUE }; insert barber into (f.content as Canvas).content; barber.start();