Rad Studio 12.3中FireMonkey实现平滑滚动文本的技术问询
在TViewPort3D实现像素级平滑滚动文本(叠加背景)
核心思路
放弃逐字符切换的方式,改用离屏缓冲区预渲染完整文本,通过定时器控制像素级偏移,每次截取缓冲区的对应区域绘制到TViewPort3D,同时叠加背景图,实现无卡顿的平滑滚动。
具体实现步骤
1. 初始化资源与变量
在窗体类中定义成员变量,用于存储离屏缓冲区、背景图、滚动偏移量和定时器:
private: TBitmap *OffScreenBmp; // 存储完整滚动文本的离屏位图 TBitmap *BackgroundBmp; // 背景图 int ScrollOffset; // 滚动偏移量(像素级) TTimer *ScrollTimer; // 控制滚动帧率
在窗体创建事件中完成初始化:
void __fastcall TForm1::FormCreate(TObject *Sender) { // 初始化离屏位图,采用32位格式提升渲染效率 OffScreenBmp = new TBitmap(); OffScreenBmp->PixelFormat = pf32bit; // 加载背景图(确保尺寸与TViewPort3D匹配,或后续适配) BackgroundBmp = new TBitmap(); BackgroundBmp->LoadFromFile("background.png"); // 初始化滚动定时器,15ms间隔对应约60帧/秒的流畅度 ScrollTimer = new TTimer(this); ScrollTimer->Interval = 15; ScrollTimer->OnTimer = &ScrollTimerTimer; ScrollTimer->Enabled = true; // 预渲染完整滚动文本到离屏缓冲区 RenderFullScrollText(); }
2. 预渲染完整文本到离屏缓冲区
编写函数一次性渲染全部滚动文本(包括一份重复文本,实现无缝循环):
void __fastcall TForm1::RenderFullScrollText() { const UnicodeString ScrollText = "需要滚动的完整文本内容"; // 计算文本总宽度 TCanvas *TempCanvas = new TCanvas(); TempCanvas->Handle = GetDC(0); TempCanvas->Font = ViewPort3D1->Font; int TextWidth = TempCanvas->TextWidth(ScrollText); int TextHeight = TempCanvas->TextHeight(ScrollText); ReleaseDC(0, TempCanvas->Handle); delete TempCanvas; // 设置离屏位图尺寸:文本宽度 + ViewPort3D宽度(实现循环滚动无空白) OffScreenBmp->Width = TextWidth + ViewPort3D1->Width; OffScreenBmp->Height = ViewPort3D1->Height; // 清空离屏画布为透明,方便叠加背景 OffScreenBmp->Canvas->Brush->Color = clNone; OffScreenBmp->Canvas->FillRect(Rect(0, 0, OffScreenBmp->Width, OffScreenBmp->Height)); // 绘制文本(垂直居中) OffScreenBmp->Canvas->Font = ViewPort3D1->Font; OffScreenBmp->Canvas->Font->Color = clWhite; // 自定义文本颜色 int YPos = (OffScreenBmp->Height - TextHeight) / 2; OffScreenBmp->Canvas->TextOut(0, YPos, ScrollText); // 绘制重复文本到末尾,实现无缝循环 OffScreenBmp->Canvas->TextOut(TextWidth, YPos, ScrollText); }
3. 定时器驱动滚动绘制
在定时器事件中更新偏移量,依次绘制背景图和截取的文本区域:
void __fastcall TForm1::ScrollTimerTimer(TObject *Sender) { // 更新偏移量,超过文本总宽度后重置(实现循环) ScrollOffset += 1; // 每次滚动1像素,可调整数值改变速度 int TextTotalWidth = OffScreenBmp->Width - ViewPort3D1->Width; if (ScrollOffset >= TextTotalWidth) ScrollOffset = 0; // 锁定ViewPort3D画布,避免UI线程冲突 ViewPort3D1->Canvas->Lock(); // 第一步:绘制背景图 ViewPort3D1->Canvas->Draw(0, 0, BackgroundBmp); // 若背景尺寸不匹配,改用StretchDraw适配: // ViewPort3D1->Canvas->StretchDraw(Rect(0,0,ViewPort3D1->Width,ViewPort3D1->Height), BackgroundBmp); // 第二步:截取离屏文本的可视区域,绘制到ViewPort3D TRect SrcRect = Rect(ScrollOffset, 0, ScrollOffset + ViewPort3D1->Width, ViewPort3D1->Height); TRect DestRect = Rect(0, 0, ViewPort3D1->Width, ViewPort3D1->Height); ViewPort3D1->Canvas->CopyRect(DestRect, OffScreenBmp->Canvas, SrcRect); // 解锁画布 ViewPort3D1->Canvas->Unlock(); }
4. 资源清理
在窗体销毁事件中释放资源,避免内存泄漏:
void __fastcall TForm1::FormDestroy(TObject *Sender) { delete OffScreenBmp; delete BackgroundBmp; delete ScrollTimer; }
关键优化与注意事项
- 采用
pf32bit位图格式,大幅提升渲染和复制效率 - 预渲染文本避免重复计算布局,减少性能消耗
- 定时器间隔设为15-30ms,平衡流畅度与系统资源占用
- 绘制重复文本实现无缝循环,避免滚动到末尾出现空白
- 若文本字体或内容变化,需重新调用
RenderFullScrollText更新离屏缓冲区
内容的提问来源于stack exchange,提问作者Barry Andrews
相关产品推荐
相关产品推荐

