花火 アニメーションサンプル


unit untFireworks;

interface

uses
  System.Types,
  System.UITypes,
  System.Classes,
  System.UIConsts,
  FMX.Filter.Effects,
  FMX.Controls,
  FMX.Effects,
  FMX.Ani,
  FMX.Types,
  FMX.Objects;


type
  TFireWorksStatus = record
  private
  public
    StartX: Single;
    FireY: Single;
    FireRadius: Single;
    FireColorHue:  Single;      // -1 から 1 までの値を取る
    LauncherDelay:  Single;
    LauncherDuration:  Single;
    FireDuration:  Single;

    BallRadius: Single;
    BallCount:  Integer;
    BallBitmap: TBitmap;

    LauncherWidth: Single;
    LauncherHeight: Single;

  end;

type
  TFireworks = class
  private
    FTimer: TTimer;

    FParentImage: TImage;
    FOwner:       TComponent;

    FStatus: TFireWorksStatus;

    FLauncher:    TRectangle;
    FLauncherPositionYAnimation: TFloatAnimation;
    FLauncherBlur: TBlurEffect;


    FBall:                  Array of TCircle;
    FBallImage:             Array of TImage;
    FBallPositionAnimation: Array of TPathAnimation;
    FBallOpacityAnimation:  Array of TFloatAnimation;
    FBallHueAdjustEffect:   Array of THueAdjustEffect;

    procedure OnTimer(Sender: TObject);
    procedure OnFLauncherPositionYAnimationFinish(Sender: TObject);
    procedure OnBallOpacityAnimationFinish(Sender: TObject);

    function getScreenHeight: Single;
    function getScreenWidth: Single;

    property ScreenWidth:  Single read getScreenWidth;
    property ScreenHeight: Single read getScreenHeight;

  public

    constructor Create(aOwner: TComponent;
                              aParent: TImage;
                              aFireWorksStatus: TFireWorksStatus);
    destructor Destroy(); override;

    procedure Fire();


  end;

const
  START_POSITION_V_MARGIN  = 0.1;
  START_POSITION_H_MARGIN  = 0.1;
  FINISH_POSITION_V_MARGIN = 0.1;

implementation


{ TFireworks }

constructor TFireworks.Create(aOwner: TComponent;
                              aParent: TImage;
                              aFireWorksStatus: TFireWorksStatus);
var
  i: Integer;
  tmpRad: Single;
