「ウィンドウアニメーション」とは筆者が勝手に付けた名前です。ウィンドウを表示したり、非表示にするときにぱっと変化するのではなく、 アニメーションしながら、消えたり、出てきたりします。
アニメーションの方法は、右側(左側)、上(下)から少しずつ現れたり、消したりできます。
中心に向かって消したり、中心から出したりもできます。
これを実現するにはAnimateWindow関数を使うと簡単です。Windows95ではサポートされていません。
BOOL AnimateWindow( HWND hwnd, // ウィンドウのハンドル DWORD dwTime, // アニメーションの時間(ミリ秒) DWORD dwFlags // アニメーションの種類 );hwndには、アニメーションするウィンドウのハンドルを指定します。
dwTimeには、再生時間をミリ秒で指定します。
dwFlagsには、アニメーションの種類を次の組み合わせで指定します。
| 定数 | 意味 |
|---|---|
| AW_SLIDE | スライドアニメーション |
| AW_ACTIVATE | ウィンドウをアクティブにします。 |
| AW_BLEND | フェードイン。dwTimeには、アニメーションのフレーム数を指定。 AW_HIDEとは併用できません。 |
| AW_HIDE | ウィンドウを隠します。 |
| AW_CENTER | AW_HIDEと併用すると中心に向かって隠します。 併用しないときは中心から現れてきます。 |
| AW_HOR_POSITIVE | 左から右へのアニメーション。AW_CENTERとは併用できない。 |
| AW_HOR_NEGATIVE | 右から左へのアニメーション。AW_CENTERとは併用できない。 |
| AW_VER_POSITIVE | 上から下へのアニメーション。AW_CENTERとは併用できない。 |
| AW_VER_NEGATIVE | 下から上へのアニメーション。AW_CENTERとは併用できない。 |
関数が成功すると0以外が返り、失敗すると0が返されます。
さて、ウィンドウにビットマップなどが表示されているとき、この関数でウィンドウを 非表示にすると、クライアント領域に描画されていたものが一瞬で消えて、非クライアント領域 のみがアニメーションします。出現するときも同じです。
これでは、つまらないですね。ではどうすればよいのでしょうか。
WM_PRINTCLIENTメッセージを処理します。このメッセージが来たら、WM_PAINTメッセージが 来たときと同様の処理を行います。これで希望通りの動作となります。
LRESULT CALLBACK WindowProc( HWND hwnd, // ウィンドウハンドル UINT uMsg, // WM_PRINTCLIENT WPARAM wParam, // hdc LPARAM lParam // オプション );WM_PRINTCLIENTメッセージは、特定のデバイスコンテキストのクライアント領域 を描画するリクエストためにウィンドウに送られます。(通常はプリンタのデバイスコンテキスト)
では、サンプルを見てみましょう。
// aniwin01.rcの一部
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
MYMENU MENU
BEGIN
POPUP "ファイル(&F)"
BEGIN
MENUITEM "終了(&X)...", IDM_END
END
POPUP "オプション(&O)"
BEGIN
MENUITEM "ウィンドウを隠す1(&1)", IDM_HIDE1
MENUITEM "ウィンドウを隠す2(&2)", IDM_HIDE2
MENUITEM "ウィンドウを隠す3(&3)", IDM_HIDE3
MENUITEM "ウィンドウを隠す4(&4)", IDM_HIDE4
MENUITEM "ウィンドウを隠す5(&5)", IDM_HIDE5
MENUITEM "ウィンドウを隠す6(&6)", IDM_HIDE6
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
MYBMP BITMAP "bitmap1.bmp"
普通のメニューと、ビットマップのリソース・スクリプトです。適当なビットマップをクライアント領域に表示します。
// aniwin01.cpp
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
char szClassName[] = "aniwin01"; //ウィンドウクラス
HINSTANCE hInst;
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
LPSTR lpsCmdLine, int nCmdShow)
{
MSG msg;
BOOL bRet;
hInst = hCurInst;
if (!InitApp(hCurInst))
return FALSE;
if (!InitInstance(hCurInst, nCmdShow))
return FALSE;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1) {
break;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
WinMain関数です。後で便利なようにインスタンスハンドルをグローバル変数にコピーしています。
//ウィンドウ・クラスの登録
ATOM InitApp(HINSTANCE hInst)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc; //プロシージャ名
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;//インスタンス
wc.hIcon = (HICON)LoadImage(NULL,
MAKEINTRESOURCE(IDI_APPLICATION),
IMAGE_ICON,
0,
0,
LR_DEFAULTSIZE | LR_SHARED);
wc.hCursor = (HCURSOR)LoadImage(NULL,
MAKEINTRESOURCE(IDC_ARROW),
IMAGE_CURSOR,
0,
0,
LR_DEFAULTSIZE | LR_SHARED);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "MYMENU"; //メニュー名
wc.lpszClassName = (LPCSTR)szClassName;
wc.hIconSm = (HICON)LoadImage(NULL,
MAKEINTRESOURCE(IDI_APPLICATION),
IMAGE_ICON,
0,
0,
LR_DEFAULTSIZE | LR_SHARED);
return (RegisterClassEx(&wc));
}
//ウィンドウの生成
BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
HWND hWnd;
hWnd = CreateWindow(szClassName,
"猫でもわかるアニメートWindow", //タイトルバーにこの名前が表示されます
WS_OVERLAPPEDWINDOW, //ウィンドウの種類
CW_USEDEFAULT, //X座標
CW_USEDEFAULT, //Y座標
614, //幅
448, //高さ
NULL, //親ウィンドウのハンドル、親を作るときはNULL
NULL, //メニューハンドル、クラスメニューを使うときはNULL
hInst, //インスタンスハンドル
NULL);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
この辺はいつもと同じです。
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
int id;
HDC hdc, hdc_mem;
PAINTSTRUCT ps;
HBITMAP hBmp;
BITMAP bmp_info;
int wx, wy;
switch (msg) {
case WM_CREATE:
AnimateWindow(hWnd, 2000, AW_CENTER);
SetForegroundWindow(hWnd);
break;
case WM_COMMAND:
switch (LOWORD(wp)) {
case IDM_END:
SendMessage(hWnd, WM_CLOSE, 0, 0);
break;
case IDM_HIDE1:
AnimateWindow(hWnd, 2000, AW_HOR_POSITIVE | AW_HIDE);
Sleep(500);
AnimateWindow(hWnd, 2000, AW_HOR_NEGATIVE);
break;
case IDM_HIDE2:
AnimateWindow(hWnd, 2000, AW_SLIDE | AW_HOR_POSITIVE | AW_HIDE);
Sleep(500);
AnimateWindow(hWnd, 2000, AW_SLIDE | AW_HOR_NEGATIVE);
break;
case IDM_HIDE3:
AnimateWindow(hWnd, 2000, AW_CENTER | AW_HIDE);
Sleep(500);
AnimateWindow(hWnd, 2000, AW_CENTER);
break;
case IDM_HIDE4:
AnimateWindow(hWnd, 2000, AW_HOR_POSITIVE | AW_HIDE);
Sleep(500);
AnimateWindow(hWnd, 1000, AW_BLEND);
break;
case IDM_HIDE5:
AnimateWindow(hWnd, 2000, AW_HOR_POSITIVE | AW_VER_POSITIVE | AW_HIDE);
Sleep(500);
AnimateWindow(hWnd, 2000, AW_HOR_NEGATIVE | AW_VER_NEGATIVE);
break;
case IDM_HIDE6:
AnimateWindow(hWnd, 2000,
AW_SLIDE | AW_HOR_POSITIVE | AW_VER_POSITIVE | AW_HIDE);
Sleep(500);
AnimateWindow(hWnd, 2000,
AW_SLIDE | AW_HOR_NEGATIVE | AW_VER_NEGATIVE);
break;
}
break;
case WM_PRINTCLIENT:
hdc = (HDC)wp;
hBmp = (HBITMAP)LoadImage(hInst,
"MYBMP",
IMAGE_BITMAP,
0, 0,
LR_DEFAULTCOLOR);
GetObject(hBmp, (int)sizeof(BITMAP), &bmp_info);
wx = bmp_info.bmWidth;
wy = bmp_info.bmHeight;
hdc_mem = CreateCompatibleDC(hdc);
SelectObject(hdc_mem, hBmp);
BitBlt(hdc, 0, 0, wx, wy, hdc_mem, 0, 0, SRCCOPY);
DeleteObject(hBmp);
DeleteDC(hdc_mem);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
hBmp = (HBITMAP)LoadImage(hInst,
"MYBMP",
IMAGE_BITMAP,
0, 0,
LR_DEFAULTCOLOR);
GetObject(hBmp, (int)sizeof(BITMAP), &bmp_info);
wx = bmp_info.bmWidth;
wy = bmp_info.bmHeight;
hdc_mem = CreateCompatibleDC(hdc);
SelectObject(hdc_mem, hBmp);
BitBlt(hdc, 0, 0, wx, wy, hdc_mem, 0, 0, SRCCOPY);
DeleteObject(hBmp);
DeleteDC(hdc_mem);
EndPaint(hWnd, &ps);
break;
case WM_CLOSE:
id = MessageBox(hWnd,
"終了してもよろしいですか",
"確認",
MB_YESNO | MB_ICONQUESTION);
if (id == IDYES) {
AnimateWindow(hWnd, 2000, AW_CENTER | AW_HIDE);
DestroyWindow(hWnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, msg, wp, lp));
}
return 0;
}
ウィンドウプロシージャです。WM_CREATEメッセージが来たら、早速AnimateWindow関数でアニメートしながら メインウィンドウを表示します。表示の場合はAW_CENTERのみでWS_ACTIVATEは 加えなくても大丈夫です。
次にSetForegroundWindow関数で、メインウィンドウをアクティブにします。 なお、SetForegroundWindow関数については第254章を参照してください。
メニューからIDM_HIDE1からIDM_HIDE6までが選択されたら、いろいろな組み合わせで ウィンドウを隠して、0.5秒後にまた出しています。
特にIDM_HIDE1とIDM_HIDE2、IDM_HIDE5とIDM_HIDE6の違い(AW_SLIDEが有るか無いか)に 注意してください。
WM_PRINTCLIENTメッセージが来たら、WM_PAINTメッセージの時とほとんど同じ処理をします。 ただ、この時WPARAM値がデバイスコンテキストハンドルになるので注意してください。
WM_PAINTメッセージが来たら、ビットマップを表示します。
WM_CLOSEメッセージが来たとき、MessageBoxの問いに「はい」と答えると AnimateWindow関数でアニメーションしながらウィンドウを消していきます。
今回も簡単でした。ダイアログボックスの場合も同様な処理ができるのか実験してみてください。
実際の画面です。
(アニメーションの途中で画面キャプチャーしても元々のウィンドウがそのままです。)
Update 06/May/2003 By Y.Kumei