字符串"撞墙反弹"成效

<MFC_4>字符串"撞墙反弹"效果

本文由BlueCoder编写

转载请说明出处:

http://blog.csdn.net/crocodile__

欢迎大家和我交流编程心得

 

 

 

 

-------------------------------------------------------------------------------------------------------------------------------------

字符串"撞墙反弹"效果

-------------------------------------------------------------------------------------------------------------------------------------

 

 

 

 

一、效果演示

 

(1). 通过右键菜单来更改字体

<MFC_4>字符串"撞墙反弹"成效

 

(2). "撞墙反弹"效果

<MFC_4>字符串"撞墙反弹"成效

 

 

二、准备工作

 

(1). 看看IDE支持的字体

“工具”--> "选项" --> "字体与颜色"

<MFC_4>字符串"撞墙反弹"成效

 

在"字体"中查看支持的字体

<MFC_4>字符串"撞墙反弹"成效

 

(2). 通过IDE创建一个右键菜单

<MFC_4>字符串"撞墙反弹"成效

这里仅用了四个字体:

名称 ID号
宋体 ID_SONG
华文彩云 ID_CAIYUN
华文行楷 ID_XINGKAI
微软雅黑 ID_YAHEI

 

(3). 讲解两个API函数

1). CDC::GetTextExtent

CSize GetTextExtent(
   const CString& str 
) const;

在当前DC中,获取字符串str的size大小

 

2).CMenu::TrackPopupMenu

BOOL TrackPopupMenu(
   UINT nFlags,		//标记——详情见msdn,上面讲的很详细,这里不再赘述
   int x,		//x坐标
   int y,		//y坐标
   CWnd* pWnd,		//所属窗口
   LPCRECT lpRect = 0	//忽略, 没用
);


对于此函数需要注意的是右键菜单的坐标问题:详见代码分析

 

 

三、代码剖析

 

(1). 预先浏览一下头文件中的相关声明

#include<atlimage.h>//使用CImage类所需添加的头文件

//计时器ID定义
#define ID_TIMER	100


class CTextView : public CView
{
protected: // 仅从序列化创建
	CTextView();
	DECLARE_DYNCREATE(CTextView)

// 属性
public:
	CTextDoc* GetDocument() const;

// 操作
public:

// 重写
public:
	virtual void OnDraw(CDC* pDC);  // 重写以绘制该视图
	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:

// 实现
public:
	virtual ~CTextView();
#ifdef _DEBUG
	virtual void AssertValid() const;
	virtual void Dump(CDumpContext& dc) const;
#endif

//私有成员变量
private:
	CMenu		m_popupMenu;

	CImage		m_bk;//背景

	CSize		m_sClient;//客户区大小

	CString		m_str;//字体动画的内容
	CString		m_sFont;//字体名称
	CPoint		m_ptStr;//字符串的左上角点
	CSize		m_sStr;//字符串的大小
	COLORREF	m_fontColor;//字符串颜色

	CSize		m_move;//字符串的移动距离(x, y)

	bool		m_isPause;//是否暂停
//私有成员函数
private:
	inline void SetStrPoint();//设置字符串的左上角起点
	inline void SetMove();//设置字符串的移动距离
	inline void MoveString();//移动字符串

protected:

// 生成的消息映射函数
protected:
	DECLARE_MESSAGE_MAP()
public:
	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	afx_msg void OnTimer(UINT_PTR nIDEvent);
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
protected:
	//重写OnCommand函数, 用于相应WM_COMMAND消息——右键菜单消息
	virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
};


(2). 设置字符串的左上角起点

void CTextView::SetStrPoint()
{
	//设置种子
	srand(GetTickCount());

	//保证坐标为整十数
	m_ptStr.x = rand() % ((m_sClient.cx - m_sStr.cx) / 10);
	m_ptStr.x *= 10;

	m_ptStr.y = rand() % ((m_sClient.cy - m_sStr.cy) / 10);
	m_ptStr.y *= 10;
}

 

(3). 设置字符串的移动距离

void CTextView::SetMove()
{
	//如果x坐标出了边界, 就反向
	if(m_ptStr.x < 0 || 
		m_ptStr.x > (m_sClient.cx - m_sStr.cx))
	{
		m_move.cx = -m_move.cx;

		//变换字体颜色
		m_fontColor = RGB(rand()%256,
						  rand()%100,
						  rand()%256);
	}

	//如果y坐标出了边界, 就反向
	if(m_ptStr.y < 0 ||
		m_ptStr.y > (m_sClient.cy - m_sStr.cy))
	{
		m_move.cy = -m_move.cy;

		//变换字体颜色
		m_fontColor = RGB(rand()%256,
						  rand()%100,
						  rand()%256);
	}
}

 

