Nico Smit
Nico Smit

Reputation: 21

Is there a way to switch components mid-loop?

I am trying to animate a dropdown menu of 7 images using a for loop, using a different component to change after each iteration of the code inside the for loop. For example, the first time the loop runs: imgCourses7.Top is used, but the second time the loop runs (when I = 1) then imgCourses6.Top should be used instead.

iCoursesCount := 7;
iTotalLength := (6+41)*iCoursesCount;
imgCourses7.Top := 6;
imgCourses6.Top := 6;
imgCourses5.Top := 6;
imgCourses4.Top := 6;
imgCourses3.Top := 6;
imgCourses2.Top := 6;
imgCourses1.Top := 6;
for I := 0 to iCoursesCount -1 do
begin
  while not(imgCourses7.Top = iTotalLength - 41*(I+1)) do
  begin
    imgCourses8.Top :=  imgCourses8.Top + 6;
    sleep(8);
    application.ProcessMessages;
    if imgCourses7.Top >= iTotalLength - 41*(I+1) then
    begin
      imgCourses7.Top := iTotalLength - 41*(I+1);
      break;
    end;
  end;
end;

Upvotes: 0

Views: 102

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596582

Like @AndreasRejbrand said in a comment, you can use an array, eg:

var
  Images: array[0..6] of TImage;
  TargetTop: Integer;
  ...

...

Images[0] := imgCourses7;
Images[1] := imgCourses6;
Images[2] := imgCourses5;
Images[3] := imgCourses4;
Images[4] := imgCourses3;
Images[5] := imgCourses2;
Images[6] := imgCourses1;

iCoursesCount := Length(Images);
TargetTop := 6+(41*iCoursesCount);

for I := 0 to iCoursesCount-1 do begin
  Images[I].Top := 6;
end;

for I := 0 to iCoursesCount-1 do
begin
  Dec(TargetTop, 41);
  while Images[I].Top <> TargetTop do
  begin
    Images[I].Top := Images[I].Top + 6;
    Sleep(8);
    Application.ProcessMessages;
    if Images[I].Top >= TargetTop then
    begin
      Images[I].Top := TargetTop;
      Break;
    end;
  end;
end;

That being said, you really shouldn't be using a sleeping loop that requires Application.ProcessMessages() on each iteration. You might consider using a TTimer or TThread.ForceQueue() instead. Don't block the main UI thread unnecessarily. For example:

published
  procedure FormCreate(Sender: TObject);
private
  Images: array[0..6] of TImage;
  TargetTop: Integer;
  CurrentImage: Integer;
  procedure StartAnimatingMenu;
  procedure StartAnimatingNextMenuItem;
  procedure StepAnimateCurrentMenuItem;
  ...

...

procedure TMyForm.FormCreate(Sender: TObject);
begin
  Images[0] := imgCourses7;
  Images[1] := imgCourses6;
  Images[2] := imgCourses5;
  Images[3] := imgCourses4;
  Images[4] := imgCourses3;
  Images[5] := imgCourses2;
  Images[6] := imgCourses1;
end;

procedure TMyForm.StartAnimatingMenu;
var
  I: Integer;
begin
  for I := Low(Images) to High(Images) do begin
    Images[I].Top := 6;
  end;
  TargetTop := 6+(41*Length(Images));
  CurrentImage := -1;
  StartAnimatingNextMenuItem;
end;

procedure TMyForm.StartAnimatingNextMenuItem;
begin
  Inc(CurrentImage);
  if CurrentImage < Length(Images) then
  begin
    Dec(TargetTop, 41);
    StepAnimateCurrentMenuItem;
  end;
end;

procedure TMyForm.StepAnimateCurrentMenuItem;
begin
  if Images[CurrentImage].Top <> TargetTop then
  begin
    Images[CurrentImage].Top := Images[CurrentImage].Top + 6;
    TThread.ForceQueue(nil, StepAnimateCurrentMenuItem, 8);
  end
  else if Images[CurrentImage].Top >= TargetTop then
  begin
    Images[CurrentImage].Top := TargetTop;
    StartAnimatingNextMenuItem;
  end;
end;

Upvotes: 1

Related Questions