Transition
なぜか、Transition について書いた記事があまりないので、Transition の紹介。
まずは、↓これを(サイズ大きめなので表示までに時間かかるかも&実機ではもっと綺麗に表示されてます)。

Transition を使えば、どこかで見たことのあるエフェクトも(比較的)簡単に作れちゃうもんね!!
Transition とは
日本語では推移効果といい、一般的には2つのシーンを効果付きで切り替える機能です。
Delphi FireMonkey における Transition も同様で、2枚の画像を効果付きで切り替えます。
たとえば、フェードしながら切り替えたり、波紋様に変形しながら切り替えたりできます。
(上記の画像は Sample ディレクトリに入っている "ShaderFilters" からキャプチャしました。標準のインストール先はC:\Users\Public\Documents\Embarcadero\Studio\22.0\Samples\Object Pascal\Multi-Device Samples\User Interface\ShaderFilters\
です)
なお冒頭の Transition は、TBlurTransition を使っています。
実用的な Transition の使い方
2枚の画像を切り替えるだけ、という用途もあまりないと思うので、Transition を利用した画面転換方法を紹介します。
0. 画面&遷移後の画面を作る
■元の画面 (以降 First と呼びます)
■遷移後の画面 (以降 Second と呼びます)
取りあえず適当にコントロールを乗せてこんな感じの画面を作りました。
Second が TPanel に構築されているのは背景をつけるためで、別に TLayout でも構いません。
ここは本来必要な UI ができているハズなので順番は 0 です。
1. Transition の設定
First に Transition をくっつけます。
Transition なら何でもいいのですが、ここではぐにょぐにょするエフェクトをかけてくれる TWiggleTransitionEffect を付けました。

2. FloatAnimation の設定
1 で付けた Transition に TFloatAnimation をくっつけます。

そして、プロパティを以下のように設定します。
プロパティ | 値 | 意味 |
---|---|---|
Duration | 3 | アニメーション時間[sec] ここでは判りやすくするために 3[sec]にしましたが、本来はもっと短く 0.2~0.5 ぐらいが良いと思います |
PropertyName | Progress | 変更する親コントロールのプロパティの名前 |
StopValue | 100 | アニメーション終了時の値 Progress プロパティは 0~100% を指定するものなので終了値は 100 です |
OnFinish | FloatAnimation1Finish | アニメーションが終了すると呼ばれるイベント |
3. コード
ScreenShot
まず、Second である pnlSecond の OnApplyStyleLookup で Transition にターゲット画像を設定します。
このターゲット画像を pnlSecond のスクリーンショットにすると画面が遷移したように見えます。
スクリーンショットを撮った後はとりあえず必用無いので Visible を False にして非表示にしています。
procedure TfrmSample.pnlSecondApplyStyleLookup(Sender: TObject);
begin
var Bmp := pnlSecond.MakeScreenshot;
try
WiggleTransitionEffect1.Target.Assign(Bmp);
finally
Bmp.Free;
end;
pnlSecond.Visible := False;
end;
MakeScreenshot はコントロールが Visible で実際に画面上にレンダリング(OnApplyStyleLookup 前には取得できません)されていないと正しく取得できません!
Target を指定しないと Android で意図通り動作しません(黒と混合されます)
Target を指定したくないときは
var Bmp := TBitmap.Create(1, 1);
try
Bmp.Clear(0);
WiggleTransitionEffect1.Target.Assign(Bmp);
finally
Bmp.Free;
end;
として、透明 Bitmap を指定します。
開始
Button1 の OnClick でアニメーションをスタートします。
WiggleTransitionEffect1.Enabled := True;
として Transition を有効化しています。
procedure TfrmSample.Button1Click(Sender: TObject);
begin
WiggleTransitionEffect1.Enabled := True;
FloatAnimation1.Start;
end;
終了
アニメーションが終了するとここが呼ばれます。
まず、WiggleTransitionEffect1.Enabled := False;
で Transition を無効化します。
次に、layoutFirst.Visible := False;
で First を非表示にします。
最後に、pnlSecond.Visible := True;
で Second を表示します。
procedure TfrmSample.FloatAnimation1Finish(Sender: TObject);
begin
WiggleTransitionEffect1.Enabled := False;
layoutFirst.Visible := False;
pnlSecond.Visible := True;
end;
実行
ここまでを実行すると、ぐにょおおおおと画面が変わります。

