Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Panel geschmeidig ein- und ausblenden? (https://www.delphipraxis.net/193860-panel-geschmeidig-ein-und-ausblenden.html)

Glados 16. Sep 2017 14:10

Panel geschmeidig ein- und ausblenden?
 
Ich nutze schon längere einen seltsamen, von mir geschriebenen Code, der ein Panel seitlich einblendet.

Beispiel:
Normal: [ CONTENT ]
Mit Panel: [ PANEL | CONTENT ]

Das Panel fährt von links rein und wird aktuell einfach von 0px an in der Größe verändert, bis es eine Maximalbreite erreicht hat.
Wie würdet ihr das Panel auf Knopfdruck geschmeidig "reinfahren" lassen? Ich rede nicht von Fade-Effekten sondern von Slide-Effekten (nennt man das so?).

stahli 16. Sep 2017 14:33

AW: Panel geschmeidig ein- und ausblenden?
 
Was ist denn seltsam an Deinem Code und was funktioniert nicht wie gewünscht?

Armin hatte hier mal einen netten Effekt umgesetzt: http://www.delphipraxis.net/156662-t...-fluessig.html

Grundsätzlich könntest Du eine Zielbreite festlegen und in einem Timer schrittweise dorthin erhöhen/reduzieren.

Dabei habe ich mal den Wert immer um die hälfte der Restdifferenz erhöht/verringert.

Z.B. bei aktuellem Wert 0 auf 100 etwa so:
50
75
88
94
97
99
100

Das sah dann ganz flüssig und dynamisch aus.

Glados 16. Sep 2017 14:35

AW: Panel geschmeidig ein- und ausblenden?
 
Zitat:

Was ist denn seltsam an Deinem Code und was funktioniert nicht wie gewünscht?
Es funktioniert wie gewünscht nur er ist nicht schön. Ehrlich gesagt sogar richtig hässlich, weswegen ich ihn nicht zeigen möchte. Stell dir einfach vor du gehst durch 10 Türen, obwohl du nur durch eine musst :thumb:

Ich probiere deinen Ansatz mal aus.

Zacherl 16. Sep 2017 14:41

AW: Panel geschmeidig ein- und ausblenden?
 
Stichwort: Easing.

Das sind Algorithmen, die dir eine bestimmte Kurve in Abhängigkeit von der Zeit generieren (bzw. den entsprechenden Multiplikator zu deinem Grundwert). Konkrete Implementierungen findest du auch recht einfach per Google bzw. hatte ich hier auch mal einige Easing-Curves umgesetzt.

Glados 16. Sep 2017 14:47

AW: Panel geschmeidig ein- und ausblenden?
 
Easing ist ja schön. Aber dafür direkt eine ganze Komponente bzw. gefühlt 20 Units, ich weiß nicht.
stahlis Idee kann ich übrigens nicht umsetzen. Mathematisch wüsste ich nicht einmal wie (ja, ich bin ein Mathe-Trottel).

Habe das als totaler Mathe-Noob mal so gemacht.
Delphi-Quellcode:
 iTargetWidth := 200;
 iRest := 0;

 repeat
  iRest := iRest + (iTargetWidth - Panel1.Width) div 2;
   Panel1.Width := iRest;
   Sleep(25);
 until Panel1.Width = iTargetWidth - 1;

Zacherl 16. Sep 2017 15:42

AW: Panel geschmeidig ein- und ausblenden?
 
Zitat:

Zitat von Glados (Beitrag 1381355)
Easing ist ja schön. Aber dafür direkt eine ganze Komponente bzw. gefühlt 20 Units, ich weiß nicht.

Bei dem Projekt handelt es sich nicht um eine Komponente, sondern um ein komplettes GUI Framework. Die Unit sollte nur ein Anstoß für dich sein, bzw. eine Quelle aus der du dir die mathematischen Berechnungen rauskopieren kannst.

Besonders
Delphi-Quellcode:
TDXInQuartEasingCurve
und
Delphi-Quellcode:
TDXOutQuartEasingCurve
sollten für dich interessant sein. Die Umsetzung ist denkbar einfach:
Delphi-Quellcode:
{ TDXInQuartEasingCurve }

function TDXInQuartEasingCurve.CalculateEasingCurve(TimePassed, Duration: DWord): Single;
var
  P: Double;
begin
  P := TimePassed / Duration;
  Result := P * P * P * P;
end;

{ TDXOutQuartEasingCurve }

function TDXOutQuartEasingCurve.CalculateEasingCurve(TimePassed, Duration: DWord): Single;
var
  P: Double;
begin
  P := TimePassed / (Duration - 1);
  Result := - (P * P * P * P) - 1;
end;

Glados 16. Sep 2017 15:52

AW: Panel geschmeidig ein- und ausblenden?
 
Was genau ist denn das Resultat? Was macht man damit und woher bekommt man TimePassed? Duration wird ja vermutlich die Zeit sein, die man selber angeben kann.

stahli 16. Sep 2017 15:55

AW: Panel geschmeidig ein- und ausblenden?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe mal schnell meinen Schnipsel (auch als Anregung) rausgesucht.
Vielleicht kannst Du Dir davon etwas ableiten.
Ich habe ein aktuelles und ein Ziel-Rect. Das aktuelle Rects wird so lange verändert, bis die Zielpositionen erreicht sind.


Delphi-Quellcode:
    if (DropRect.IsEmpty) then
    begin
      if (not DropRectReal.IsEmpty) then
      begin
        X := DropRectReal.Left + (DropRectReal.Width div 2);
        Y := DropRectReal.Top + (DropRectReal.Height div 2);
        L := Max((X - DropRectReal.Left) div 4, 1);
        T := Max((Y - DropRectReal.Top) div 4, 1);
        R := Min((X - DropRectReal.Right) div 4, -1);
        B := Min((Y - DropRectReal.Bottom) div 4, -1);
        DropRectReal := TRect.Create(DropRectReal.Left + L, DropRectReal.Top + T,
          DropRectReal.Right + R, DropRectReal.Bottom + B);
      end;
    end
    else
    begin
      if (DropRectReal.IsEmpty) then
      begin
        X := DropRect.Left + (DropRect.Width div 2);
        Y := DropRect.Top + (DropRect.Height div 2);
        DropRectReal := TRect.Create(X, Y, X, Y);
      end;
      L := (DropRect.Left - DropRectReal.Left) div 4;
      T := (DropRect.Top - DropRectReal.Top) div 4;
      R := (DropRect.Right - DropRectReal.Right) div 4;
      B := (DropRect.Bottom - DropRectReal.Bottom) div 4;
      DropRectReal := TRect.Create(DropRectReal.Left + L, DropRectReal.Top + T,
        DropRectReal.Right + R, DropRectReal.Bottom + B);
    end;

    if (DropRectReal.Left <> DropRect.Left) or (DropRectReal.Top <> DropRect.Top) or
      (DropRectReal.Width <> DropRect.Width) or (DropRectReal.Height <> DropRect.Height) then
    begin
      NächstenSchrittVeranlassen;
    end;

Aber wenn Dein Ergebnis funktioniert, kannst Du Deinen Code letztlich auch einfach so als Jugendsünde drin lassen. Stört ja keinen. :-)
Wenn es gut und stabil (ohne Abstürze) läuft, ist es doch i.O.

