Search code examples
delphivcl

How to stop flickering in layered image animation


i used 30 png pictures on a transplanted from to make a simple animation, a Timer make an event every 33 Millisecond to change the visibility of the TImage Components which have the png images, i tried all the method suggested in other posts to stop flickering but could not solve the problem.

unit Animation;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, AdvShaper, pngimage, ExtCtrls, StdCtrls;

type
  TAnimation_Form = class(TForm)
    Image1: TImage;
    Timer: TTimer;
    Image2: 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;
    Image24: TImage;
    Image25: TImage;
    Image26: TImage;
    Image27: TImage;
    Image28: TImage;
    Image29: TImage;
    Image30: TImage;
    Exit: TButton;
    procedure TimerTimer(Sender: TObject);
    procedure ExitClick(Sender: TObject);
  private
    { Private declarations }
    Image_Counter:Integer;

    procedure ChooseImage(I:Integer);
    procedure Init();
  public
    { Public declarations }
  end;


procedure Run_Animation_Form();
procedure Finish_Animation_Form();

implementation
var
  Animation_Form: TAnimation_Form;
{$R *.dfm}

procedure Finish_Animation_Form();
Begin
  Animation_Form.Close;
End;

procedure Run_Animation_Form();

Begin
  Animation_Form := TAnimation_Form.Create(nil);
  Try
    Animation_Form.Init();
    Animation_Form.ShowModal();
  Finally
    Animation_Form.Free;
  End;
End;

{ TAnimation_Form }

procedure TAnimation_Form.ChooseImage(I: Integer);
begin
  TwinControl(FindComponent(Format('Image%d',[I]))).Visible := False;
  TwinControl(FindComponent(Format('Image%d',[I+1]))).Visible := True;
end;

procedure TAnimation_Form.ExitClick(Sender: TObject);
begin
  Close;
end;

procedure TAnimation_Form.Init;
begin
  TransparentColor := True;
  TransparentColorValue := Color;
  Image1.Visible := True;
  Image_Counter:=1;
  ControlStyle:=ControlStyle - [csOpaque];
  BorderWidth := 10;
  Anchors := [akLeft, akTop, akBottom, akRight];

end;

procedure TAnimation_Form.TimerTimer(Sender: TObject);
begin
  if Image_Counter >= 30 then
    Begin
      Image30.Visible := False;
      Image1.Visible := True;
      Image_Counter:=1;
    End
  else
    Begin
      ChooseImage(Image_Counter);
      Inc(Image_Counter);
    End;

end;

end.

Thanks for your help and sorry for my bad English


