我正在使用BDS 2007 ......
当用户在我们的应用程序中双击网格单元格时,我们会在网格上显示一个模态窗口。我开发了下面的例程,在网格上绘制一个动画帧。它通过在窗口显示之前逐步绘制和擦除帧来进行放大(非常类似于Windows UI动画)。窗口关闭后,再次调用相同的例程(使用bExpand = False),以使窗口的UI效果重新进入网格单元格。
此例程在Windows XP中运行良好但在启用Aero时行为不当(在Windows 7 中没有 Aero的情况下正常工作)。动画帧重绘得很慢......即使我注释掉DelayMSecs行(这只是一个重复调用Sleep(0)的循环,直到iDelay毫秒数已经过去)。
当再次调用例程时(窗口关闭后)它同样慢,加上在屏幕上留下一个框架,再加上一个(现已关闭的)窗口留下的鬼图像,基本上是alpha混合的网格的正常显示内容。
大多数例程的代码计算变化的矩形大小。实际抽奖只有三行。擦除动画rect:
ScreenCanvas.Rectangle( r ); //draw frame
SysStuff.DelayMSecs( iDelay ); //
ScreenCanvas.Rectangle( r ); //pmXOR pen ...erase frame
关于为什么在Aero下这么慢,或者我需要改变什么的想法?
procedure T_fmExplore.AnimateRects(ASourceRect, ADestRect: TRect; bExpand:
boolean; bAdjustSourceForFrame: boolean = True);
{ Draw animation frames in steps, for transition from ASourceRect to ADestRect.
bExpand: determines whether the animation rect is expanding or contracting.
bAdjustSourceForFrame: resize the animation rect smaller for the window frame. }
const
MINSTEPS = 10; //Min redraws of the animation rect (frame)
MAXSTEPS = 20; //30 was too many, too slow
MAXDELAY = 100; //Delay between drawing each rect frame
MINDELAY = 1;
var
iSteps: integer;
DeltaHt: Integer; //Rect size chg for each redraw of animation window
DeltaWidth: Integer;
DeltaTop : integer; //Origin change for each redraw
DeltaLeft : integer;
TgtWidth, TgtHt: Integer;
iTemp: Integer;
iDelay: integer;
r : TRect; //Animation frame's rect
ScreenCanvas: TCanvas;
begin
r := ASourceRect;
TgtWidth := ADestRect.Right - ADestRect.Left; //Target rect's Width
TgtHt := ADestRect.Bottom - ADestRect.Top; //Target rect's Height
//Initially Deltas hold total chg in Width & Height
DeltaWidth := TgtWidth - (r.Right - r.Left); //TgtWidth - old width
DeltaHt := TgtHt - (r.Bottom - r.Top);
//For smooth animation we adjust number of iSteps & Delay relative to the window area.
//Larger window = more iSteps and shorter Delay between drawing each step.
iSteps := Max( DeltaWidth * DeltaHt div 6500, MINSTEPS );
iSteps := Min( iSteps, MAXSTEPS );
//Now convert Deltas to the delta in window rect size
DeltaWidth := DeltaWidth div iSteps;
DeltaHt := DeltaHt div iSteps;
DeltaTop := (ADestRect.Top - ASourceRect.Top) div iSteps;
DeltaLeft := (ADestRect.Left - ASourceRect.Left) div iSteps;
iDelay := Max( MAXDELAY div iSteps, MINDELAY );
ScreenCanvas := TCanvas.Create;
try
ScreenCanvas.Handle := GetDC( 0 ); //Desktop
try
with ScreenCanvas do begin
Pen.Color := clWhite; //Do NOT use clBlack with pmXOR mode: with (r=0, g=0, b=0) there are no bytes to XOR.
Pen.Mode := pmXOR; //MUST use pmXOR pen, so 2nd Rectangle call (see below) erases what we drew.
Pen.Style := psSolid;
Pen.Width := 3; //Thin line. Was: Pen.Width := GetSystemMetrics(SM_CXFRAME);
Brush.Style := bsClear;
if bAdjustSourceForFrame then
InflateRect(ASourceRect, -Pen.Width, -Pen.Width);
repeat
iTemp := (r.Bottom - r.Top) + DeltaHt; //Height
if (bExpand and (iTemp > TgtHt)) or (not bExpand and (iTemp < TgtHt)) then begin
r.Top := ADestRect.Top;
r.Bottom := Top + TgtHt;
end else begin
r.Top := r.Top + DeltaTop; //Assign Top first...Bottom is calc'd from it
r.Bottom := r.Top + iTemp;
end;
iTemp := (r.Right - r.Left) + DeltaWidth; //Width
if (bExpand and (iTemp > TgtWidth)) or (not bExpand and (iTemp < TgtWidth)) then begin
r.Left := r.Left + DeltaLeft;
r.Right := r.Left + TgtWidth;
end else begin
r.Left := r.Left + DeltaLeft; //Assign Left first...Right is calc'd from it
r.Right := r.Left + iTemp;
end;
ScreenCanvas.Rectangle( r ); //draw frame
SysStuff.DelayMSecs( iDelay ); //
ScreenCanvas.Rectangle( r ); //pmXOR pen ...erase frame
until (r.Right - r.Left = TgtWidth) and (r.Bottom - r.Top = TgtHt);
end;
finally
ReleaseDC( 0, ScreenCanvas.Handle );
ScreenCanvas.Handle := 0;
end;
finally
ScreenCanvas.Free;
end;
end;