Zacherl 16. Sep 2017 16:14

AW: Panel geschmeidig ein- und ausblenden?
 
Zitat:

Zitat von Glados (Beitrag 1381359)
Was genau ist denn das Resultat? Was macht man damit und woher bekommt man TimePassed? Duration wird ja vermutlich die Zeit sein, die man selber angeben kann.

Delphi-Quellcode:
const
  DURATION = 1000; // 1 Sek für Gesamtanimation
  POS_START =   8;
  POS_END  = 300;
var
  C, D: Cardinal;
begin
  C := GetTickCount;
  D := 0;
  while (D < DURATION) do
  begin
    Button1.Left := POS_START +
      Round(POS_END * TDXInOutQuintEasingCurve.CalculateEasingCurve(D, DURATION));
    Application.ProcessMessages;
    D := GetTickCount - C;
  end;
end;

Glados 16. Sep 2017 17:38

AW: Panel geschmeidig ein- und ausblenden?
 
Ich glaube ich bin zu dumm dafür das umzukehren
Delphi-Quellcode:
   while (D < Duration) do
    begin
     Button1.Left := Button1.Left - Round(POS_START * CalculateEasingCurveOut(D, Duration));
     Application.ProcessMessages;
     D := GetTickCount - C;
    end;

jaenicke 16. Sep 2017 18:10

AW: Panel geschmeidig ein- und ausblenden?
 
Es gibt übrigens auch noch die API Funktion AnimateWindow... Damit geht so etwas auch und sieht meistens auch besser aus als eigene Funktionen:
https://msdn.microsoft.com/de-de/lib...%3Dvs.85).aspx

Glados 16. Sep 2017 18:38

AW: Panel geschmeidig ein- und ausblenden?
 
Das ist nicht genau das was ich suche.
Manuell ist in meinem Fall definitiv besser. AnimateWindow macht nicht das was ich möchte.

Zacherl Lösung ist wirklich sehr gut und sieht gut aus. Nur verstehe ich nicht die Logik wie man es umkehrt.

Verstanden habe ich es zwar nicht, aber so läuft es fast. Er fährt von 20 (START) bis 200 (END). Umgekehrt funktioniert es nicht.

Delphi-Quellcode:
const
 Duration = 3000;
 POS_START = 20;
 POS_END = 200;

 if Button1.Left = POS_START then
  begin
   while (D < Duration) do
    begin
     Button1.Left := POS_START + Round(POS_END * CalculateEasingCurve(D, Duration));
     Application.ProcessMessages;
     D := GetTickCount - C;
    end;
  end
 else
  begin
   while (D < Duration) do
    begin
     Button1.Left := POS_END - Round(POS_END * CalculateEasingCurve(D, Duration));
     Application.ProcessMessages;
     D := GetTickCount - C;
    end;
  end;
Vorzeitige Lösung
Delphi-Quellcode:
     if Panel1.Width = POS_START then
      begin
       iNegative := 1;
       iTmp := POS_START
      end
     else
      begin
       iNegative := -1;
       iTmp := POS_START + POS_END;
      end;

     while (D < Duration) do
      begin
       Panel1.Width := iTmp + (Round(POS_END * CalculateEasingCurve(D, Duration)) * iNegative);
       Application.ProcessMessages;
       D := GetTickCount - C;
      end;

