Playing card flip animation

こ雲淡風輕ζ 提交于 2019-11-30 17:04:46

问题


Do you know of any free components/libraries, which allow to achieve a 3D flip effect?

Demo here: snorkl.tv


回答1:


Something like this might do the similar effect (just another attempt to show how this could be done, also not so precise, but it's just for fun since you've asked for a library or component). The principle is based on a rectnagle that is being resized and centered in the paint box where the card is being rendered with the StretchDraw function:

Unit1.pas

unit Unit1;

interface

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

type
  TCardSide = (csBack, csFront);
  TForm1 = class(TForm)
    Timer1: TTimer;
    Timer2: TTimer;
    PaintBox1: TPaintBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Timer2Timer(Sender: TObject);
    procedure PaintBox1Click(Sender: TObject);
    procedure PaintBox1Paint(Sender: TObject);
  private
    FCardRect: TRect;
    FCardSide: TCardSide;
    FCardBack: TPNGImage;
    FCardFront: TPNGImage;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FCardSide := csBack;
  FCardRect := PaintBox1.ClientRect;
  FCardBack := TPNGImage.Create;
  FCardBack.LoadFromFile('tps2N.png');
  FCardFront := TPNGImage.Create;
  FCardFront.LoadFromFile('Ey3cv.png');
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FCardBack.Free;
  FCardFront.Free;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if FCardRect.Right - FCardRect.Left > 0 then
  begin
    FCardRect.Left := FCardRect.Left + 3;
    FCardRect.Right := FCardRect.Right - 3;
    PaintBox1.Invalidate;
  end
  else
  begin
    Timer1.Enabled := False;
    case FCardSide of
      csBack: FCardSide := csFront;
      csFront: FCardSide := csBack;
    end;
    Timer2.Enabled := True;
  end;
end;

procedure TForm1.Timer2Timer(Sender: TObject);
begin
  if FCardRect.Right - FCardRect.Left < PaintBox1.ClientWidth then
  begin
    FCardRect.Left := FCardRect.Left - 3;
    FCardRect.Right := FCardRect.Right + 3;
    PaintBox1.Invalidate;
  end
  else
    Timer2.Enabled := False;
end;

procedure TForm1.PaintBox1Click(Sender: TObject);
begin
  Timer1.Enabled := False;
  Timer2.Enabled := False;
  FCardRect := PaintBox1.ClientRect;
  Timer1.Enabled := True;
  PaintBox1.Invalidate;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
  case FCardSide of
    csBack: PaintBox1.Canvas.StretchDraw(FCardRect, FCardBack);
    csFront: PaintBox1.Canvas.StretchDraw(FCardRect, FCardFront);
  end;
end;

end.

Unit1.dfm

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 203
  ClientWidth = 173
  Color = clBtnFace
  DoubleBuffered = True
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  Position = poScreenCenter
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object PaintBox1: TPaintBox
    Left = 48
    Top = 40
    Width = 77
    Height = 121
    OnClick = PaintBox1Click
    OnPaint = PaintBox1Paint
  end
  object Timer1: TTimer
    Enabled = False
    Interval = 10
    OnTimer = Timer1Timer
    Left = 32
    Top = 88
  end
  object Timer2: TTimer
    Enabled = False
    Interval = 10
    OnTimer = Timer2Timer
    Left = 88
    Top = 88
  end
end

Cards




回答2:


Here's an attempt using SetWorldTransform:

type
  TForm1 = class(TForm)
    PaintBox1: TPaintBox;
    Button1: TButton;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure PaintBox1Paint(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    FFrontBmp, FBackBmp: TBitmap;
    FBmps: array [Boolean] of TBitmap;
    FXForm: TXForm;
    FStep: Integer;
  end;

var
  Form1: TForm1;

implementation

uses
  Math;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  FFrontBmp := TBitmap.Create;
  FFrontBmp.LoadFromFile(ExtractFilePath(Application.ExeName) + '53.bmp');
  FBackBmp := TBitmap.Create;
  FBackBmp.LoadFromFile(ExtractFilePath(Application.ExeName) + 'b1fv.bmp');
  FBmps[True] := FFrontBmp;
  FBmps[False] := FBackBmp;

  FXForm.eM11 := 1;
  FXForm.eM12 := 0;
  FXForm.eM21 := 0;
  FXForm.eM22 := 1;
  FXForm.eDx := 0;
  FXForm.eDy := 0;

  Timer1.Enabled := False;
  Timer1.Interval := 30;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FFrontBmp.Free;
  FBackBmp.Free;
end;

procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
  SetGraphicsMode(PaintBox1.Canvas.Handle, GM_ADVANCED);
  SetWorldTransform(PaintBox1.Canvas.Handle, FXForm);
  PaintBox1.Canvas.Draw(0, 0, FBmps[FStep < 20]);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  Bmp: TBitmap;
  Sign: Integer;
begin
  Inc(FStep);

  Sign := math.Sign(FStep - 20);
  FXForm.eM11 := FXForm.eM11 + 0.05 * Sign;
  FXForm.eM21 := FXForm.eM21 - 0.005 * Sign;
  FXForm.eDx := FXForm.eDx - 1 * Sign;
  if FStep = 39 then begin
    Timer1.Enabled := False;
    PaintBox1.Refresh;
  end else
    PaintBox1.Invalidate;

  if not Timer1.Enabled then begin
    Bmp := FBmps[True];
    FBmps[True] := FBmps[False];
    FBmps[False] := Bmp;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Timer1.Enabled := True;
  FStep := 0;
end;


I'm not sure if this stood a chance of turning out to be anything beautiful in case I had some maths capability, but here's currently how it looks:



The images used:  

 



来源:https://stackoverflow.com/questions/10585068/playing-card-flip-animation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!