begin

  FTimer          := TTimer.Create(nil);
  FTimer.OnTimer  := OnTimer;
  FTimer.Enabled  := False;

  FOwner := aOwner;
  FParentImage := aParent;
  FParentImage.ClipChildren := True;

  FStatus := aFireWorksStatus;

  //
  FTimer.Interval := Round(FStatus.LauncherDelay * 1000);
  FTimer.Enabled := False;
  //
  FLauncher := TRectangle.Create(FOwner);
  FLauncher.Parent := FParentImage;
  FLauncher.Visible := False;

  FLauncherBlur := TBlurEffect.Create(FOwner);
  FLauncherBlur.Parent := FLauncher;
  FLauncherBlur.Softness := 0.1;

  with FStatus do
  begin
    FLauncherPositionYAnimation := TFloatAnimation.Create(FOwner);
    FLauncherPositionYAnimation.Parent := FLauncher;
    FLauncherPositionYAnimation.PropertyName := 'Position.Y';
    FLauncherPositionYAnimation.OnFinish  := OnFLauncherPositionYAnimationFinish;

    FLauncher.Width      := LauncherWidth;
    FLauncher.Height     := LauncherHeight;
    FLauncher.Position.X := StartX - LauncherWidth  / 2;
    FLauncher.Position.Y := Self.ScreenHeight * 0.95 - LauncherHeight / 2;

    FLauncherPositionYAnimation.StartValue := FLauncher.Position.Y;
    FLauncherPositionYAnimation.StopValue  := FireY - LauncherHeight / 2;
    FLauncherPositionYAnimation.Duration   := LauncherDuration;
    FLauncherPositionYAnimation.AnimationType := TAnimationType.atOut;
    FLauncherPositionYAnimation.Interpolation := TInterpolationType.itCubic;
  end;


  //
  SetLength(FBall,                  FStatus.BallCount);
  SetLength(FBallPositionAnimation, FStatus.BallCount);
  SetLength(FBallOpacityAnimation,  FStatus.BallCount);
  SetLength(FBallImage,             FStatus.BallCount);
  SetLength(FBallHueAdjustEffect,   FStatus.BallCount);

  for i := 0 to FStatus.BallCount - 1 do
  begin

    FBallImage[i]            := TImage.Create(FOwner);
    FBallImage[i].Parent     := FParentImage;
    FBallImage[i].Bitmap.Assign(FStatus.BallBitmap);
    FBallImage[i].Width      := FStatus.BallRadius * 2;
    FBallImage[i].Height     := FStatus.BallRadius * 2;
    FBallImage[i].Position.X := FStatus.StartX - FStatus.BallRadius;
    FBallImage[i].Position.Y := FStatus.FireY - FStatus.BallRadius;
    FBallImage[i].RotationAngle := (360 / FStatus.BallCount) * i + 90;
    FBallImage[i].Visible     := False;

    //
    FBallPositionAnimation[i] := TPathAnimation.Create(FOwner);
    FBallPositionAnimation[i].Parent := FBallImage[i];
    FBallPositionAnimation[i].Duration := FStatus.FireDuration * 0.8;
    FBallPositionAnimation[i].AnimationType := TAnimationType.atOut;
    FBallPositionAnimation[i].Interpolation := TInterpolationType.itCubic;
    FBallPositionAnimation[i].Path.Clear;
    FBallPositionAnimation[i].Path.MoveTo(PointF(0, 0));
    tmpRad := (2 * PI / FStatus.BallCount) * i;
    FBallPositionAnimation[i].Path.LineTo(PointF(Cos(tmpRad) * FStatus.FireRadius,
                                                 Sin(tmpRad) * FStatus.FireRadius));

    //
    FBallOpacityAnimation[i] :=TFloatAnimation.Create(FOwner);
    FBallOpacityAnimation[i].Parent := FBallImage[i];
    FBallOpacityAnimation[i].PropertyName := 'Opacity';
    FBallOpacityAnimation[i].Duration   := FStatus.FireDuration;
    FBallOpacityAnimation[i].StartValue := 1;
    FBallOpacityAnimation[i].StopValue  := 0.1;
    FBallOpacityAnimation[i].AnimationType := TAnimationType.atIn;
    FBallOpacityAnimation[i].Interpolation := TInterpolationType.itCubic;

    //
    FBallHueAdjustEffect[i] := THueAdjustEffect.Create(FOwner);
    FBallHueAdjustEffect[i].Parent := FBallImage[i];
    FBallHueAdjustEffect[i].Hue := FStatus.FireColorHue;

  end;

  // 代表として Index 0 を基準にする
  FBallOpacityAnimation[0].OnFinish := OnBallOpacityAnimationFinish;

end;

destructor TFireworks.Destroy;
begin
  FTimer.Free;
  inherited;
end;

function TFireworks.getScreenHeight: Single;
begin
  Result := FParentImage.Height;
end;

function TFireworks.getScreenWidth: Single;
begin
  Result := FParentImage.Width;
end;

procedure TFireworks.OnBallOpacityAnimationFinish(Sender: TObject);
var
  i: Integer;
begin
  for i := 0 to FStatus.BallCount - 1 do
  begin
    FBallImage[i].Visible := False;
  end;

  self.Free;
end;

procedure TFireworks.OnFLauncherPositionYAnimationFinish(Sender: TObject);
var
  i: Integer;
begin
  FLauncher.Visible := False;
  for i := 0 to FStatus.BallCount - 1 do
  begin
    FBallImage[i].Visible := True;
    FBallPositionAnimation[i].Start();
    FBallOpacityAnimation[i].Start();
  end;
end;

procedure TFireworks.OnTimer(Sender: TObject);
begin
  FLauncher.Visible := True;
  FLauncherPositionYAnimation.Start();
  FTimer.Enabled := False;
end;

procedure TFireworks.Fire;
begin
  // Start
  FTimer.Enabled := True;
end;


end.

コメントを残す

メールアドレスが公開されることはありません。


9 × = 九

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>