You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 01:43:15