Glados 16. Sep 2017 21:23

AW: Panel geschmeidig ein- und ausblenden?
 
Ich muss leider noch einmal nerven. Irgendwas stimmt hier leider noch nicht.
Es hat irgendwas mit der markierten Zeile zu tun
Delphi-Quellcode:
 if Control.Height >= iMaxHeight then
  begin
   iNegative := -1;
   iTmp := iMinHeight + iMaxHeight; // DIESE ZEILE
  end
 else // if Control.Height <= iMinHeight then
  begin
   iNegative := 1;
   iTmp := iMinHeight
  end;

 while (D < 3000) do
  begin
   Control.Height := iTmp + (Round(iMaxHeight * Easing(D, 3000)) * iNegative);
   Application.ProcessMessages;
   D := GetTickCount - C;
  end;
Das ist bei mir jetzt eine Prozedur. Control ist ein TWinControl. Wenn ich dort als Control ein Formular TForm (aktuell 350 Height) übergebe mit Minimum 220 (iMinHeight) und Maximum 350 (iMaxHeight), dann wird das Formular erst einmal komplett lang gezogen (Min + Max). Bei anderen Controls funktioniert das ohne Probleme und ohne dieses initiale Langziehen.

Meine Grundfrage wäre also... wie sieht das da unten umgekehrt aus?
Delphi-Quellcode:
   while (D < Duration) do
    begin
     Button1.Left := POS_START + Round(POS_END * CalculateEasingCurve(D, DURATION));
     Application.ProcessMessages;
     D := GetTickCount - C;
    end;

stahli 16. Sep 2017 22:59

AW: Panel geschmeidig ein- und ausblenden?
 
Müsste es vielleicht so sein?
Delphi-Quellcode:
 iTmp := iMaxHeight
Es würde dann die maximale Höhe eingestellt (wenn ich das richtig interpretiere).
Sonst startest Du mit der maximalen+minimalen Höhe.

Glados 16. Sep 2017 23:09

AW: Panel geschmeidig ein- und ausblenden?
 
Ich habe dir hier mal ein Beispiel gebastelt. Einfach in einen Button packen (Form sollte initial ~500 hoch sein) und die Caption beobachten.

Delphi-Quellcode:
function QuintEasing(TimePassed, Duration: DWord): Single;
var
 P: Double;
begin
 P := TimePassed / (Duration / 2);
 if (P < 1) then
  begin
   Result := 1 / 2 * P * P * P * P * P;
  end
 else
  begin
   P := P - 2;
   Result := 1 / 2 * (P * P * P * P * P + 2);
  end;
end;


Button
var
 C, D: Cardinal;
const
 Duration = 3000;
 POS_START = 200; // Größe eingeklappt
 POS_END = 500; // Größe ausgeklappt
begin
 C := GetTickCount;
 D := 0;
 if Height >= POS_END then
  begin
   // EDIT
   // Mit diesem Spagetti-Code funktioniert es auch. Aber das ist sicherlich alles mehr als falsch
   // POS_END := POS_END - POS_START - (POS_END - Height);
   // POS_START := Height;

   while (D < Duration) do
    begin
     Height := POS_START - Round(POS_END * QuintEasing(D, Duration));
     Application.ProcessMessages;
     D := GetTickCount - C;
    end;

   Caption := '1. ' + Height.ToString;

   // Formular wird beim ersten Klick erst einmalig 700px hoch eingestellt (falsch) und fährt dann auf 200px runter (richtig)
  end
 else
  begin
   // POS_START := Height;
   // POS_END := POS_END - POS_START;
   // Diese beiden Zeilen reparieren dieses If-Statement. Oben verstehe ich die umgekehrte Logik aber nicht und finde keine Lösung.

   while (D < Duration) do
    begin
     Height := POS_START + Round(POS_END * QuintEasing(D, Duration));
     Application.ProcessMessages;
     D := GetTickCount - C;
    end;

   Caption := '2. ' + Height.ToString;

   // Ein anschließender Klick hier stellt das Formular 700px hoch ein (falsch)
  end;
Edit: siehe Kommentar bei Height >= POS_END

stahli 16. Sep 2017 23:37

AW: Panel geschmeidig ein- und ausblenden?
 
Die Funktion ermittelt nicht, welche Höhe beim Start vorliegt, sondern entscheidet nur, ob von groß nach klein oder von klein nach groß animiert wird.

Fixen kannst Du das ggf. so:
Delphi-Quellcode:
    while (D < Duration) do
    begin
      H := POS_START + POS_END - Round(POS_END * QuintEasing(D, Duration));
      if (H < Height) then
        Height := H;
      Application.ProcessMessages;
      D := GetTickCount - C;
    end;
So entsteht allerdings am Anfang eine Verzögerung, da ja die getimeten Berechnungen trotzdem erfolgen.
Um das zu Verhindern müsstest Du die Berechnungsdauer (Duration) verkürzen oder besser in die Berechnung die Starthöhe einbeziehen.

Glados 16. Sep 2017 23:41

