ボタンなどUIのパーツだけではなく、パスを引いて好きな形を描くことが出来る。AfterEffectsでいうと、マスクやシェイプを作成するときのイメージ。
各UIパーツのgraphicsプロパティからアクセスできるScriptUIGraphicsオブジェクト。ここに描画に関するツールが揃っている。それらを使ってパス図形、文字、画像を作成できる。パスは矩形、楕円、直線が使える。曲線は?って思った?曲線を書きたい時は短い直線を繋げていく。
単純に表示したい「だけ」ならグループ(Group)を用意すればよい。グループを適当なサイズで作成して、そこをキャンバスにして自由に描けばいい。(Groupの領域を超えて描画は出来ないよ)
となるとボタン(Button)にもgraphicsプロパティはあるわけで...やってみたくなるのが人の性。
まぁ好きな形を描くって言っても、好きな形のボタンが作れるとかそういうわけじゃないんでしょ?...え、まさかそんなこと...。できるんですか?
できるんです。
もとい、
できるはずなんです...!!😂
ボタンの領域内で自由に描画が出来る。この場合、AfterEffectsで用意されている黒いボタン、あれが描画されないだけであって、ボタンとしての位置・大きさ・機能は持っている。見た目が無くなるだけでボタンとしての機能は果たしているわけ。あとはボタンの領域内で、自由な見た目のデザインが可能というわけ。
var w = new Window('dialog{\
btn : Button{}\
}');
// ボタンクリック時の挙動
w.btn.onClick = function () {
alert("Click!!");
}
// onDrawを設定するとデフォルトのデザインが反映されなくなるが、ボタンなどの機能は果たす。この関数の中で自由にデザインをする。
w.btn.onDraw = function () {
var g = this.graphics;
var br = g.newBrush(g.BrushType.SOLID_COLOR, [1, 0, 0]);
g.newPath();
g.rectPath(0, 0, this.size.width, this.size.height);
g.fillPath(br);
}
w.show();
やってみればわかるが、onDraw関数を消せばいつものボタンが出てくるはず。
まぁただ、バグが多いのでバージョンによって見え方が変わったり挙動も不安定で困る。正直使いにくいのが実情。
「できるはず」と書いたのは、そういうこと。
画像のような表示ができるサンプルコード。後で何度も書いてるが、バグや不具合が多いので表示されたりされなかったり、なんだかもうイライラしてきたら諦めよう!
var w = new Window('dialog{ \
gr : Button{ preferredSize : [500,500] }\
}');
// onDrawを設定するとデフォルトのデザインが描画されなくなるが、機能は果たす。この中で自由なデザインをする。
w.gr.onDraw = function () {
var g = this.graphics;
// 先にペン/ブラシを作っておこっかな
var redBrush = g.newBrush(g.BrushType.SOLID_COLOR, [1, 0, 0]);
var greenBrush = g.newBrush(g.BrushType.SOLID_COLOR, [0, 1, 0]);
var redPen = g.newPen(g.PenType.SOLID_COLOR, [1, 0, 0], 5);
var orangePen = g.newPen(g.PenType.SOLID_COLOR, [1, .5, .3], 10);
var bluePen = g.newPen(g.PenType.SOLID_COLOR, [0, 0, 1], 2);
var greenPen = g.newPen(g.PenType.SOLID_COLOR, [0, 1, 0], 3);
var pinkPen = g.newPen(g.PenType.SOLID_COLOR, [1, 0, 1], 3);
var offset = [250, 250];
// 図形の数式
var fx = {
cardioid: {
x: function (i) { return 50 * (1 + Math.cos(i)) * Math.cos(i) },
y: function (i) { return 50 * (1 + Math.cos(i)) * Math.sin(i) }
},
astroid: {
x: function (i) { return 50 * Math.pow(Math.cos(i), 3) },
y: function (i) { return 50 * Math.pow(Math.sin(i), 3) }
},
Lissajous: {
x: function (i) { return 50 * Math.sin(5 * i); },
y: function (i) { return 50 * Math.sin(4 * i); }
},
involute: {
x: function (i) { return 5 * (Math.cos(i) + i * Math.sin(i)) },
y: function (i) { return 5 * (Math.sin(i) - i * Math.cos(i)) }
}
}
// カージオイド
var offset = [50, 80];
g.newPath();
g.moveTo(fx.cardioid.x(0) + offset[0], fx.cardioid.y(0) + offset[1]);
for (var i = 0; i <= 2 * Math.PI; i += Math.PI / 100) {
g.lineTo(fx.cardioid.x(i) + offset[0], fx.cardioid.y(i) + offset[1]);
}
g.strokePath(redPen);
// アステロイド
var offset = [360, 80];
g.newPath();
g.moveTo(fx.astroid.x(0) + offset[0], fx.astroid.y(0) + offset[1]);
for (var i = 0; i <= 2 * Math.PI; i += Math.PI / 100) {
g.lineTo(fx.astroid.x(i) + offset[0], fx.astroid.y(i) + offset[1]);
}
g.strokePath(greenPen);
// リサージュ
var offset = [100, 300];
g.newPath();
g.moveTo(fx.Lissajous.x(0) + offset[0], fx.Lissajous.y(0) + offset[1]);
for (var i = 0; i <= 2 * Math.PI; i += Math.PI / 100) {
g.lineTo(fx.Lissajous.x(i) + offset[0], fx.Lissajous.y(i) + offset[1]);
}
g.strokePath(bluePen);
// インボリュート
var offset = [380, 300];
g.newPath();
g.moveTo(fx.involute.x(0) + offset[0], fx.involute.y(0) + offset[1]);
for (var i = 0; i <= 6 * Math.PI; i += Math.PI / 100) {
g.lineTo(fx.involute.x(i) + offset[0], fx.involute.y(i) + offset[1]);
}
g.strokePath(orangePen);
// 文字の表示
var font1 = ScriptUI.newFont("HG創英プレゼンスEB", ScriptUI.FontStyle.BOLD, 30);
g.drawString("カージオイド曲線", redPen, 0, 150, font1);
g.drawString("アステロイド曲線", greenPen, 250, 150, font1);
g.drawString("リサージュ図形", bluePen, 0, 400, font1);
g.drawString("インボリュート曲線", orangePen, 250, 400, font1);
}
w.show();
ターゲットのパーツのonDrawコールバックの中で書かないとエラーになるので注意。
パスを作成する
いらないパスが既に入っている場合は newPath() でクリアする
ellipsePath()、rectPath()、moveTo()、lineTo()、closePath()を使ってパスを作成する
線/塗りを適用する
使いたいペン/ブラシの設定を作成する
それを使って、strokePath/fillPathで線/塗りを適用する
以上1.2.を繰り返し
newFont()を使ってフォントの設定を作成する
それを使ってdrawString()を使って文字を描く
newImage()を使って画像の設定を作成する
それを使ってdrawImage()を使って画像を描く
以上を必要なだけ繰り返す、という感じ。
シェイプレイヤーやテキストレイヤーを想像してもらえれば、このあとの説明もすんなり理解できるかと。「パス」「線」「塗り」この3つのキーワードが重要。
ターゲットにしたいパーツ(controlObjとする)のgraphicsプロパティにアクセスする。図形の作成に必要なものは全てgraphicsの中にある。毎度アクセスするのは面倒なので、変数で取っておく。変数すらも面倒ならwith構文を使ってもいい。
変数に取るなら、
var g = controlObj.graphics;
with構文なら、
with (controlObj.graphics) {
// この中にいろいろ書いていく
}
といった感じ。
パスを作成して、線/塗り、パスを作成して、線/塗り...を繰り返していくわけだが。追加した全てのパスを変数でとっておいて、それら1つ1つに線と塗りを設定していく、というのも出来るがやっぱり面倒。同じ塗り/線設定の時はまとめて設定したいよね。
パスを作成するといつでもcurrentPath(=現在のパス)に追加されていく。ブラシやペンを適用するときにターゲットパスの指定を省略すればcurrentPathの中にあるパス全てが対象になる。まとめて設定できて便利。
ただ、ずっとcurrentPathに残ったままになっていると、その後に適用した別の塗り/線がどんどん追加されてしまう。必要な塗り/線を適用し終わったパスに関してはcurerntPathから外してやるほうがいいわけ。そのときに使う。
// カレントパスのリセット
controlObj.graphics.newPath();
これを実行すると、currentPathが空っぽになる。リセットするわけ。その後パスを追加していき、線/塗りを適用すると、リセットした後に追加したものだけに適用できるってわけ。
一番最初は空っぽなので使用しなくてもよいし、念の為やっといてもいい。
以下のメソッドを使用してパスを描く。この時点では何も見た目には現れない。シェイプレイヤーの「塗り」「線」がオフになっている状態と同じ。
現在地(currentPoint)を移動する。
controlObj.graphics.moveTo (x, y)
現在地(currentPoint)から指定した点(x, y)へ直線を引く。パスがcurrentPathに追加され、現在地(currentPoint)が(x, y)に移動する。ペンツールでマスクを作る時のイメージ。
controlObj.graphics.lineTo (x, y)
楕円形パスを作成する。パスがcurrentPathに追加され、現在地(currentPoint)が左上の座標(left, top)に移動する。楕円形ツールのイメージ。
controlObj.graphics.ellipsePath (left, top, width, height)
矩形パスを定義する。パスがcurrentPathに追加され、現在地(currentPoint)が左上の座標(left.top)に移動する。長方形ツールのイメージ。
controlObj.graphics.rectPath (left, top, width, height)
塗る・線を引くといっても、色や太さなど複数の情報が必要である。その情報をひとまとめにして「ブラシ」「ペン」として作っておく。
例えばシェイプレイヤーで「塗り 1」「線 1」というのはプロパティをまとめてグループ化しているだけであって、その中に「色」とか「太さ」とかいろいろプロパティがあるだろう。そのイメージ。newBrush、newPenというのは「塗り 1」「線 1」グループを作成しているのと同じ。その中の「色」「太さ」にあたるのが引数になっている「color」「lineWidth」である。簡単。ここでは設定を作成しているだけなので、適用はまだされない。
// 塗り
controlObj.graphics.newBurush(type, color)
// 線
controlObj.graphics.newPen(type, color, lineWidth)
type
下の項目「ブラシ/ペンの種類」に書いてあるどれかを入れる。「好きな色を指定する?それともアプリケーションで用意されている色を使う?」という指示をここで与える。
color
色の指定。先程のtypeにSOLID_COLORを入れた時は[R,G,B,A]の配列で与える。それぞれの値は 0.0から1.0まで。THEME_COLORの場合はアプリケーション側で用意されている文字列を使って与える。どんなのが用意されているのか知らないから、具体的に何を書けばいいのかわからん。
lineWidth
線の太さ。ピクセル数で数値指定する。難しくない。言うまでもないが「塗り」の設定であるブラシには設定出来ない。
newBrush()やnewPen()で塗り/線を作成する時、type引数に与えるもの。
塗りが必要な時は
controlObj.graphics.BrushType.SOLID_COLOR
controlObj.graphics.BrushType.THEME_COLOR
線の時は
controlObj.graphics.PenType.SOLID_COLOR
controlObj.graphics.PenType.THEME_COLOR
のどちらかになるが、THEME_COLORは使い方がいまいち不明。通常はSOLID_COLORにする。
実態はただの数値なので
SOLID_COLOR == 0
THEME_COLOR == 1
である。数値を入れても大丈夫だが、ADOBEの気まぐれでバージョンによって数値が変わる可能性も無きにしもあらずなので、ちゃんと指定したほうが無難。
これまでにパスを描いて、ブラシ/ペンを用意してきた。そして以下のメソッドを使用することで、初めて適用される。
// 塗りを適用
controlObj.graphics.fillPath( brush [, path] )
// 線を適用
controlObj.graphics.strokePath( brush [, path] )
brushはScriptUIBrush/ScriptUIPenオブジェクト、つまり「2.2.a ブラシ/ペンを作成する」で作成したものを代入する。
pathは塗り/線のターゲットとなるパスをScriptUIPathオブジェクトで指定する。指定しない時はcurrentPathが適用される。
以下のサンプルを見れば特に難しくないのがわかる。
var w = new Window('dialog{ \
gr : Group{ preferredSize : [500,500] }\
}');
// onDrawを設定するとデフォルトのデザインが描画されなくなるが、機能は果たす。この中で自由なデザインをする。
w.gr.onDraw = function () {
var g = this.graphics;
// ペンとブラシ作成
var redBrush = g.newBrush(g.BrushType.SOLID_COLOR, [1, 0, 0]);
var greenBrush = g.newBrush(g.BrushType.SOLID_COLOR, [0, 1, 0]);
var yellowBrush = g.newBrush(g.BrushType.SOLID_COLOR, [1, 1, 0]);
var redPen = g.newPen(g.PenType.SOLID_COLOR, [1, 0, 0], 5);
var orangePen = g.newPen(g.PenType.SOLID_COLOR, [1, .5, .3], 10);
var bluePen = g.newPen(g.PenType.SOLID_COLOR, [0, 0, 1], 2);
var greenPen = g.newPen(g.PenType.SOLID_COLOR, [0, 1, 0], 3);
var pinkPen = g.newPen(g.PenType.SOLID_COLOR, [1, 0, 1], 3);
// 赤い矩形と楕円作成
g.newPath();
g.rectPath(300, 40, 100, 100);
g.ellipsePath(300, 40, 100, 100);
g.fillPath(redBrush);
// g.strokePath( bluePen );
// 緑の三角形作成
g.newPath();
g.moveTo(50, 250);
g.lineTo(200, 200);
g.lineTo(100, 100)
g.closePath();
g.fillPath(greenBrush);
// g.strokePath( orangePen ); //CS6はここを有効にすると、描画がおかしくなる
// 黄色の砂時計型を作成
g.newPath();
g.moveTo(300, 300);
g.lineTo(350, 350);
g.lineTo(100, 400);
g.lineTo(200, 450);
g.fillPath(yellowBrush);
g.strokePath(redPen);
}
w.show();
線/塗りまわりがバグだらけなのでバージョンによって見た目に差がでることを承知で使う。
CS6
線と塗りの適用順が反映されない。常に線が上。
線の角が丸くなる
fillPathを使った時点で「fillPathより前にstrokePathを使用している」が成立していると、何故かclosePathと最後に使ったstrokePathがfillPathの直後に実行される。その後strokePathすると線が2重になる。塗りだけにしたいパスは、strokePathを使うよりも先に済ましておくほうがよい。
パスが重なった部分は中抜される
描画した部分だけがクリックできる
CC2019
線と塗りの適用順が反映される。後から実行したほうが上。
線の角が維持される
パスを重ねても中抜きが出来ない
領域のどこでもクリックできる
var w = new Window('dialog{\
gr : Group{ preferredSize : [300,300] }\
}');
w.gr.onDraw = function () {
//先にペン/ブラシを作っておこっかな
var redBrush = g.newBrush(g.BrushType.SOLID_COLOR, [1, 0, 0]);
var greenBrush = g.newBrush(g.BrushType.SOLID_COLOR, [0, 1, 0]);
var orangePen = g.newPen(g.PenType.SOLID_COLOR, [1, .5, .3], 10);
var bluePen = g.newPen(g.PenType.SOLID_COLOR, [0, 0, 1, 1], 1);
g.newPath();
g.rectPath(50, 10, 150, 50);
g.strokePath(orangePen);
g.fillPath(greenBrush);
g.newPath();
g.ellipsePath(50, 100, 150, 150);
g.strokePath(bluePen); //CS6でstrokePathのコメントアウトすると何故かorangePenが適用される
g.fillPath(redBrush);
}
w.show();
まず、テキストを表示させるのに必要なフォントの設定を作成する。新規のScriptUIFontオブジェクトを作成するには次のようにする。graphicsからのアクセスではないので注意。フォント設定のオブジェクトが返ってくるので変数で取っておく。
ScriptUI.newFont(name, style, size);
name
フォント名を文字列で。CS6だと無視されてゴシックになる。まじ使えねー。
style
太字とかのフォントスタイル指定する。文字列か次のものどちらかを使って指定する。
ScriptUI.FontStyle.REGULAR - 普通
ScriptUI.FontStyle.BOLD - 太字
ScriptUI.FontStyle.ITALIC - 斜体
ScriptUI.FontStyle.BOLDITALIC - 太字斜体
size
フォントサイズを数値で。
図形だけでなく、文字も表示できる。StaticTextみたいなもん。
controlObj.graphics.drawString(text, pen, x, y, font);
text
表示したいテキスト
pen
ペンの設定。「2.2.a ブラシ/ペンを作成する」で作成したペンオブジェクトを代入する。
x, y
左上の座標
font
「3.1.フォントの設定を作成する」で作成したフォント設定を代入する。
戻り値は、undefined。
指定したフォントを使用した場合、テキストが実際どれだけの幅、高さを取るのか調べることが出来る。
controlObj.graphics.measureString(text, font[, boundingWidth]);
text
表示したいテキスト
font
「3.1.フォントの設定を作成する」で作成したフォント設定を代入する。
boundingWidth
許可する最大幅を指定する。ここを超えると次の行に表示されることになる。
戻り値は、Dimensionオブジェクト( [幅,高さ] の入った配列)。
画像も同様に、表示させる前に先にいろいろ設定したオブジェクトを作成しておく。1つのオブジェクトに4つの画像をひと纏めに出来る。これはパーツの状態によって画像を切り替えるためである。関係ない4つの画像を1つにまとめられるということではない。
画像設定のオブジェクトが返ってくるので変数で取っておく。
ScriptUI.newImage(normal, disabled, pressed, rollover);
normal
通常時に使用される画像のパス。
disabled
パーツが無効(enabled=false)になったときに使用される画像のパス。
pressed
クリック時(マウスボタンを押し下げた時)に使用される画像のパス。
rollover
マウスカーソルが被さった時に使用する画像のパス。
画像を表示する。
controlObj.graphics.drawImage(image, left, top[, width, height]);
image
「4.1.画像の設定を作成する」で作成した画像設定を代入する。
left, top
左と上の座標。
width, height
幅と高さ。指定しないと元の画像のサイズになる。指定すると、画像はそのサイズに伸縮する。
戻り値は、undefined。
onDrawを設定するともともとのボタンなどのデザインは表示されなくなるが、以下の一文を入れておくと、デフォルトのデザインも描画される。他の描画よりも最初に書いておくといいっぽい?
controlObj.graphics.drawOSControl();
動作は不安定。ESTK上では動いたが、AEではバージョンによって挙動がばらばら。実用に耐えない。
UIパーツをデザインした後、フォーカスリングの位置・大きさも決めれる。あんまいらない。WindowsだとESTKで実行した時に破線で表示される。AfterEffectsもっていくと関係ない。だからいらない。
controlObj.graphics.drawFocusRing(left, top, width, height);
カスタムエレメントというのがある。デフォルトで描画されるビジュアルは何も無いが、ボタンなどのパーツとして機能するもの......もとい、するはずのもの。
正しい使い方がいまいちわからんけど、一応説明しとく。
Custom{ type: TYPE }
TYPE
種類を文字列で与える
customBoundedValue:プログレスバー、スライダー、スクロールバーのように、値(value)と最大・最小値とを持つコントロール
customButton:ボタン、アイコンボタン、チェックボックスなどのように使えるコントロール
customView:画像表示のように、表示するだけのコントロール
これはaddでは追加できないので文字列方式で与える。
ググればいろんな式が出てくる。「分からない!」じゃなくて自分で調べる。自分で調べることのできない人間は成長しない。