用delphi的VCL库部件到底能不能实现不闪烁的动画???(200分)

  • 主题发起人 主题发起人 xiongjz
  • 开始时间 开始时间
X

xiongjz

Unregistered / Unconfirmed
GUEST, unregistred user!
在Image的canvas上画图时,即使是只在上面加一个点,或将Image平移时,它也会将原来的图象全部
擦掉再重画,这样在画布画点画线,或移动image时,就会产生闪烁
请问怎样解决这个问题,其大虾们指点,如果有满意的答案,可在加200分.
 
改用 TPaintBox 控件(System page)
TImage 主要用来显示图像文件
TPaintBox 则是用来在Canvas上画图的
 
图象组件如Image、文本如Label在移动时,屏幕会发生闪烁,如果图象较小或者文本中字体较小,
可能不会很明显。以下代码:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Image1.Left := Image1.Left + 1;
Label1.Left := Label1.Left + 1;
end;

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



  闪烁的情况会随图形的增大而增加,会随文字的变大、增多而增加。简单地分析一下它的原因,是因为每一次一
个组件的位置属性如Left,Top等更改时,会自动触发它的刷新、重画过程。当重画的次数增大到某一程度时,我们便
会感觉到闪烁。对于此问题,解决的方案很奇怪也有点难以解释,就是不要更改图象或者标签的位置,而让它们放在
一个面板(Panel)内,然后只修改面板的位置属性,就不会闪烁。

  为了使面板不会影响瞻观,可以使它的AutoSize属性为真。试试这个:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Panel1.Left := Panel1.Left + 1;
Label1.Left := Label1.Left + 1;
end;

这样一来,图象的闪烁的基本消除了,就算多大的图象也不会闪烁。但是有一个问题,就是面板叠放的个
数不能是偶数个,否则又会出现闪烁。真奇怪。

对于标签就没有这么幸运了,尽管放在面板上它的闪烁有所减少,但仍然较明显,属于不能忍受的程度,
特别在领导面前演示时就更不好了。经过版主多方发掘,终于发现在LMD这套里组件里有一个面板:
TLMDSimpleLabel,我们将标签放在此面板上移动面板就不会闪烁了。您不妨一试。 

 
如果你一定要用TImage,可以这样:

1、将TImage隐藏
2、在Form的OnPaint事件中,画此TImage
3、在修改TImage后,重新画此TImage到Form的Canvas上

此方法简单易行。
 
To clicker:我认为用Timage 和用TpaintPox没什么区别,因为每次bitmap都会刷新重画

To www 既然是动画,当然要求是透明的,如果背后加一个panel 的话,久会把后面的背景都盖住
怎么办?

 
你是否了解TPaintBox和TImage是如何响应消息处理画图的?
建议你看看TPaintBox和TImage的定义,以及他们的Paint的实现方法,在下定论.
在ExtCtrls.pas文件中.

BTW: I am slicker, not clicker
 
sorry! slicker 下不为例!!
具体描述一下我的问题吧:
我要在Form上放一个部件(TImage er else ,但不要从TWinControl类继承下来的),用来动态显示
一些线条数字,数字线条要不断的更新,不能闪烁,要求背景可以透明, 大家给个建议.
 
TImage和TPaintBox都是从TGraphicControl的子类,而不是TWinControl的子类;
Delphi 的继承关系,从TControl类开始处理Windows消息,而Delphi的可视控件都是TControl
的子类,同时Delphi采用的事件驱动机制,而不是消息驱动,比如,可视控件的Left和Top,
当同时改变时消息处理为:
恢复背景,
left, top 改变
在新位置显示

而Delphi的事件处理如下:
left改变->调用SetBounds->子控件Invalidate, Parent Invalidate, self Invalidate
top 改变->调用SetBounds->子控件Invalidate, Parent Invalidate, self Invalidate
......
这仅仅是简单的描述,事实上激发了更多的事件,还有子控件,父控件....

所以采用事件驱动机制会产生更多的无畏重复操作,但是事件驱动比消息处理机制容易理解,
再加上Delphi的类封装,在使用上更简单一些.

对于一般应用,还是尽量使用VCL,对于你的需求,可能难以达到理想的效果.
要减少无畏的处理,尤其是WM_PAINT,WM_NCPAINT,WM_ERASEBKGND灯会引起,重画窗口的消息,
最好的方法就是抛弃Delphi的控件,直接使用Windows API.从你的叙述来看,应用相对简单一些,
使用API也容易控制,只需要调用画图的API即可.

相关的API如下:
BOOL InvalidateRgn(

HWND hWnd, // handle of window with changed update region
HRGN hRgn, // handle of region to add
BOOL bErase // erase-background flag
);
BOOL InvalidateRect(

HWND hWnd, // handle of window with changed update region
CONST RECT *lpRect, // address of rectangle coordinates
BOOL bErase // erase-background flag
);

如果要求更高,只有使用DirectX实现了

很遗憾
 
如果是画图的话,先用底部的色彩画一遍,再画图,改变时也一样就不会闪烁。。
 
用flash,先做好flash动画,贴上去就行了。
 
我觉得采用象素重描似乎好一些。
 
用PaintBox,再在onpaint事件中重画图形 ,或者用缓冲用一个Tbitmap先画好背景,
画动画时,调出背景,再把变化的部分画上去.我试过这两种方法,画一副动画都没有问题
但是,如果在一个屏幕上同时显示多副动画,并且动画有重叠的时候,动画重叠的部分闪的厉害
这个问题是否一定要用到双缓冲才能解决.
 
如果动画所需的图片的是位图的话,那么完全可以实现无闪烁的动画。
//AniList是一个ImageList.
procedure TForm1.Timer1Timer(Sender: TObject);
const
i:integer=13;
begin
AniList.Draw(Canvas,8,36,i);
if i=13 then
i:=0
else
i:=i+1;
end;
 
一副动画不闪烁我已经实现了,但是如果把两副动画叠在一块,仍闪的厉害
 
产生闪烁的原因是上下两个控件均要重画,因此下面的控件重画会导致上面
控件的重画,如果上面的控件经常移动上下,两个控件交替重画产生闪烁。
比较好的方法是只使用一个控件(通常就只一个TForm)然后在内部生成
一个TBitmap,然后在需要的时候重画TBitmap,响应FormPaint,FormResize
等(即双缓冲机制),可以实现无闪操作。
 
看来是一定要用什么双缓冲了,但是如何在画面上响应mouse,键盘消息,这下可烦了。
一切都要重写,VCL库部件的功能要自己编程实现。
 
其实TImage本身会自动重画,所以对于在其上面进行大量作图操作的话,

使用TImage实在不智。

TImage适用于显示一幅图像,而又不经常改动且

不想通过代码对其进行更新操作时时用。
 
T0:JohnsonGuo
其实用TImage与TPaintBox都一样,虽能解决单副图象的闪烁问题,但只要动态图象部分被遮盖
刷新是肯定会产生闪烁。
不知有何高见?
 
多人接受答案了。
 
后退
顶部