动画闪烁的根本原因是动画背景和动画元素的交替输出。解决方案是使用双缓冲技术。
所谓双缓冲就是将每一帧要显示的动画,都放到一个内存DC里,每一帧都是一张动画。这样就避免了动画元素交替输出的问题。
下面的代码中,我把所有要输出的动画都放到了m_dc中,最后绘制完整的m_dc。每一帧都是m_dc动画,就不存在动画交替输出的问题了。
void Butterfly::OnPaint()
{
CPaintDC dc(this); // 创建设备上下文对象
m_dc.DeleteDC(); // 删除原有的设备上下文对象
if (!m_dc.CreateCompatibleDC(NULL)) { // 创建与屏幕兼容的设备上下文对象
m_dc.DeleteDC();
return;
}
CBitmap tmpBmp{}; // 创建临时位图对象
tmpBmp.CreateCompatibleBitmap(&CClientDC(NULL), m_winRect.Width(), m_winRect.Height()); // 创建与屏幕兼容的位图对象
m_dc.SelectObject(tmpBmp); // 将临时位图对象选入设备上下文对象
tmp_dc.CreateCompatibleDC(&dc); // 创建与设备上下文对象兼容的临时设备上下文对象
tmp_dc.SelectObject(m_grass); // 将草地位图对象选入临时设备上下文对象
// 加载背景
m_dc.StretchBlt(0, 0, m_winRect.Width(), m_winRect.Height()
, &tmp_dc, 0, 0, m_grassSize.cx, m_grassSize.cy, SRCCOPY);
//m_dc.DeleteDC();
m_bf_dc.CreateCompatibleDC(&m_dc); // 创建与设备上下文对象兼容的蝴蝶设备上下文对象
m_bf_dc.SelectObject(m_butterfly[bf_index]); // 将蝴蝶位图对象选入蝴蝶设备上下文对象
CRect FillRect{ 50,50,800,800 }; // 创建填充矩形对象
m_dc.FillSolidRect(FillRect, RGB(0, 0, 0)); // 使用黑色填充矩形区域
int numStr = 0; // 用于标记蝴蝶数量的变量
// 设置透明色
COLORREF transColor = RGB(255, 0, 255);
POSITION pos = bfList.GetHeadPosition(); // 获取蝴蝶列表的头位置迭代器
while (pos != NULL) {
bf& tmp = bfList.GetNext(pos); // 获取蝴蝶对象的引用
m_dc.TransparentBlt(tmp.moveRect.left, tmp.moveRect.top, m_bfSize.cx, m_bfSize.cy
, &m_bf_dc, 0, 0, m_bfSize.cx, m_bfSize.cy, transColor); // 使用透明色绘制蝴蝶
CheckDirection(tmp); // 检查蝴蝶的移动方向
// 标记蝴蝶数量
CString str;
str.Format(_T("%d"), numStr++);
m_dc.SetBkColor(RGB(255, 255, 255));
m_dc.TextOutW(tmp.moveRect.left, tmp.moveRect.top, str); // 在蝴蝶位置绘制标记文字
}
dc.BitBlt(0, 0, m_winRect.Width(), m_winRect.Height()
, &m_dc, 0, 0, SRCCOPY); // 将内存设备上下文对象中的图像拷贝到屏幕上
tmp_dc.DeleteDC(); // 删除临时设备上下文对象
m_dc.DeleteDC(); // 删除设备上下文对象
m_bf_dc.DeleteDC(); // 删除蝴蝶设备上下文对象
return;
}