Solution

  • Rather than using multiple overlapping TImage objects and swapping their Visible property, I would suggest you create an array of 30 TPNGImage objects and then either:

    1. use a single TImage that is always visible and assign the desired PNG to its TImage.Picture property whenever the TTimer elapses:

      unit Animation;
      
      interface
      
      uses
        Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
        Dialogs, AdvShaper, pngimage, ExtCtrls, StdCtrls;
      
      type
        TAnimation_Form = class(TForm)
          Image1: TImage;
          Timer: TTimer;
          Exit: TButton;
          procedure FormCreate(Sender: TObject);
          procedure FormDestroy(Sender: TObject);
          procedure TimerTimer(Sender: TObject);
          procedure ExitClick(Sender: TObject);
        private
          { Private declarations }
          Image_Counter: Integer;
          Images: array[0..29] of TPNGImage;
        public
          { Public declarations }
        end;
      
      procedure Run_Animation_Form();
      procedure Finish_Animation_Form();
      
      implementation
      
      var
        Animation_Form: TAnimation_Form = nil;
      
      {$R *.dfm}
      
      procedure Finish_Animation_Form();
      Begin
        if Animation_Form <> nil then
          Animation_Form.Close;
      End;
      
      procedure Run_Animation_Form();
      Begin
        Animation_Form := TAnimation_Form.Create(nil);
        Try
          Animation_Form.ShowModal();
        Finally
          FreeAndNil(Animation_Form);
        End;
      End;
      
      { TAnimation_Form }
      
      procedure TAnimation_Form.FormCreate(Sender: TObject);
      var
        I: Integer;
      begin
        for I := Low(Images) to High(Images) do
        begin
          Images[I] := TPNGImage.Create;
          // load PNG image into Images[I] as needed...
        end;
      
        // FYI, these properties can be set at design time...
        TransparentColor := True;
        TransparentColorValue := Color;
        Image1.Visible := True;
        BorderWidth := 10;
        Anchors := [akLeft, akTop, akBottom, akRight];
      
        Image_Counter := 0;
        Image1.Picture := Images[0];
      
        ControlStyle := ControlStyle - [csOpaque];
      end;
      
      procedure TAnimation_Form.FormDestroy(Sender: TObject);
      var
        I: Integer;
      begin
        for I := Low(Images) to High(Images) do
          Images[I].Free;
      end;
      
      procedure TAnimation_Form.ExitClick(Sender: TObject);
      begin
        Close;
      end;
      
      procedure TAnimation_Form.TimerTimer(Sender: TObject);
      begin
        Inc(Image_Counter);
        if Image_Counter > High(Images) then
          Image_Counter := 0;
        Image1.Picture := Images[Image_Counter];
      end;
      
      end.
      
    2. use a single TPaintBox and assign an OnPaint event handler to it that draws the current PNG onto the TPaintBox.Canvas, and then have the TTimer simply update the current PNG and call TPaintBox.Invalidate() to trigger a repaint:

      unit Animation;
      
      interface
      
      uses
        Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
        Dialogs, AdvShaper, pngimage, ExtCtrls, StdCtrls;
      
      type
        TAnimation_Form = class(TForm)
          PaintBox1: TPaintBox;
          Timer: TTimer;
          Exit: TButton;
          procedure FormCreate(Sender: TObject);
          procedure FormDestroy(Sender: TObject);
          procedure TimerTimer(Sender: TObject);
          procedure ExitClick(Sender: TObject);
          procedure PaintBox1Paint(Sender: TObject);
        private
          { Private declarations }
          Image_Counter: Integer;
          Images: array[0..29] of TPNGImage;
        public
          { Public declarations }
        end;
      
      procedure Run_Animation_Form();
      procedure Finish_Animation_Form();
      
      implementation
      
      var
        Animation_Form: TAnimation_Form = nil;
      
      {$R *.dfm}
      
      procedure Finish_Animation_Form();
      Begin
        if Animation_Form <> nil then
          Animation_Form.Close;
      End;
      
      procedure Run_Animation_Form();
      Begin
        Animation_Form := TAnimation_Form.Create(nil);
        Try
          Animation_Form.ShowModal();
        Finally
          FreeAndNil(Animation_Form);
        End;
      End;
      
      { TAnimation_Form }
      
      procedure TAnimation_Form.FormCreate(Sender: TObject);
      var
        I: Integer;
      begin
        for I := Low(Images) to High(Images) do
        begin
          Images[I] := TPNGImage.Create;
          // load PNG image into Images[I] as needed...
        end;
      
        // FYI, these properties can be set at design time...
        TransparentColor := True;
        TransparentColorValue := Color;
        Image1.Visible := True;
        BorderWidth := 10;
        Anchors := [akLeft, akTop, akBottom, akRight];
      
        Image_Counter := 0;
      
        ControlStyle := ControlStyle - [csOpaque];
      end;
      
      procedure TAnimation_Form.FormDestroy(Sender: TObject);
      var
        I: Integer;
      begin
        for I := Low(Images) to High(Images) do
          Images[I].Free;
      end;
      
      procedure TAnimation_Form.ExitClick(Sender: TObject);
      begin
        Close;
      end;
      
      procedure TAnimation_Form.TimerTimer(Sender: TObject);
      begin
        Inc(Image_Counter);
        if Image_Counter > High(Images) then
          Image_Counter := 0;
        PaintBox1.Invalidate;
      end;
      
      procedure TAnimation_Form.PaintBox1Paint(Sender: TObject);
      begin
        PaintBox1.Canvas.Draw(0, 0, Images[Image_Counter]);
        // or:
        // PaintBox1.Canvas.StretchDraw(Rect(0, 0, PaintBox1.Width, PaintBox1.Height), Images[Image_Counter]);
      end;
      
      end.