OpenLaszloでマルチデバイス対応RIAを作ろう

第7回アニメーション効果

今回はRIA(リッチインターネットアプリケーション)らしい演出を可能にする楽しいアニメーション効果について解説します。アニメーション効果はx(横位置⁠⁠、y(縦位置⁠⁠、width(幅⁠⁠、height(高さ⁠⁠、opacity(透明度)など数値で表せられる属性の値を変化させることで実現しています。タグは<animator>およびそれをひとまとめにして実行するための<animatorgroup>を使います

<animator>

何を(attribute⁠⁠、どの値から(from⁠⁠、どの値へ(to⁠⁠、どれぐらいの時間をかけて(duration)変化させるかという設定をするタグです。アニメーションの動作は、アプリ起動と同時に開始するか、あるいはボタンクリックなどのきっかけで開始するようにするかはstartedで指定します。

表1 <animator>の主な属性
属性内容
attribute変化させたい属性の名前。x、y,width、height、opacityなど。
from開始値。デフォルトは現在の属性値。
to終了値。
durationアニメーションさせる時間。単位はミリ秒。
startedアニメーションを自動開始するかどうか。デフォルトはtrueで自動開始。falseの場合は手動開始。
motionアニメーションの加速、減速の設定。設定値はlinear(一定速度⁠⁠、easein(開始時に加速⁠⁠、easeout(終了時に減速⁠⁠、easeboth(開始時に加速、終了時に減速⁠⁠。デフォルトはeaseboth。
relativetoで指定した値が初期値に対して相対的(true)か絶対的(false)かの指定。デフォルトはfalse。
repeat繰り返し数。repeat="Infinity"とすると永久に繰り返す。

アニメーションの開始、一時停止、停止をするためのメソッドがあります。

表2 <animator>の主なメソッド
メソッド内容
doStart()アニメーションを開始する。
pause()アニメーションの一時停止あるいは再開。
stop()アニメーションの停止。

基本セット(attribute,from,to,duration)と起動セット(started、doStart())

まずは<animator>の基本セットから。リスト1は赤い四角を表す<view>の中に<animator>を入れ子にしています。<animator>はデフォルトでは親タグに対して作用します。<animator>自身は画面に表示されませんがタグ自身が階層構造を持っているので、自分自身はthis、親タグはparentになります。逆に親タグの<view>から<animator>を指すときはその名前を指定します。

タグの記述位置などは前回記事で紹介した<handler>や<method>と使い方は似ていますが、階層構造の扱いが違うためthisやparentなどの意味が違うので注意してください。

リスト1を細かく解説すると、<animator>は、親タグのx属性値(attribute="x")を0から(from="0")200に(to="200")700ミリ秒(duration="700",=0.7秒)かけて手動開始で動かす、という設定になっています。親タグの<view>は、クリックされたら(onclick)自分の子タグであるのanmという名の<animator>(this.anm)を開始する(doStart()⁠⁠、という設定になっています。

started=falseなのでアプリ起動時には自動開始されず、doStart()実行により開始されます。started=falseとdoStart()は手動でアニメーション開始するためにセットで使います。

リスト1
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view width="50" height="50" bgcolor="red" onclick="this.anm.doStart()">
    <animator name="anm" attribute="x" from="0" to="200" duration="700" started="false" />
  </view>
</canvas>

リスト1 サンプル

リスト1はfrom="0"でしたが、<view>の元々のx値が0なのであってもなくても同じです。リスト2はfrom="100"にしました。青四角は最初x=0の位置に表示されていますが、クリックするとx=100の位置に瞬間移動してから動き出します。2回目からは毎回x=100から開始します。

リスト2
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view width="50" height="50" bgcolor="blue" onclick="this.anm.doStart()">
    <animator name="anm" attribute="x" from="100" to="200" duration="700" started="false" />
  </view>
</canvas>

リスト2 サンプル

リスト3は<view>の初期位置をx=200、<animator>でfrom=200およびto=0にして、右から左へ移動するようにした例です。サンプルの緑四角をクリックしてみてください。

リスト3
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view x="200" width="50" height="50" bgcolor="green" onclick="this.anm.doStart()">
    <animator name="anm" attribute="x" from="200" to="0" duration="700" started="false" />
  </view>
</canvas>

リスト3 サンプル

motion属性

motion属性はアニメーションの加速、減速の設定をします。設定値は下記の4種類で、デフォルトはeasebothです。

  • linear(一定速度)
  • easein(開始時に加速)
  • easeout(終了時に減速)
  • easeboth(開始時に加速、終了時に減速)…デフォルト

リスト4では、4つの<animator>でそれぞれ別のmotionを設定しました。ボタンを押すとそれぞれのmotion設定で動作します。サンプルのTESTボタンをクリックしてみてください。

リスト4
<canvas proxied="false" bgcolor="0xffffcc"> 
  <simplelayout />
  <button onclick="anm1.doStart();anm2.doStart();anm3.doStart();anm4.doStart();">TEST</button>
  <view width="50" height="50" bgcolor="red">
    <animator id="anm1" attribute="x" from="0" to="200" duration="3000" started="false"/>
  </view>
  <view width="50" height="50" bgcolor="blue">
    <animator id="anm3" attribute="x" from="0" to="200" duration="3000" started="false" motion="easein"/>
  </view>
  <view width="50" height="50" bgcolor="aqua">
    <animator id="anm2" attribute="x" from="0" to="200" duration="3000" started="false" motion="easeout"/>
  </view>
  <view width="50" height="50" bgcolor="green">
    <animator id="anm4" attribute="x" from="0" to="200" duration="3000" started="false" motion="linear"/>
  </view>
</canvas>

リスト4 サンプル

relative属性

relativeでは、to値が、初期値に対して絶対的(false)か相対的(true)かを指定します。デフォルトはfalseです。

何のことかというと、たとえばx属性についてto="30"なら、デフォルトのrelative=falseの場合はx=30に固定的に移動することになり、relative=trueの場合はxが30ずつ増えるということになります。

リスト5のサンプルの赤四角と青四角はrelativeがfalseかtrueの違いだけです。赤四角はクリックごとにx=30の位置に移動(=つまり見た目上は一回目のクリックで移動して終了 ですが、青四角はクリックするたびにxが30増加(右に移動していく)します。

リスト5
<canvas proxied="false" bgcolor="0xffffcc"> 
  <simplelayout spacing="5" />
  <view width="50" height="50" bgcolor="red" onclick="this.anm.doStart();">
    <animator name="anm" started="false" attribute="x" to="30" duration="300" />
  </view>
  <view width="50" height="50" bgcolor="blue" onclick="this.anm.doStart();">
    <animator name="anm" started="false" attribute="x" to="30" duration="300" relative="true"/>
  </view>
</canvas>

リスト5 サンプル

animate()

<animator>のスクリプト版でanimate()というメソッドがあります。文法はanimate(属性名,to値,duration値,relative値)になります。リスト5と全く同じ動作をするコードをanimate()で書くとリスト6のようになります。

リスト6
<canvas proxied="false" bgcolor="0xffffcc"> 
  <simplelayout spacing="5" />
  <view width="50" height="50" bgcolor="red" onclick="this.animate('x',30,300,false);" />
  <view width="50" height="50" bgcolor="blue" onclick="this.animate('x',30,300,true);" />
</canvas>

リスト6 サンプル

<animatorgrpup>

<animatorgrpup>を使うと、複数の<animator>をひとまとめにして順番あるいは同時に動作させることができます。

リスト7のように<animatorgroup>の中に<animator>を入れていきます。デフォルトでは上から順番に実行されていきます。サンプルの赤四角をクリックしてみてください。

リスト7
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view width="50" height="50" bgcolor="red" onclick="this.anm.doStart();">
    <animatorgroup name="anm" started="false">
      <animator attribute="x" to="200" duration="700" />
      <animator attribute="y" to="50" duration="700" />
      <animator attribute="x" from="200" to="0" duration="700" />
      <animator attribute="y" from="50" to="0" duration="700" />
    </animatorgroup>
  </view>
</canvas>

リスト7 サンプル

process属性

<animatorgroup>にprocess="simultaneous"をつけると、<animator>をすべて「同時に実行」します。デフォルトはsequentialで「順番に実行」です。

リスト8のサンプルの四角をそれぞれクリックしてみてください。xとy同時に変化させているので斜めに移動しますが、durationを微妙に変えると動作も変わります。

リスト8
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view width="50" height="50" bgcolor="red" onclick="this.anm.doStart();">
    <animatorgroup name="anm" started="false" process="simultaneous">
      <animator attribute="x" from="0" to="200" duration="900" />
      <animator attribute="y" from="0" to="100" duration="900" />
    </animatorgroup>
  </view>
  <view y="50" width="50" height="50" bgcolor="green" onclick="this.anm.doStart();">
    <animatorgroup name="anm" started="false" process="simultaneous">
      <animator attribute="x" from="0" to="200" duration="700" />
      <animator attribute="y" from="50" to="0" duration="900" />
    </animatorgroup>
  </view>
  <view y="100" width="50" height="50" bgcolor="blue" onclick="this.anm.doStart();">
    <animatorgroup name="anm" started="false" process="simultaneous">
      <animator attribute="x" from="0" to="200" duration="900" />
      <animator attribute="y" from="100" to="50" duration="700" />
    </animatorgroup>
  </view>
</canvas>

リスト8 サンプル

motion属性も変更すると簡単に面白い動きをさせることができます。

リスト9
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view width="50" height="50" bgcolor="red" onclick="this.anm.doStart();">
    <animatorgroup name="anm" started="false" process="simultaneous">
      <animator attribute="x" from="0" to="200" duration="1000" motion="linear"/>
      <animator attribute="y" from="0" to="100" duration="1000"  />
    </animatorgroup>
  </view>
</canvas>

リスト9 サンプル

<animatorgroup>の中に<animatorgroup>を入れ子にすることもできます。リスト10は、(1)x移動、(2)y移動、(3)xとyの同時移動、の例です。

リスト10
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view width="50" height="50" bgcolor="red" onclick="this.anm.doStart();">
    <animatorgroup name="anm" started="false">
      <animator attribute="x" to="200" duration="700" />
      <animator attribute="y" to="100" duration="700" />
      <animatorgroup process="simaultaneous">
        <animator attribute="x" from="200" to="0" duration="700" />
        <animator attribute="y" from="100" to="0" duration="700" />
      </animatorgroup>
    </animatorgroup>
  </view>
</canvas>

リスト10 サンプル

応用編

x,y以外での使用例を紹介します。

透明度を変化

透明度はopacity属性で指定します。値は0~1です。0が完全透明、1が不透明です。0.5だと半透明。この値を変化させることでフェードイン、フェードアウトなどの効果を実現できます。スライドショーなどでよく使う効果ですね。サンプルでは2回繰り返しています。間隔を短くすれば点滅にできます。

リスト11
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view width="50" height="50" bgcolor="red" onclick="this.anm.doStart();">
    <animatorgroup name="anm" started="false" repeat="2" >
      <animator attribute="opacity" to="0" duration="1000" />
      <animator attribute="opacity" to="1" duration="1000" />
    </animatorgroup>
  </view>
</canvas>

リスト11 サンプル

幅、高さの変化

width(幅)とheight(高さ)の変化によって、画面の表示・非表示を表現してみました。ついでにopacityでフェードイン・フェードアウトを追加し、ボタンの文字列も変えています。

リスト12
<canvas proxied="false" bgcolor="0xffffcc"> 
  <button>Open
    <handler name="onclick">
      if(v.width==0){
        anm1.doStart();
        this.setAttribute('text',"Close");
      }else{
        anm2.doStart();
        this.setAttribute('text',"Open");
      }
    </handler>
  </button>
  <view id="v" y="30" width="0" height="0" bgcolor="red">
    <animatorgroup id="anm1" started="false" process="simultaneous" duration="500" >
      <animator attribute="width" to="200" />
      <animator attribute="height" to="150" />
      <animator attribute="opacity" to="1" />
    </animatorgroup>
    <animatorgroup id="anm2" started="false" process="simultaneous" duration="500" >
      <animator attribute="width" to="0" />
      <animator attribute="height" to="0" />
      <animator attribute="opacity" to="0" />
    </animatorgroup>
  </view>
</canvas>

リスト12 サンプル

回転

回転はrotation属性を使います。rotationでは角度を指定しますので、360だと一回転になります。

リスト13は赤四角をクリックすると回転しますが、回転軸が左上になっています。これは基準点が赤四角にとってのx:y=0:0になっているからです。

リスト13
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view align="center" valign="middle" width="50" height="50" bgcolor="0xff0000" onclick="this.anm.doStart()">
    <animator name="anm" attribute="rotation" from="0" to="360" duration="1000" started="false" />
  </view>
</canvas>

リスト13 サンプル

リスト14は赤四角をクリックすると回転しますが、xoffsetとyoffsetで基準点をずらして回転軸を真ん中に持ってきました。

リスト14
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view align="center" valign="middle" xoffset="${this.width/2}" yoffset="${this.height/2}" width="50" height="50" bgcolor="0xff0000" onclick="this.anm.doStart()">
    <animator name="anm" attribute="rotation" from="0" to="360" duration="1000" started="false" />
  </view>
</canvas>

リスト14 サンプル

リスト15はリスト13と14をくっつけてそれぞれの回転軸の違いがわかるようにしました。グレー四角をクリックするとそれだけが、赤四角をクリックすると両方が回転します。

赤四角が回転するとrotation値が変化しますが、その値をグレー四角のほうでコンストレイントを使ってrotationに自動セット(rotation="${v.rotation}")しているためグレー四角も同時に回転するという仕掛けです。

リスト15
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view align="center" valign="middle" xoffset="${this.width/2}" yoffset="${this.height/2}" width="50" height="50" bgcolor="gray" onclick="this.anm.doStart()" rotation="${v.rotation}" >
    <animator name="anm" attribute="rotation" from="0" to="360" duration="1000" started="false" />
  </view>
  <view id="v" align="center" valign="middle" width="50" height="50" bgcolor="red" onclick="anm.doStart()">
    <animator id="anm" attribute="rotation" from="0" to="360" duration="1000" started="false" />
  </view>
</canvas>

リスト15 サンプル

円運動

三角関数を使った値の変化を利用するといろんな円運動をさせることができます。高度な技ですが面白いので紹介します。<attribute>というタグを使うと独自の属性(いわゆる変数みたいなものです)を作ることができ、その属性値を<animator>で変化させています。

リスト16
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view bgcolor="#ff0000" width="20" height="20" x="${r+(r*Math.cos(cnt))}" y="${r+(r*Math.sin(cnt))}">
    <attribute name="cnt"/>
    <attribute name="r" value="50"/>
    <animator attribute="cnt" from="0" to="$once{Math.PI * -2}" duration="1000" motion="linear" repeat="Infinity"/>
  </view>
</canvas>

リスト16 サンプル

リスト17は心電図みたいな動きをするサンプルです。

リスト17
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view bgcolor="#ff0000" width="20" height="20" x="0" y="${r+(r*Math.sin(cnt))}">
    <attribute name="cnt"/>
    <attribute name="r" value="50"/>
    <animator attribute="cnt" from="0" to="$once{Math.PI * -2}" duration="1000" motion="linear" repeat="Infinity"/>
    <animator attribute="x" from="0" to="${canvas.width}" duration="7000" motion="linear" repeat="Infinity"/>
  </view>
</canvas>

リスト17 サンプル

リスト18は楕円運動です。

リスト18
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view x="150" y="150">
    <view bgcolor="blue" width="20" height="20" 
      x="${(Math.cos(radAngle)*Math.cos(radDegree)*rLong) - (Math.sin(radAngle)*Math.sin(radDegree)*rShort)}"
      y="${(Math.cos(radAngle)*Math.sin(radDegree)*rLong) + (Math.sin(radAngle)*Math.cos(radDegree)*rShort)}">
      <attribute name="rLong" value="120"/>
      <attribute name="rShort" value="50"/>
      <attribute name="degree" value="45"/>
      <attribute name="angle"/>
      <attribute name="radDegree" value="${Math.PI/180*(degree)}"/>
      <attribute name="radAngle" value="${Math.PI/180*angle}"/>
      <animator attribute="angle" from="0" to="360" duration="1000" motion="linear" repeat="Infinity"/>
    </view>
  </view>
</canvas>

リスト18 サンプル

リスト19は3Dっぽい奥行き感のある楕円運動です。

リスト19
<canvas proxied="false" bgcolor="0xffffcc"> 
  <view x="20" y="20">
    <view bgcolor="#ff0000" width="${this.y/4+20}" height="${this.width}" x="${r+(r*Math.cos(cnt))}" y="${(r+(r*Math.sin(cnt)))/5}" opacity="${this.y/200+0.4}">
      <attribute name="cnt"/>
      <attribute name="r" value="200"/>
      <animator attribute="cnt" from="0" to="$once{Math.PI * 2}" duration="3000" motion="linear" repeat="Infinity"/>
    </view>
  </view>
</canvas>

リスト19 サンプル

ビューの間隔の変化

<simplelayout>のspacing属性も数値なのでアニメーション変化させることができます。

リスト20
<canvas proxied="false" bgcolor="0xffffcc"> 
  <simplelayout id="sl" axis="x"/>
  <button onclick="this.anm.doStart()">TEST
    <animatorgroup name="anm"  started="false">
      <animator attribute="spacing" target="sl" to="20" duration="700"/>  
      <animator attribute="spacing" target="sl" to="0" duration="700" />  
    </animatorgroup>
  </button>
  <view width="30" height="30" bgcolor="red"/>
  <view width="30" height="30" bgcolor="blue"/>
  <view width="30" height="30" bgcolor="aqua"/>
  <view width="30" height="30" bgcolor="green"/>
</canvas>

リスト20 サンプル

文字サイズの変化

<text>のfontsize属性も数値なのでアニメーション変化させることができます。

リスト21
<canvas proxied="false" bgcolor="0xffffcc"> 
  <simplelayout id="sl" axis="x"/>
  <button onclick="this.anm.doStart()">TEST
    <animatorgroup name="anm"  started="false">
      <animator attribute="fontsize" target="t" to="50" duration="700"/>  
      <animator attribute="fontsize" target="t" to="12" duration="700" />  
    </animatorgroup>
  </button>
  <text id="t">Hello World</text>
</canvas>

リスト21 サンプル

色の変化

bgcolor属性も数値ですが、値の通常の形式は0xff0000のように16進数表記なので変化させるには大変です。でもrgb(R,G,B)形式で表した場合、R、G、Bそれぞれが10進数なので簡単に変化させることができます。

リスト22のサンプルはTestボタンを押すと四角の色がランダムに変わっていきます。

リスト22
<canvas proxied="false" bgcolor="0xffffcc">
  <simplelayout spacing="20"/>
  <view id="v" width="210" height="50" bgcolor="${'rgb('+this.R+','+this.G+','+this.B+')'}" >
    <attribute name="R" value="255"/>
    <attribute name="G" value="0"/>
    <attribute name="B" value="0"/>
  </view>
  <button onclick="parent.anm1.doStart();parent.anm2.doStart();parent.anm3.doStart();">Test</button>
  <animatorgroup name="anm1" started="false" target="v" repeat="Infinity">
    <animator attribute="R" from="255" to="0" duration="2222"/> 
    <animator attribute="R" from="0" to="255" duration="2222"/> 
  </animatorgroup>
  <animatorgroup name="anm2" started="false" target="v" repeat="Infinity">
    <animator attribute="G" from="0" to="255" duration="3333"/> 
    <animator attribute="G" from="255" to="0" duration="3333"/> 
  </animatorgroup>
  <animatorgroup name="anm3" started="false" target="v" repeat="Infinity">
    <animator attribute="B" from="0" to="255" duration="4444"/> 
    <animator attribute="B" from="255" to="0" duration="4444"/> 
  </animatorgroup>
</canvas>

リスト22 サンプル

まとめ

アニメーション効果は楽しいですが、くれぐれも使いすぎにご注意ください(^^;)。

おすすめ記事

記事・ニュース一覧