ActionScriptでグラデーションの塗りを行う場合、graphics.beginGradientFill() あるいは graphics.lineGradientStyle() で塗りの指定を行うわけだけど、グラデーションの位置などは Matrix で指定する。この Matrix は、matrix.createGradientBox() メソッドで作成できるのだけど、イマイチ使いにくい感がある。なんか気持ち悪い。
てなわけで、グラデーション的な Matrix をもう少し分かりやすく作りたいなぁ、というエントリーです。
createGradientBox のイヤなところ
createGradientBox()メソッドは以下のように使いますね。
//幅、高さ、回転、左上の座標で指定 var mat:Matrix = new Matrix(); mat.createGradientBox( width, height, radian, x, y );
メソッドのAPIは問題ないように思えるんですが、結果としてつくられる行列をみると「?」な感じがる。例えば
mat.createGradientBox( 500, 100, 0, 300, 200 ); trace( mat ); //(a=0.30517578125, b=0, c=0, d=0.06103515625, tx=550, ty=250)
まず、a,d の値ですね。ここはスケーリングの値が入ってくるので、500,100 のような感じがするんですが、全然違う感じになってる。
これは、基準になる大きさが 1 でないということですね。つまり、この場合の「a」でいうと、
500 = 基準になる大きさ * 0.30517578125;
まぁ何かしら初期値があるんですね。ちなみに、beginGradientFill() で matrix を null で指定した場合、単位行列が入りそうだけど、だいたい createGradientBox( 200, 200, 0, -100, -100 ) 相当の行列が入るみたい。
次に、tx,ty の値。これは、指定した x,y に幅、及び高さの半分が足された値になるんですね。つまり、指定した矩形の中心の値が入るんです。これが気持ち悪い…初めから中心の値を入れさせたらよさそうなもんですが…。ヘルプにも「tx と ty の値は、width と height の半分だけオフセットされます。」とありますね。こっちの方が指定しにくい要因かと思います。
まとめると、グラデの矩形を指定しなくてはならないため、特に回転が入る場合など、回転前の矩形の状態を鑑みつつ、幅や高さ、左上座標なんかを考えるのが面倒いなぁ、という。あと、初期値が単位行列でねぇってこと。
グラデーション的単位行列
てなわけで、とりあえず単位行列的なものをつくっておけばあとあと楽でねぇかと考えるわけです。こんなの。
//幅、高さが1、中心が0,0 なグラデーション的単位行列 var mat:Matrix = new Matrix(); mat.createGradientBox( 1, 1, 0, -0.5, -0.5 ); trace( mat ); //(a=0.0006103515625, b=0, c=0, d=0.0006103515625, tx=0, ty=0)
こんとき、a,d の値はとりあえず無視です。サイズが 1.0 座標が 0.0 ってのが大事です。つまり、この行列から scale, rotate, translate をすればいい感じでグラデ行列がつくれるんでねぇかという話です。
Linear Gradient
まず、線形グラデーションで考えてみましょう。普通にグラフィックソフトで指定する場合、こっからここまで、といったエディットをしますよね?
この場合、サイズは p0 から p1 の距離で、回転は傾き、中心は p0,p1 の中点になります。スクリプト的にはこんな感じで。
var matrix:Matrix = new Matrix(); //とりあえず単位行列的な matrix.createGradientBox( 1, 1, 0, -0.5, -0.5 ); //線分のベクトル var vx:Number = x1 - x0; var vy:Number = y1 - y0; //線分の長さ(p0,p1の距離) var w:Number = Math.sqrt( vx*vx + vy*vy ); //傾き var r:Number = Math.atan2( vy, vx ); //中点 var cx:Number = (x0 + x1)/2; var cy:Number = (y0 + y1)/2; //あとはあててくだけ matrix.scale( w, 1 ); //線形なんで高さは 1 にしとく matrix.rotate( r ); matrix.translate( cx, cy );
この方法で、グラデ Matrix を指定したのが以下のような感じになります。白い点が、p0,p1 ですね。ドラッグってください。
どうですかね?
Linear Gradient 2
次は、点 p2 を足して skew 的なものを考えてみる。
ここから回転の何の考えると面倒なんですが、行列で考える場合、実はあんまり意識しなくてよかったりします。素直にベクトルをあてるだけでよかですね。「※Matrixによる変形のこと」
var matrix:Matrix = new Matrix(); //とりあえず単位行列的な matrix.createGradientBox( 1, 1, 0, -0.5, -0.5 ); //x軸ベクトル var vx0:Number = x1 - x0; var vy0:Number = y1 - y0; //y軸ベクトル var vx1:Number = x2 - x0; var vy1:Number = y2 - y0; //中点 var cx:Number = (x0 + x1)/2; var cy:Number = (y0 + y1)/2; matrix.concat( new Matrix( vx0, vy0, vx1, vy1, cx, cy ) );
サンプルはこんな感じ。
Radial Gradient
最後に放射です。
ここも楕円の何のかんの考えると面倒ですが、素直にベクトルあてとけばよかですね。中心が p0 なのと、ベクトルの長さが半径になるので倍にすると幅・高さになってよい感じです。
var matrix:Matrix = new Matrix(); //とりあえず単位行列的な matrix.createGradientBox( 1, 1, 0, -0.5, -0.5 ); //x軸半径ベクトル var vx0:Number = x1 - x0; var vy0:Number = y1 - y0; //y軸半径ベクトル var vx1:Number = x2 - x0; var vy1:Number = y2 - y0; //中心 var cx:Number = x0; var cy:Number = y0; matrix.concat( new Matrix( vx0*2, vy0*2, vx1*2, vy1*2, cx, cy ) );
サンプルはこんな感じ。
そんなわけで、グラデーションもとっつきやすくなるかなぁ、など思いますがどうでしょうか。