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

Delphi实现平滑页面擦除滚动过渡的性能问题求助

Delphi 平滑页面滚动过渡实现问题

我用Delphi开发类似老虎机的应用,需要实现网页/移动端那样的平滑擦除滚动过渡效果,但当前方案存在编码和性能问题:

  • 用BitBlt处理1920x1080分辨率过渡时,调整步长和循环参数后效果仍不理想,小步长会导致程序卡顿
  • 尝试的定时器代码逻辑有问题,页面直接切换而非滚动推送
  • 最终需要实现1530x870区域的平滑过渡,解决大屏问题后300x300小尺寸场景也需适用

解决方案

GDI优化方案(适配Delphi)

针对原代码的性能瓶颈,从减少冗余操作、优化绘制逻辑、正确帧率控制三个方向改进:

  1. 避免重复复制位图:原代码每次循环全量复制imgFirst到临时位图,改为提前创建固定尺寸的缓冲位图,仅更新变化区域
  2. 减少BitBlt调用损耗:合并不必要的绘制操作,仅绘制过渡帧的关键区域
  3. 用TTimer替代忙等待:避免占用CPU资源,控制稳定帧率(如60帧/秒,每帧间隔约16ms)

改进后的Delphi代码示例:

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    FFirstBmp, FSecondBmp, FBufferBmp: TBitmap;
    FCurrentStep: Integer;
    FTotalSteps: Integer;
    FStepSize: Integer;
    FTargetWidth, FTargetHeight: Integer;
  public
    procedure StartTransition;
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // 设置目标过渡区域尺寸
  FTargetWidth := 1530;
  FTargetHeight := 870;
  
  // 初始化位图资源
  FFirstBmp := TBitmap.Create;
  FFirstBmp.LoadFromFile('C:\TransitionTest\bg1.bmp');
  FSecondBmp := TBitmap.Create;
  FSecondBmp.LoadFromFile('C:\TransitionTest\bg2.bmp');
  
  // 创建缓冲位图,避免直接绘制到Form导致闪烁
  FBufferBmp := TBitmap.Create;
  FBufferBmp.SetSize(FTargetWidth, FTargetHeight);
  
  // 设置定时器间隔,对应约60帧/秒
  Timer1.Interval := 16;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // 释放资源
  FFirstBmp.Free;
  FSecondBmp.Free;
  FBufferBmp.Free;
end;

procedure TForm1.StartTransition;
begin
  // 计算过渡步数和步长,可根据需求调整速度
  FTotalSteps := FTargetWidth div 4;
  FStepSize := FTargetWidth div FTotalSteps;
  FCurrentStep := 0;
  Timer1.Enabled := True;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  DrawWidth: Integer;
begin
  Inc(FCurrentStep);
  DrawWidth := FCurrentStep * FStepSize;
  
  // 结束过渡逻辑
  if DrawWidth >= FTargetWidth then
  begin
    DrawWidth := FTargetWidth;
    Timer1.Enabled := False;
  end;

  // 绘制过渡帧到缓冲位图
  // 1. 绘制第二张图的可见部分(从右侧滑入)
  BitBlt(FBufferBmp.Canvas.Handle, 0, 0, DrawWidth, FTargetHeight,
         FSecondBmp.Canvas.Handle, FTargetWidth - DrawWidth, 0, SRCCOPY);
  // 2. 绘制第一张图的剩余部分
  BitBlt(FBufferBmp.Canvas.Handle, DrawWidth, 0, FTargetWidth - DrawWidth, FTargetHeight,
         FFirstBmp.Canvas.Handle, 0, 0, SRCCOPY);
  // 3. 一次性绘制到Form画布,避免闪烁
  BitBlt(Canvas.Handle, 0, 0, FTargetWidth, FTargetHeight,
         FBufferBmp.Canvas.Handle, 0, 0, SRCCOPY);
end;

DirectX方案(高性能大屏场景)

如果GDI性能仍无法满足需求,可采用Direct2D/Direct3D实现硬件加速,利用GPU提升绘制效率:

  • 加载两张位图作为Direct2D纹理
  • 每一帧根据过渡进度计算绘制区域,通过GPU完成拼接渲染
  • 支持垂直同步,确保画面平滑无撕裂

C++ Direct2D简化逻辑示例:

#include <d2d1.h>

ID2D1HwndRenderTarget* pRenderTarget = nullptr;
ID2D1Bitmap* pFirstBmp = nullptr;
ID2D1Bitmap* pSecondBmp = nullptr;
// 初始化Direct2D设备和位图代码省略

float progress = 0.0f;
const float targetWidth = 1530.0f;
const float targetHeight = 870.0f;

while (progress < 1.0f) {
    pRenderTarget->BeginDraw();
    pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::Black));
    
    // 计算两张图的绘制区域
    D2D1_RECT_F firstRect = D2D1::RectF(progress * targetWidth, 0, targetWidth, targetHeight);
    D2D1_RECT_F secondRect = D2D1::RectF(0, 0, progress * targetWidth, targetHeight);
    
    // 绘制过渡帧
    pRenderTarget->DrawBitmap(pFirstBmp, firstRect);
    pRenderTarget->DrawBitmap(pSecondBmp, secondRect);
    
    pRenderTarget->EndDraw();
    progress += 0.016f; // 对应60帧/秒的进度增量
    // 等待垂直同步或控制帧率,避免过度占用GPU
}

C# WPF参考方案

WPF自带动画框架,可快速实现平滑过渡,核心逻辑如下:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;

// 创建画布和图片控件
Canvas canvas = new Canvas { Width = 1530, Height = 870 };
Image imgFirst = new Image { Source = new BitmapImage(new Uri(@"C:\TransitionTest\bg1.bmp")) };
Image imgSecond = new Image { Source = new BitmapImage(new Uri(@"C:\TransitionTest\bg2.bmp")) };

// 设置第二张图初始位置在画布右侧
Canvas.SetLeft(imgSecond, 1530);
canvas.Children.Add(imgFirst);
canvas.Children.Add(imgSecond);

// 创建平移动画,0.5秒内从右侧滑入到左侧
DoubleAnimation slideAnimation = new DoubleAnimation(0, TimeSpan.FromSeconds(0.5));
imgSecond.BeginAnimation(Canvas.LeftProperty, slideAnimation);

内容的提问来源于stack exchange,提问作者brane

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 04:31:01