冒頭の Transition について
まずこんな画面を作ります。

↓のコードでやっていることは上記のコードと基本的には同じです。
ただ、GridLayout からまるで外に飛び出したように見せるための座標計算、飛び出した画像の大きさを変更するための TFloatAnimation.OnProcess などが書いてあります。
また、TBlurTransitionEffect は画面外に飛び出したように見えるため Target は透明画像を指定しています。
Transition の種類によっては Target 画像を指定しない方が上手く見える事も良くあります。
(MakeScreenshot をしなくて良いので使える場合は、こちらの方が楽です)
コード全文
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects,
FMX.Layouts, FMX.Effects, FMX.Filter.Effects, FMX.Controls.Presentation,
FMX.StdCtrls, FMX.Ani;
type
TForm1 = class(TForm)
layoutGallery: TGridLayout;
Image1: TImage;
Image2: TImage;
Image24: TImage;
Image3: TImage;
Image4: TImage;
Image5: TImage;
Image6: TImage;
Image7: TImage;
Image8: TImage;
Image9: TImage;
Image10: TImage;
Image11: TImage;
Image12: TImage;
Image13: TImage;
Image14: TImage;
Image15: TImage;
Image16: TImage;
Image17: TImage;
Image18: TImage;
Image19: TImage;
Image20: TImage;
Image21: TImage;
Image22: TImage;
Image23: TImage;
StyleBook1: TStyleBook;
BlurTransitionEffect1: TBlurTransitionEffect;
FloatAnimation1: TFloatAnimation;
layoutOneImageBase: TLayout;
imgOneImage: TImage;
imgTarget: TImage;
layoutRoot: TLayout;
procedure FloatAnimation1Finish(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FloatAnimation1Process(Sender: TObject);
procedure ImageClick(Sender: TObject);
procedure imgOneImageClick(Sender: TObject);
procedure layoutGalleryResized(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word; var KeyChar: Char;
Shift: TShiftState);
private var
FTarget: TImage;
FStartPos: TPointF;
FStartSize: TSizeF;
FDeltaPos: TPointF;
FDeltaSize: TSizeF;
private
procedure StartTransition(const AInverse: Boolean);
public
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
{$IFDEF ANDROID}
uses
Androidapi.Helpers;
{$ENDIF}
procedure TForm1.FloatAnimation1Finish(Sender: TObject);
begin
// アニメーションの終了処理
// Transition 無効化
BlurTransitionEffect1.Enabled := False;
// 可視状態変更
imgTarget.Visible := False;
FTarget.Visible := True;
if FloatAnimation1.Inverse then
begin
layoutOneImageBase.Visible := False;
imgOneImage.Bitmap.Assign(nil);
end
else
begin
layoutOneImageBase.Opacity := 1;
layoutOneImageBase.Visible := True;
layoutGallery.Visible := False;
end;
end;
procedure TForm1.FloatAnimation1Process(Sender: TObject);
begin
// アニメーションの段階毎に呼ばれる
// 進捗パーセンテージを計算
var P := BlurTransitionEffect1.Progress / FloatAnimation1.StopValue;
if FloatAnimation1.Inverse then
P := 1 - P;
// ターゲットの位置と透明度を設定
imgTarget.Opacity := P;
imgTarget.SetBounds(
FStartPos.X + FDeltaPos.X * P,
FStartPos.Y + FDeltaPos.Y * P,
FStartSize.cx + FDeltaSize.cx * P,
FStartSize.cy + FDeltaSize.cy * P);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
// Transition 用透明画像をセット
var Bmp := TBitmap.Create(1, 1);
try
BlurTransitionEffect1.Target.Assign(Bmp);
finally
Bmp.Free;
end;
imgTarget.Visible := False;
layoutOneImageBase.Visible := False;
end;
procedure TForm1.FormKeyDown(
Sender: TObject;
var Key: Word;
var KeyChar: Char;
Shift: TShiftState);
begin
{$IFDEF ANDROID}
// Back Key の制御
if Key = vkHardwareBack then
begin
Key := 0;
if layoutOneImageBase.Visible then
imgOneImageClick(nil)
else
TAndroidHelper.Activity.moveTaskToBack(True);
end;
{$ENDIF}
end;
procedure TForm1.ImageClick(Sender: TObject);
begin
if not (Sender is TImage) then
Exit;
// ターゲット設定
FTarget := TImage(Sender);
imgOneImage.Bitmap.Assign(FTarget.Bitmap);
// アニメーション
StartTransition(False);
end;
procedure TForm1.imgOneImageClick(Sender: TObject);
begin
// 逆順アニメーション
StartTransition(True);
end;
procedure TForm1.layoutGalleryResized(Sender: TObject);
begin
// 画面内が4列になるように計算
var Size := layoutGallery.Width;
if Size > layoutGallery.Height then
Size := layoutGallery.Height;
Size := (Size - layoutGallery.Padding.Left * 2) / 4;
layoutGallery.ItemWidth := Size;
layoutGallery.ItemHeight := Size;
end;
procedure TForm1.StartTransition(const AInverse: Boolean);
const
ONE_IMAGE_MARGIN = 16 * 2;
begin
if
(FTarget = nil) or
(FTarget.Parent = nil) or
(imgOneImage.Parent = nil) or
(not (FTarget.Parent is TControl)) or
(not (imgOneImage.Parent is TControl))
then
Exit;
// 綺麗に見せるための表示調整
FTarget.Visible := False;
layoutGallery.Visible := True;
layoutOneImageBase.Opacity := 0;
layoutOneImageBase.Visible := True;
layoutOneImageBase.BeginUpdate;
try
var Size := layoutOneImageBase.Width;
if Size > layoutOneImageBase.Height then
Size := layoutOneImageBase.Height;
Size := Size - ONE_IMAGE_MARGIN;
imgOneImage.SetBounds(0, 0, Size, Size);
finally
layoutOneImageBase.EndUpdate;
end;
var StartImg, EndImg: TImage;
// 座標計算
if AInverse then
begin
StartImg := imgOneImage;
EndImg := FTarget;
end
else
begin
StartImg := FTarget;
EndImg := imgOneImage;
end;
// 開始位置・終了位置計算
FStartPos :=
TControl(StartImg.Parent).LocalToAbsolute(StartImg.Position.Point);
var EndPos :=
TControl(EndImg.Parent).LocalToAbsolute(EndImg.Position.Point);
FStartPos := layoutRoot.AbsoluteToLocal(FStartPos);
EndPos := layoutRoot.AbsoluteToLocal(EndPos);
// 終了位置と開始位置の差
FDeltaPos.X := EndPos.X - FStartPos.X;
FDeltaPos.Y := EndPos.Y - FStartPos.Y;
// 開始状態の大きさ
FStartSize.cx := StartImg.Width;
FStartSize.cy := StartImg.Height;
// 開始と終了の大きさの差
FDeltaSize.cx := EndImg.Width - StartImg.Width;
FDeltaSize.cy := EndImg.Height - StartImg.Height;
// Target Image 設定
imgTarget.Bitmap.Assign(EndImg.Bitmap);
imgTarget.Visible := True;
// スタート
BlurTransitionEffect1.Enabled := True;
FloatAnimation1.Inverse := AInverse;
FloatAnimation1.Start;
end;
end.
まとめ
Transition の使い方をまとめると
- Transition を元画面に付ける
- TFloatAnimation を Transition に付ける
- Transition.Target に遷移先画面の Bitmap か、透明 Bitmap を指定する
- 遷移先画面を非表示にする
- FloatAnimation.Start でアニメーションをスタートする
- アニメーションが終了したら、元画面を非表示にし、遷移先画面を表示する
となります。
言葉で説明すると長いですが、コードは上に上げたたったこれだけです。
Delphi で作られたアプリケーションがもっと華やかになりますように!