本文由BlueCoder編寫 轉載請說明出處:
http://blog.csdn.net/crocodile__/article/details/10486095
我的郵箱:[email protected] 歡迎大家和我交流編程心得
我的微博:BlueCoder_黎小華 歡迎光臨^_^
今年年初入手了一部諾基亞新款WP8手機——Lumia 620 經典藍,用起來感覺很不錯,很流暢、界面很清新
到現在,用了大概有大半年時間了,一直很好奇WP8中磁貼動態翻轉的實現算法——使用過WP8手機的朋友都知道,這個功能很有3D的效果,看起來感覺很不錯
但是,它到底是如何實現的呢?
今兒,我就來和大家一起剖析一下它的實現細節
WP8中磁貼動態翻轉功能細節:
(1)將當前圖標逐漸縮小(這個縮小的倍率很講究)
(2)當前圖標縮小到一定程度——基本上看不太清楚的時候——就變換另外一張圖標
(3)將另外一張圖標逐漸放大,直至和原圖標大小一致爲止
(4)停頓一會兒,繼續(1)、(2)、(3)的操作
由此,可見這個看似有3D效果的磁貼翻轉功能也就是使用2D技術實現的——畢竟是在一個平面上——但是這個過程比較快,人的肉眼有記憶推遲特點,因此看起來很連貫,就像是將一張圖360°翻轉過來一樣(其實和看視頻的原理差不多)
使用過WP8手機的朋友應該都知道"天氣通"這個應用吧,它可以產生磁貼放到WP8手機桌面中,並能實現翻轉功能,我也就借用這個應用的圖標來模擬
下面來看看,我模擬的程序的實現效果:
怎麼樣,還不錯吧?呵呵^_^
…………
功能細節大家知道了,可是我們如何用win32純c語言來實現呢?
OK,我的對策是"對症下藥":
<1>首先必須實現縮放位圖的功能,這個可以使用StretchBlt這個方法(熟悉Win32的應該知道這個函數吧),它可以按照指定的大小來縮放這個位圖
<2>其次,所謂"逐漸縮放"、"停頓一會兒"——當然第一時間想到的就是計時器和Sleep這個函數來實現
使用到的技術主要就是這兩個,下面着重講解一下具體的代碼細節:
<1>變量瀏覽
//全局變量(用來控制每一次縮放的大小,注意:只是縮放位圖的高度、寬度不變)
int height[] = {144, 88, 32, 10, 75, 137, 200};
//回調函數中的靜態局部變量
static HBITMAP hBmp[2]; //存放正反面位圖句柄
static SIZE sBmp, sClient; //位圖大小、客戶區大小(用於縮放)
static POINT ptBmp; //位圖位置(保證始終居中顯示)
static int curIndex, isFirst; //當前索引(height數組)、是否爲正面(用於交換位圖)
<2>WndProc中的實現細節:
case WM_CREATE:
//加載位圖
hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
hBmp[0] = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1));
hBmp[1] = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP2));
GetObject(hBmp[0], sizeof(BITMAP), &bmp);
sBmp.cx = bmp.bmWidth;
sBmp.cy = bmp.bmHeight;
//設置計時器
SetTimer(hwnd, ID_TIMER, 120, NULL);
return 0;
case WM_SIZE:
//獲取客戶區大小
sClient.cx = LOWORD(lParam);
sClient.cy = HIWORD(lParam);
//修改位圖當前位置座標
ptBmp.x = (sClient.cx - sBmp.cx) / 2;
ptBmp.y = (sClient.cy - sBmp.cy) / 2;
return 0;
case WM_PAINT:
//按照一定比例縮放位圖
hdc = BeginPaint(hwnd, &ps);
hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, hBmp[isFirst]);
SetStretchBltMode(hdc, COLORONCOLOR);
StretchBlt(hdc, ptBmp.x, ptBmp.y, sBmp.cx, sBmp.cy, hdcMem, 0, 0, 200, 200, SRCCOPY);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
return 0;
case WM_TIMER:
//修改當前顯示的位圖高度
sBmp.cy = height[curIndex];
//如果位圖高度變爲最小(也就是幾乎看不太清的程序),就交換位圖
if(sBmp.cy == height[3])
{
isFirst = !(isFirst & 1);
}
//如果位圖高度之前是原始大小,那麼應該停頓一會兒,這裏停頓當前線程1秒
else if(sBmp.cy == height[0])
{
Sleep(1000);
}
//修改位圖顯示位置,保證居中顯示
ptBmp.y = (sClient.cy - sBmp.cy) / 2;
//索引下一個高度(height)
curIndex = (curIndex + 1) % NUM;
//重繪
InvalidateRect(hwnd, NULL, TRUE);
return 0;