AW: Panel geschmeidig ein- und ausblenden?
 
Was denkst du denn über meine Änderung oben? Ich poste es hier unten noch einmal sauber neu:

Delphi-Quellcode:
Button
var
 C, D: Cardinal;
const
 Duration = 3000;
 POS_START = 200; // Größe eingeklappt
 POS_END = 500; // Größe ausgeklappt
begin
 C := GetTickCount;
 D := 0;
 if Height >= POS_END then
  begin
   // > ÄNDERUNG
   POS_END := POS_END - POS_START - (POS_END - Height);
   POS_START := Height;
   // < ÄNDERUNG

   while (D < Duration) do
    begin
     Height := POS_START - Round(POS_END * QuintEasing(D, Duration));
     Application.ProcessMessages;
     D := GetTickCount - C;
    end;

   Caption := '1. ' + Height.ToString;
  end
 else
  begin
   // > ÄNDERUNG
   POS_START := Height;
   POS_END := POS_END - POS_START;
   // < ÄNDERUNG

   while (D < Duration) do
    begin
     Height := POS_START + Round(POS_END * QuintEasing(D, Duration));
     Application.ProcessMessages;
     D := GetTickCount - C;
    end;

   Caption := '2. ' + Height.ToString;
  end;

stahli 17. Sep 2017 00:04

AW: Panel geschmeidig ein- und ausblenden?
 
Hast Du versucht, das zu compilieren?
Das geht so nicht, da man Pos_End nichts zuweisen kann, da das eine Konstante ist.

Ich hatte vorhin schonmal versucht, die Starthöhe zu berücksichtigen.
Das ist mir aber nicht gelungen.

Vielleicht Zacherl man noch einen Tipp geben.
Ich denke aber, dass eine Kurvenfunktion dafür die bessere Lösung wäre.
(Ich bin aber mathematisch leider eine Niete und tue mich schwer mit so etwas.)

Glados 17. Sep 2017 00:17

AW: Panel geschmeidig ein- und ausblenden?
 
Zitat:

Hast Du versucht, das zu compilieren?
Das geht so nicht, da man Pos_End nichts zuweisen kann, da das eine Konstante ist.
Habe daraus eine Variable gemacht.

Es funktioniert nun einwandfrei, egal bei welcher Starthöhe. Es geht wird immer auf Minimum und Maximum gesetzt. Dafür sieht das auch etwas anders aus jetzt
Delphi-Quellcode:
 if Height >= iMaxHeight then
  begin
   iNegative := -1;

   // > ÄNDERUNG
   iTmp := iMinHeight;
   iMaxHeight := iMaxHeight - iTmp - (iMaxHeight - Height);
   iTmp := Height;
  end
 else // if Height <= iMinHeight then
  begin
   iNegative := 1;

   // > ÄNDERUNG
   iTmp := Height;
   iMaxHeight := iMaxHeight - iTmp;
  end;

 while (D < 3000) do
  begin
   iTmpRes := iTmp + (Round(iMaxHeight * QuintEasing(D, 3000)) * iNegative);
 
   // mache was mit iTmpRes
  end;
Funktioniert so bestens.

Zitat:

(Ich bin aber mathematisch leider eine Niete und tue mich schwer mit so etwas.)
geht mir genau so. Ich habe an diesen 4 Zeilen stunden lang gesessen und bestimmt 50x kompiliert und getestet :D
Berechnet habe ich da nix, da ich nicht weiß wie. Ich habe einfach alle Variablen genommen, zusammengemixt und geguckt wann das Resultat bei allen Szenarien richtig ist.

Zacherl 18. Sep 2017 01:18

AW: Panel geschmeidig ein- und ausblenden?
 
Tatsächlich enthält mein Beispielcode einen kleinen Flüchtigkeitsfehler
Delphi-Quellcode:
<
statt
Delphi-Quellcode:
<=
in der Schleifenbedingung. Habe die Kurvenfunktionen mal etwas intuitiver gestaltet:
Delphi-Quellcode:
function EaseInQuart(T, B, C, D: Integer): Integer;
var
  T2: Single;
begin
  T2 := T / D;
  Result := Round(C * T2 * T2 * T2 * T2) + B;
end;

function EaseOutQuart(T, B, C, D: Integer): Integer;
var
  T2: Single;
begin
  T2 := T / D - 1;
  Result := Round(-C * (T2 * T2 * T2 * T2 - 1)) + B;
end;

function EaseInQuint(T, B, C, D: Integer): Integer;
var
  T2: Single;
begin
  T2 := T / D;
  Result := Round(C * T2 * T2 * T2 * T2 * T2) + B;
end;

function EaseOutQuint(T, B, C, D: Integer): Integer;
var
  T2: Single;
begin
  T2 := T / D - 1;
  Result := Round(C * (T2 * T2 * T2 * T2 * T2 + 1)) + B;
end;

function EaseOutBounce(T, B, C, D: Integer): Integer;
var
  T2: Single;