(4).  移动字符串

void CTextView::MoveString()
{
	m_ptStr.x += m_move.cx;
	m_ptStr.y += m_move.cy;
}

 

(5). 鼠标左键控制字符串的启动和暂停

void CTextView::OnLButtonDown(UINT nFlags, CPoint point)
{
	//如果暂停, 就关闭计时器
	if(m_isPause)
	{
		this->KillTimer(ID_TIMER);

		//下一次点击时, 就是不暂停
		m_isPause = false;
	}
	//否则, 重新启动计时器
	else
	{
		this->SetTimer(ID_TIMER, 40, NULL);

		//下一次点击时, 就是暂停
		m_isPause = true;
	}

	CView::OnLButtonDown(nFlags, point);
}

 

(6). 鼠标右键选择字体

void CTextView::OnRButtonDown(UINT nFlags, CPoint point)
{
	/*
		因为当前获取的坐标point是客户区坐标,而TrackPopupMenu函数坐标参数
		是屏幕坐标,因此应该使用ClientToScreen函数,转换为屏幕坐标
	*/
	this->ClientToScreen(&point);

	//显示右键菜单
	m_popupMenu.TrackPopupMenu(
		TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON,
		point.x, point.y, this, NULL);

	CView::OnRButtonDown(nFlags, point);
}

 

(7). 响应WM_COMMAND消息,设置选择的字体

BOOL CTextView::OnCommand(WPARAM wParam, LPARAM lParam)
{
	//修改字体
	switch(LOWORD(wParam))
	{
	case ID_SONG:
		m_sFont = "宋体";
		break;
	case ID_CAIYUN:
		m_sFont = "华文彩云";
		break;
	case ID_XINGKAI:
		m_sFont = "华文行楷";
		break;
	case ID_YAHEI:
		m_sFont = "微软雅黑";
		break;
	}

	//重绘
	InvalidateRect(NULL, FALSE);

	return CView::OnCommand(wParam, lParam);
}


 

(8). 绘制客户区——响应WM_PAINT消息

void CTextView::OnDraw(CDC* pDC)
{
	CTextDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	//创建内存DC, 以用双缓冲绘图
	CDC		bufferDC;
	bufferDC.CreateCompatibleDC(NULL);

	CBitmap bufferBmp;
	bufferBmp.CreateCompatibleBitmap(pDC, m_sClient.cx, m_sClient.cy);

	bufferDC.SelectObject(bufferBmp);

	//------------------------------------------------------
	//				以下都在内存DC中绘图
	//------------------------------------------------------

	//获取指定的字体
	CFont font;
	font.CreatePointFont(150, (LPCTSTR)m_sFont);

	bufferDC.SelectObject(font);

	m_sStr = bufferDC.GetTextExtent(m_str);

	//贴背景
	bufferDC.SetStretchBltMode(COLORONCOLOR);
	bufferDC.SetTextColor(m_fontColor);
	m_bk.StretchBlt(bufferDC, 0, 0, m_sClient.cx, m_sClient.cy, SRCCOPY);

	//贴字符串
	bufferDC.SetBkMode(TRANSPARENT);
	bufferDC.TextOutW(m_ptStr.x, m_ptStr.y, m_str);

	//------------------------------------------------------
	//				将内存DC中的图绘制到窗口客户区DC中
	//------------------------------------------------------
	pDC->BitBlt(0, 0, m_sClient.cx, m_sClient.cy, &bufferDC, 0, 0, SRCCOPY);

	//删除字体
	font.DeleteObject();
	//删除内存bmp
	bufferBmp.DeleteObject();
	//删除DC
	bufferDC.DeleteDC();
}


 

 

四、零积分源码下载

点击下载源码

 

 

 

 

———————————————————————————————

———————————————————————————————

 

欢迎大家留下自己的意见,交流交流自己的心得是一件很惬意的事情

 

欢迎大家转载鄙人的博文,不过请在转载时注明转自BlueCoder

 

欢迎大家继续关注鄙人的博客^_^

 

1楼Enjoy__Coding54分钟前
赞,效果不错
Re: crocodile_52分钟前
回复Enjoy__Codingnthx , :)