begin
  T2 := T / D;
  if (T2 < (1.0 / 2.75)) then
  begin
    Result := Round(C * (7.5625 * T2 * T2)) + B;
  end else
  if (T2 < (2.0 / 2.75)) then
  begin
    T2 := T2 - (1.5 / 2.75);
    Result := Round(C * (7.5625 * T2 * T2 + 0.75)) + B;
  end else
  if (T2 < (2.5 / 2.75)) then
  begin
    T2 := T2 - (2.25 / 2.75);
    Result := Round(C * (7.5625 * T2 * T2 + 0.9375)) + B;
  end else
  begin
    T2 := T2 - (2.625 / 2.75);
    Result := Round(C * (7.5625 * T2 * T2 + 0.984375)) + B;
  end;
end;

function EaseOutElastic(T, B, C, D: Integer): Integer;
var
  S, P, A, T2: Double;
begin
  if (T = 0) then
  begin
    Exit(B);
  end;
  T2 := T / D;
  if (T2 = 1) then
  begin
    Exit(B + C);
  end;
  P := D * 0.3;
  A := C;
  if (A < Abs(C)) then
  begin
    S := P / 4;
  end else
  begin
    S := P / (2 * PI) * ArcSin(C / A);
  end;
  Result := Round(A * Power(2, -10 * T2) * Sin((T2 * D - S) * (2 * PI) / P)) + C + B;
end;

procedure TForm1.Button3Click(Sender: TObject);
const
  DURATION = 1000;
  POS_START =   8;
  POS_END  = 520;
var
  C, D: Cardinal;
begin
  // Über einen Zeitraum von 1 Sekunde von Position 8 nach Position 320 verschieben
  C := GetTickCount;
  D := 0;
  while (D <= DURATION) do
  begin
    Button1.Left := EaseOutElastic(D, POS_START, POS_END - POS_START, DURATION);
    Application.ProcessMessages;
    D := GetTickCount - C;
  end;
  Button1.Left := POS_END;
end;

procedure TForm1.Button4Click(Sender: TObject);
const
  DURATION = 1000;
  POS_START = 520;
  POS_END  =   8;
var
  C, D: Cardinal;
begin
  // Über einen Zeitraum von 1 Sekunde von Position 320 nach Position 8 verschieben
  C := GetTickCount;
  D := 0;
  while (D <= DURATION) do
  begin
    Button1.Left := EaseOutElastic(D, POS_START, POS_END - POS_START, DURATION);
    Application.ProcessMessages;
    D := GetTickCount - C;
  end;
  Button1.Left := POS_END;
end;
Erklärung der Parameter:
Delphi-Quellcode:
T = Vergangene Zeit
B = Startwert
C = Änderung
D = Dauer der Animation
Als Bonus gibt es noch die
Delphi-Quellcode:
EaseOutBounce
und die
Delphi-Quellcode:
EaseOutElastic
Kurven, welche ich persönlich ganz schick finde.

Headbucket 18. Sep 2017 07:01

AW: Panel geschmeidig ein- und ausblenden?
 
Guten Morgen,

mich wundert sehr, dass hier noch niemand TSplitView genannt hat. Dieser ist ja genau für deinen Zweck gemacht und es wurde bereits eine Animation für das Ein- und Ausblenden implementiert.

Er ist allerdings erst ab den neueren Delphi-Versionen enthalten.

Grüße
Headbucket

Glados 20. Sep 2017 00:33

AW: Panel geschmeidig ein- und ausblenden?
 
Zacherl, ich habe mit deinen neuen Codes zwei Units für mich erstellt. Jetzt kann ich die Animationen gut verwenden.

Darf ich die hier reinstellen und teilen?

Zacherl 20. Sep 2017 01:11

AW: Panel geschmeidig ein- und ausblenden?
 
Zitat:

Zitat von Glados (Beitrag 1381678)
Darf ich die hier reinstellen und teilen?

Aber gerne doch :thumb:

Glados 20. Sep 2017 11:03

AW: Panel geschmeidig ein- und ausblenden?
 
Hier die zwei Units. ich packe das in Delphi-Tags, damit jeder Zugriff hat. Es sind circa 200 Zeilen Code.
Delphi-Quellcode:
// Ein Aufruf, Um ButtonY auf FormX horizontal von Position 20 zu 200 und umgekehrt zu animieren, könnte so aussehen
TUtilsEasingAnimations.AnimateControl(FormX.ButtonY, 20, 200, TAnimationDirection.adHorizontal, TAnimationType.adChangeLeftTop);

// Wenn man den Button in der Breite von 20 zu 200 und umgekehrt animieren möchte so
TUtilsEasingAnimations.AnimateControl(FormX.ButtonY, 20, 200, TAnimationDirection.adHorizontal, TAnimationType.adChangeWidthHeight);
Selbes gilt für adVertical statt adHorizontal.

Speziell AnimateControl hat Optimierungsbedarf. Für mich aber funktioniert es. Kritik gerne willkommen.

### 99% von diesem Code wurde von Zacherl erstellt ###

Core Unit
Delphi-Quellcode:
unit Utils.Easing.Core;

interface

uses Math;

type
 TUtilsEasingCore = class
 public
  class function EaseInQuart(T, B, C, D: Integer): Integer; static;
  class function EaseOutQuart(T, B, C, D: Integer): Integer; static;
  class function EaseInQuint(T, B, C, D: Integer): Integer; static;
  class function EaseOutQuint(T, B, C, D: Integer): Integer; static;
  class function EaseOutBounce(T, B, C, D: Integer): Integer; static;
  class function EaseOutElastic(T, B, C, D: Integer): Integer; static;
 end;

implementation

// Quart easing
// ==============================================================================================================================================
class function TUtilsEasingCore.EaseInQuart(T, B, C, D: Integer): Integer;
var
 T2: Single;
begin
 T2 := T / D;
 Result := Round(C * T2 * T2 * T2 * T2) + B;
end;

class function TUtilsEasingCore.EaseOutQuart(T, B, C, D: Integer): Integer;
var
 T2: Single;
begin
 T2 := T / D - 1;
 Result := Round(-C * (T2 * T2 * T2 * T2 - 1)) + B;
end;
// ==============================================================================================================================================

// Quint easing
// ==============================================================================================================================================
class function TUtilsEasingCore.EaseInQuint(T, B, C, D: Integer): Integer;
var
 T2: Single;
begin
 T2 := T / D;
 Result := Round(C * T2 * T2 * T2 * T2 * T2) + B;
end;

class function TUtilsEasingCore.EaseOutQuint(T, B, C, D: Integer): Integer;
var
 T2: Single;
begin
 T2 := T / D - 1;
 Result := Round(C * (T2 * T2 * T2 * T2 * T2 + 1)) + B;
end;
// ==============================================================================================================================================

// Bounce/elastic easing
// ==============================================================================================================================================
class function TUtilsEasingCore.EaseOutBounce(T, B, C, D: Integer): Integer;
var
 T2: Single;
begin
 T2 := T / D;
 if (T2 < (1.0 / 2.75)) then
  begin
   Result := Round(C * (7.5625 * T2 * T2)) + B;
  end
 else if (T2 < (2.0 / 2.75)) then
  begin
   T2 := T2 - (1.5 / 2.75);
   Result := Round(C * (7.5625 * T2 * T2 + 0.75)) + B;
  end
 else if (T2 < (2.5 / 2.75)) then
  begin
   T2 := T2 - (2.25 / 2.75);
   Result := Round(C * (7.5625 * T2 * T2 + 0.9375)) + B;
  end
 else
  begin
   T2 := T2 - (2.625 / 2.75);
   Result := Round(C * (7.5625 * T2 * T2 + 0.984375)) + B;
  end;
end;

class function TUtilsEasingCore.EaseOutElastic(T, B, C, D: Integer): Integer;
var
 S, P, A, T2: Double;
begin
 if (T = 0) then
  begin
   Exit(B);
  end;
 T2 := T / D;
 if (T2 = 1) then
  begin
   Exit(B + C);
  end;
 P := D * 0.3;
 A := C;
 if (A < Abs(C)) then
  begin
   S := P / 4;
  end
 else
  begin
   S := P / (2 * PI) * ArcSin(C / A);
  end;
 Result := Round(A * Power(2, -10 * T2) * Sin((T2 * D - S) * (2 * PI) / P)) + C + B;
end;
// ==============================================================================================================================================

end.
Unit, welche für die Animation zuständig ist
Delphi-Quellcode:
unit Utils.Easing.Animations;

interface

uses Winapi.Windows, Vcl.Controls, Vcl.Forms, SysUtils;

type
 TAnimationDirection = (adHorizontal, adVertical);
 TAnimationType = (adChangeWidthHeight, adChangeLeftTop);

type
 TUtilsEasingAnimations = class
 private const
  AnimateDuration = 300;
 public
  class procedure AnimateControl(Control: TWinControl; iStart, iEnd: Integer; aAnimationDirection: TAnimationDirection; aAnimationType: TAnimationType); static;
 end;

implementation

uses Utils.Easing.Core;

// Main animation controller
// ==============================================================================================================================================
// Example call:
// TUtilsEasingAnimations.AnimateControl(FormX.ButtonY, 20, 200, TAnimationDirection.adHorizontal, TAnimationType.adChangeLeftTop);
class procedure TUtilsEasingAnimations.AnimateControl(Control: TWinControl; iStart, iEnd: Integer; aAnimationDirection: TAnimationDirection; aAnimationType: TAnimationType);
var
 C, D: Cardinal;
 iTmpControlDimension, iTmp, iStartTmp, iEndTmp: Integer;
 procedure setControlDimension(iDimension: Integer);
 begin
  if aAnimationDirection = TAnimationDirection.adHorizontal then
   begin
    if aAnimationType = TAnimationType.adChangeWidthHeight then
     Control.Width := iDimension
    else if aAnimationType = TAnimationType.adChangeLeftTop then
     Control.Left := iDimension
   end
  else if aAnimationDirection = TAnimationDirection.adVertical then
   begin
    if aAnimationType = TAnimationType.adChangeWidthHeight then
     Control.Height := iDimension
    else if aAnimationType = TAnimationType.adChangeLeftTop then
     Control.Top := iDimension;
   end;
 end;

begin
 // Prepare needed data
 // ==============================================================================================================================================
 C := GetTickCount;
 D := 0;
 iTmpControlDimension := 0;

 if aAnimationDirection = TAnimationDirection.adHorizontal then
  begin
   if aAnimationType = TAnimationType.adChangeWidthHeight then
    iTmpControlDimension := Control.Width
   else if aAnimationType = TAnimationType.adChangeLeftTop then
    iTmpControlDimension := Control.Left;
  end
 else if aAnimationDirection = TAnimationDirection.adVertical then
  begin
   if aAnimationType = TAnimationType.adChangeWidthHeight then
    iTmpControlDimension := Control.Height
   else if aAnimationType = TAnimationType.adChangeLeftTop then
    iTmpControlDimension := Control.Top;
  end;

 if iTmpControlDimension = 0 then
  Exit;

 iStartTmp := iTmpControlDimension;
 iEndTmp := iEnd;
 if iStartTmp >= iEndTmp then
  begin
   iTmp := iStart;
   iStart := iEnd;
   iEnd := iTmp;
  end;
 // ============================================================================================================================================== end;

 // Display the animation
 // ==============================================================================================================================================
 while (D <= TUtilsEasingAnimations.AnimateDuration) do
  begin
   setControlDimension(TUtilsEasingCore.EaseOutQuint(D, iStart, iEnd - iStart, TUtilsEasingAnimations.AnimateDuration));

   Application.ProcessMessages;
   D := GetTickCount - C;
  end;
 setControlDimension(iEnd);
 // ==============================================================================================================================================
end;
// ==============================================================================================================================================

end.

EWeiss 20. Sep 2017 11:21

AW: Panel geschmeidig ein- und ausblenden?
 
Ich würde mal die Namensgebung überdenken in 5 Jahren weist selbst du nicht mehr für was "T, B, C, D" überhaupt steht.
Ob das sinnvoll ist das gesamte Alphabet in seinem Code zu implementieren mag dahin gestellt sein.

gruss

Der schöne Günther 20. Sep 2017 11:26

AW: Panel geschmeidig ein- und ausblenden?
 
Zitat:

Zitat von Headbucket (Beitrag 1381414)
dass hier noch niemand TSplitView genannt hat. Dieser ist ja genau für deinen Zweck gemacht und es wurde bereits eine Animation für das Ein- und Ausblenden implementiert.

Haben wir auch seit 1, 2 Jahren in Verwendung. Macht keine Mucken, läuft flüssig, alles ok :thumb:

Glados 20. Sep 2017 12:01

AW: Panel geschmeidig ein- und ausblenden?
 
Zitat:

Ich würde mal die Namensgebung überdenken in 5 Jahren weist selbst du nicht mehr für was "T, B, C, D" überhaupt steht.
Wie erwähnt sind 99% des Codes nicht von mir sondern von Zacherl.

Zitat:

dass hier noch niemand TSplitView genannt hat. Dieser ist ja genau für deinen Zweck gemacht und es wurde bereits eine Animation für das Ein- und Ausblenden implementiert.
Das kann man nicht vergleichen. Versuche mal mit TSplitView einen Button von oben nach unten "easen" zu lassen.

EWeiss 20. Sep 2017 12:27

AW: Panel geschmeidig ein- und ausblenden?
 
Zitat:

Wie erwähnt sind 99% des Codes nicht von mir sondern von Zacherl.
Na ja es könnte ja nicht schaden einen Vermerk hinzuzuschreiben wofür T, B, C, D denn steht.
Es gibt immer so viele unnütze Kommentare im Code, hier wäre es angebracht ;)

Ist nur ein Ratschlag.
Zacherl hat es drauf keine frage :)

Ich sehe gerade Zacherl hat es zum Code addiert.. das solltest du unbedingt berichtigen.
Als Kommentar hinzufügen.

Zitat:

T = Vergangene Zeit
B = Startwert
C = Änderung
D = Dauer der Animation
gruss

Glados 20. Sep 2017 12:40

AW: Panel geschmeidig ein- und ausblenden?
 
Abgesehen davon, dass ich diese Prefix-Gebung hier unten mag, so sieht es etwas einleuchtender aus denke ic h
Delphi-Quellcode:
  class function EaseInQuart(iTimeElapsed, iValueStart, iValueEnd, iAnimationTime: Integer): Integer;
  ...

EWeiss 20. Sep 2017 12:44

AW: Panel geschmeidig ein- und ausblenden?
 
Zitat:

Zitat von Glados (Beitrag 1381712)
Abgesehen davon, dass ich diese Prefix-Gebung hier unten mag, so sieht es etwas einleuchtender aus denke ic h
Delphi-Quellcode:
  class function EaseInQuart(iTimeElapsed, iValueStart, iValueEnd, iAnimationTime: Integer): Integer;
  ...

Dito! :)

gruss

Headbucket 21. Sep 2017 09:50

AW: Panel geschmeidig ein- und ausblenden?
 
Hey
Zitat:

Zitat von Glados (Beitrag 1381708)
Zitat:

dass hier noch niemand TSplitView genannt hat. Dieser ist ja genau für deinen Zweck gemacht und es wurde bereits eine Animation für das Ein- und Ausblenden implementiert.
Das kann man nicht vergleichen. Versuche mal mit TSplitView einen Button von oben nach unten "easen" zu lassen.

Ich hatte mich an den Eingangspost gehalten. Dort war lediglich gefragt ein Panel von links reinzufahren. Und genau dafür ist TSplitView gemacht. Oder willst du das Panel nur partiell von links einblenden?

Grüße
Headbucket

Glados 21. Sep 2017 09:54

AW: Panel geschmeidig ein- und ausblenden?
 
Nein mir geht es darum eine rund-um-Lösung zu haben für alles mögliche. Größe ändern, Position ändern, horizontal, vertikal. Und genau das klappt jetzt alles.

stahli 21. Sep 2017 11:20

AW: Panel geschmeidig ein- und ausblenden?
 
Wenn Du mal Zeit und Lust hast, kannst Du ja mal ein kurzes Video veröffentlichen. Würde mich mal interessieren, wie das bei einer realen Anwendung rüber kommt.

Kann cool sein, kann aber auch nerven...

Glados 21. Sep 2017 11:56

AW: Panel geschmeidig ein- und ausblenden?
 
Würde ich sofort machen aber ich bin nur ein Hobby-Tipper ohne ernsthafte Projekte.

Uwe Raabe 21. Sep 2017 12:31

AW: Panel geschmeidig ein- und ausblenden?
 
Mich stört ja gewaltig das
Delphi-Quellcode:
Application.ProcessMessages
, weil dabei ja erst die gesamte Message-Queue abgearbeitet wird. Das kann sowohl zu einem Stocken der Animation führen, als auch unerwünschte Nebeneffekte oder Reentrance-Probleme verursachen. Bei simplen Testanwendungen kommt das zwar sicher kaum vor, aber in komplexen Anwendungen ist sowas durchaus denkbar.

Eine mögliche Alternative wäre, das Neuzeichnen während der Animation selbst anzustoßen. Dazu müsste in der Uses-Anweisung noch ein Winapi.Messages aufgenommen, in der AnimateControl-Methode eine neue Variable deklariert und die Animationsschleife wie folgt abgeändert werden:

Delphi-Quellcode:
uses
  Utils.Easing.Core, Winapi.Messages;
...
var
  dc: HDC;
...
 dc := GetDC(Control.Handle);
 try
   while (D <= TUtilsEasingAnimations.AnimateDuration) do
    begin
     setControlDimension(TUtilsEasingCore.EaseOutQuint(D, iStart, iEnd - iStart, TUtilsEasingAnimations.AnimateDuration));

     Control.Perform(WM_PRINTCLIENT, dc, PRF_CLIENT);
     D := GetTickCount - C;
    end;
 finally
   ReleaseDC(Control.Handle, dc);
 end;
Ist jetzt natürlich nicht in allen Lebenslagen getestet, aber für die genannten Button-Beispiele funktioniert es.

Als angenehmer Nebeneffekt kann die Referenz auf Vcl.Forms in der Uses-Anweisung nun getrost entfernt werden.

Glados 21. Sep 2017 12:41

AW: Panel geschmeidig ein- und ausblenden?
 
Habe es gerade mal mit dem Ändern einer TForm getestet.

Angenommen das Formular hat zwei Labels
[-Form1----------------]
| Label1----------------|
|-------------------------|
|-------------------------|
|-------------------------|
|-------------------------|
| Label2----------------|
[-------------------------]

Wenn ich nun mit der Animation und den Änderungen das Formular in der Höhe verkleinern möchte, dann sieht man wie Label2 zwar neu gezeichnet wird, aber die Stellen wo Label2 vorher zu sehen war,
haben noch eine Art "Label2-Caption"-Leiche, die erst entfernt wird, wenn das Formular seine Zielhöhe erreicht hat.

Aviator 21. Sep 2017 12:52

AW: Panel geschmeidig ein- und ausblenden?
 
Hast du es mal mit
Delphi-Quellcode:
DoubleBuffered := True
aller Parents der Labels probiert? Inklusive der Form?

stahli 21. Sep 2017 12:54

AW: Panel geschmeidig ein- und ausblenden?
 
Offenbar spielst Du ja "nur" herum und hast kein dringendes Projekt...
Falls Du Dich mal mit dem Thema GUI ein wenig befassen willst, kannst Du mal hier schauen: http://www.delphipraxis.net/175033-f...-schlecht.html

Die VCL ist für solche grafischen Spielereien nicht direkt ausgelegt.

Von der Idee her sollte eigentlich FMX besser dafür geeignet sein, in der Praxis aber auch nicht unbedingt.

Glados 21. Sep 2017 12:56

AW: Panel geschmeidig ein- und ausblenden?
 
Steht alles schon auf DoubleBuffered. Das von mir genutzte Label ist ein JvHTLabel. Aber auch ein Panel zeigt diese Artefakte ohne ProcessMessages.

Uwe Raabe 21. Sep 2017 12:58

AW: Panel geschmeidig ein- und ausblenden?
 
Zitat:

Zitat von Glados (Beitrag 1381764)
Wenn ich nun mit der Animation und den Änderungen das Formular in der Höhe verkleinern möchte, dann sieht man wie Label2 zwar neu gezeichnet wird, aber die Stellen wo Label2 vorher zu sehen war,
haben noch eine Art "Label2-Caption"-Leiche, die erst entfernt wird, wenn das Formular seine Zielhöhe erreicht hat.

Wieso ändert Label2 überhaupt seine Position, wenn das Form in der Höhe verkleinert wird?


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:07 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz