第 7 章: Windows アニメーション マネージャーの使用

アニメーションは時間の経過と共に画像が変化するプロセスであり、変化の間隔が短い場合、人間の目には連続した変化に見え、動いているような印象を与えます。アニメーションには直線的に変化するだけの単純なものもありますが、加速や減速を伴う変化など、より複雑なものもあり、いくつかのコンポーネントの動きで構成されるアニメーションもあります。こういったアニメーションにはすべて非常に複雑なコーディングが必要です。Windows 7 ではこうした課題に対処するために Windows アニメーション マネージャーというコンポーネントを提供しています。

Windows アニメーション マネージャーの使用

アニメーションは、ストーリーボードにまとめられた一連のフレームと見なすことができます。各フレームは複数の項目で構成され、フレーム間でこれらの項目が変化することでアニメーションが成立します。変化としては項目の色、位置、形があり、変化自体をスムーズに行ったり、不連続に行ったりすることができます。スムーズな変化の場合、一定の速さで変化させることも、変化を速くしたり遅くしたりすることも可能です。1 つの項目の複数のプロパティが変化することもあります。図 1 は、5 つのフレームで構成される単純なストーリーボードです。フレームごとに赤い円の位置が変化しています。

図 1 変数と遷移を示すストーリーボード

Ff934859.9b71dc5b-6a56-41a9-a27e-d05ab4cbd54e-thumb(ja-jp,MSDN.10).png

Windows アニメーション マネージャーの用語では、このような変化を "遷移" **といい、遷移によって "変数" と呼ばれる浮動小数点数値が変化します。アニメーション マネージャーは画面上のピクセルを変化させるわけではなく、変数を変化させます。開発者が提供する描画コードでは、この変数を使用する必要があります。遷移によって、一定の時間内 ("期間") に変数がどのように変化するかが決定されます。変数は特定の初期値で遷移を開始します。

遷移はつなぐことができます。つまり、変数の変化を記述する遷移の後に、同じ変数に対する別の変化を記述する遷移を続けることができます。遷移から次の遷移へ引き渡される時点で、変数は最初の遷移に従った特定の方法で変化し続けています (いわゆる "速度"**)。ある種の遷移はこの速度に依存するので、この値は次の遷移で使用されます。図 2 はスムーズな停止の遷移を表したものであり、この遷移の初期値と最終値、および期間が示されています。Windows アニメーション マネージャーでサポートされるその他の種類の遷移については、Windows Animation 開発者向けガイドの「ストーリーボードの構築」を参照してください。図中の矢印は、遷移の最初の速度を表しています。スムーズな停止の遷移の期間中、変数は初期値から最終値まで減少していきます。

図 2 スムーズな停止の遷移のプロパティ

Ff934859.f3e08554-786d-4c18-bd59-f98a319c8b1a(ja-jp,MSDN.10).png

この期間中、指定した方法で変数の値が変化することがアニメーション マネージャーによって保証され、アプリケーションは変数の値を取得して各フレームをレンダリングします。当然ながら、変数は遷移が開始してからの経過時間に依存します。したがって、アニメーション マネージャーには遷移が開始された正確な時刻、および各フレームが描画された時刻の正確な値が必要です。アニメーション マネージャーが使用できる高精度のタイマー オブジェクトが Windows によって提供されます。さらに、Windows は遷移ライブラリと呼ばれるオブジェクトで、いくつかの定義済みの遷移を提供しています。

アニメーションは、画面のピクセルを変化させれば終わりというわけではありません。ちらつきを防止するために、注意深くモニターの更新との同期を取る必要があります。開発者に代わってその処理を実行するのが Direct2D です。そのため Windows アニメーション マネージャーと Direct2D の組み合わせは、Windows 7 アプリケーションでアニメーションを提供する最高の方法であるといえます。

Windows アニメーション マネージャーの作成

Windows アニメーション マネージャーは COM オブジェクトであり、シングルスレッドの COM アパートメント (STA) で作成する必要があります。アニメーション マネージャーを使用するスレッド (通常、ウィンドウへのメッセージを処理するスレッド) は、CoInitialize を呼び出して初期化する必要があります。アプリケーションでアニメーション マネージャーを作成した後、1 つ以上の遷移をカプセル化するストーリーボード オブジェクトを作成できます。リスト 1 は、メイン オブジェクトを作成するコードです。このオブジェクトはすべてのアニメーションで使われており、あらゆるアプリケーションに組み込まれています。

リスト 1 アニメーション オブジェクトの作成

// アプリケーションで使用するオブジェクト
IUIAnimationManager* g_animationManager = nullptr;
IUIAnimationTimer* g_animationTimer = nullptr;
IUIAnimationTransitionLibrary* g_transitionLibrary = nullptr;

CoCreateInstance(
CLSID_UIAnimationManager,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&g_animationManager)
        );

CoCreateInstance(
CLSID_UIAnimationTimer,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&g_animationTimer)
        );
CoCreateInstance(
CLSID_UIAnimationTransitionLibrary,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&g_transitionLibrary)
        );
    }

アニメーション マネージャーでは定期的な時刻の更新が必要であり、アニメーション タイマー オブジェクトの存在理由もそこにあります。遷移ライブラリは、アニメーションでよく使われる遷移の遷移オブジェクトを提供するファクトリ オブジェクトです。

アニメーション変数の作成

アニメーション マネージャーはアニメーションの期間中、アニメーション変数を更新します。アニメーション変数の変化は遷移オブジェクトによって決定されるので、アニメーション変数は 1 つ以上の遷移オブジェクトに関連付けられている必要があります。したがって、変数は遷移を作成するコードにアクセス可能でなければなりません。変数の値は画面の描画に使用されます。そのため、変数はレンダリング コードからアクセス可能である必要があります。変数の寿命は 1 つのアニメーション期間に限られる場合がある一方、前回のアニメーションが終了した時点で次の回を開始してアニメーションを繰り返す場合には、変数オブジェクトが複数のアニメーションを通じて存在することになります。こういった実装上の細部は開発者が決定します。ただし重要なポイントとして、変数オブジェクトは添付先の遷移よりも寿命が長い場合があること、そして、アプリケーション内のさまざまなメソッドによってアクセス可能でなければならないことがあります。

リスト 2 に、アニメーション変数の作成方法を示します。IUIAnimationManager::CreateAnimationVariable の呼び出しによって、指定した初期値で変数を作成します。アプリケーションは IUIAnimationVariable インターフェイスを使用し、SetUpperBound および SetLowerBound メソッドを呼び出して変数の最大値と最小値を提供できるほか、GetValue を呼び出して変数の現在値を取得できます。

リスト 2 アニメーション変数の作成

// 変数は別の場所で宣言する
IUIAnimationVariable* m_variable;

if (nullptr == m_variable)
{
hr = g_animationManager->CreateAnimationVariable(initialValue, &m_variable);
}

ストーリーボード オブジェクトの作成と初期化

アプリケーションはアニメーションを開始する時点で、ストーリーボード オブジェクトを作成します。ストーリーボードには、アニメーションを記述する 1 つ以上の遷移が含まれます。これらの遷移オブジェクトとストーリーボードは、アニメーションの期間に限って存在します。アニメーションを繰り返すには、ストーリーボードと遷移の新しいインスタンスを作成する必要があります。

リスト 3 は、ストーリーボード オブジェクトを作成するコードです。このオブジェクトは、ストーリーボード オブジェクトの参照を返す IUIAnimationManager::CreateStoryboard メソッドの呼び出しによって作成します。このメソッドを呼び出すと、アニメーション マネージャーは、このストーリーボードに含まれるすべての遷移の期間中、ストーリーボードへのその他の参照を保持します。リスト 3 のコードで、アプリケーションがストーリーボード オブジェクトの初期化を完了した時点でストーリーボード オブジェクトの参照を解放しているのはこのためです。

ストーリーボードが 1 つ以上の遷移を使って初期化され、それぞれの遷移が特定の変数に関連付けられます。リスト 3 では、遷移ライブラリを使用して加速/減速のある遷移を作成しています。遷移の期間は 2 番目のパラメーターによって指定され、遷移が完了した時点で変数の値が endValue になります。この遷移の形状は 3 番目と 4 番目のパラメーターによって決定されます。accRatio の値は最初の加速に費やされる期間の比率、decRatio は最終値まで減速するのに費やされる期間の比率を表します。この遷移オブジェクトが、変化する変数と共にストーリーボードに追加されます。ストーリーボードに遷移を追加することで、ストーリーボードは遷移オブジェクトへの参照を保持することになります。したがって、コードで遷移の初期化が終わると、その参照が解放されます。この時点で遷移の寿命がストーリーボード オブジェクトによって制御されるようになり、ストーリーボード オブジェクトの寿命はアニメーション マネージャーによって制御されます。

リスト 3 ストーリーボードによるアニメーション マネージャーの初期化

IUIAnimationStoryboard* storyboard;
IUIAnimationTransition* transition;

HRESULT hr = g_animationManager->CreateStoryboard(&storyboard);
        
if (SUCCEEDED(hr))
{
hr = g_transitionLibrary->CreateAccelerateDecelerateTransition(
duration,
endValue,
accRatio,
decRatio,
&transition);

if (SUCCEEDED(hr))
    {
hr = storyboard->AddTransition(m_variable, transition);
    }

// ストーリーボードのスケジュール (リスト 5 を参照)
}

transition->Release();
storyboard->Release();

IUIAnimationTransitionLibrary::CreateAccelerateDecelerateTransition メソッドは、固定された期間 (duration パラメーター) だけ実行される遷移を作成します。遷移ライブラリでは、実行時に決定される値によって期間が異なる遷移を作成することもできます。たとえば、リスト 4 では IUIAnimationTransitionLibrary::CreateLinearTransitionFromSpeed メソッドが使われています。このメソッドの初期化は、変数の初期的な変化率 (speed) と、要求される最終値だけを使って行われます。遷移の期間は、遷移が開始される直前の変数の初期値によって異なります。

リスト 4 期間が確定されない遷移の作成

hr = g_animationManager->CreateAnimationVariable(initialValue, &m_variable);

if (SUCCEEDED(hr))
{
hr = g_transitionLibrary->CreateLinearTransitionFromSpeed(
speed,
endValue,
&transition);

if (SUCCEEDED(hr))
    {
hr = storyboard->AddTransition(m_variable, transition);
    }
}

アプリケーションは IUIAnimationTransition インターフェイスを使用して変数の初期値と初期速度 (変化率) を設定できます。

遷移をつなぐ

1 つのストーリーボードに複数の変数が含まれる場合があります。また変数は、さまざまな遷移に関連付けられたストーリーボードに追加することができます。ストーリーボードに追加され、同じ変数に関連付けられた一連の遷移は、直列に実行されます。つまり、ストーリーボードに最初に追加された遷移がその期間にわたって実行された後、2 番目の遷移が実行されます。これを図解したのが図 3 です。ここでは同じ変数 1 が 2 回、ストーリーボードに追加されています。つまり、ストーリーボードに追加された順序に従って 2 つの遷移が実行されます。

図 3 同じ変数を使って 2 つの遷移をつなぐ

Ff934859.819f7f52-3ca2-481d-9953-3edbf171e824(ja-jp,MSDN.10).png

それぞれ異なる変数に関連付けられた遷移でストーリーボードを初期化すると、アニメーションが開始される時点で、2 つの変数が並列に更新されます。これを図解したのが図 4 です。ここでは、3 つの遷移をストーリーボードに追加しています。これらの遷移は、2 つ の X と 1 つの Y に関連付けられています。つまり、アニメーションが開始される時点で X に関連付けられた最初の遷移と、Y に関連付けられた最初の遷移が同時に始まり、変数 XY がパラレルに変化します。

IUIAnimationStoryboard::AddTransition メソッドを使用して別の変数による遷移を追加すると、2 つの遷移は同時に開始するようにスケジュールされます。キー フレームを作成することにより、遷移が後で開始されるように指定することができます。基本的にキー フレームとは、ストーリーボードが開始された後の特定の時点です。キー フレームを作成するには、IUIAnimationStoryboard::AddKeyframeAtOffset メソッドを呼び出します。キー フレームを作成した後、この時点に遷移を追加するには、IUIAnimationStoryboard::AddTransitionAtKeyFrame メソッドを呼び出します。

図 4 ストーリーボードに 2 つの変数を追加

Ff934859.69e1052b-16c2-4aa5-817c-0ef3480eb5d0(ja-jp,MSDN.10).png

ストーリーボードのスケジュール

リスト 3 のコードは、ストーリーボードを作成して初期化します。アニメーションを開始するには、リスト 5 に示すように、ストーリーボードをスケジュールする必要があります。

リスト 5 ストーリーボードのスケジュール

if (SUCCEEDED(hr))
{
UI_ANIMATION_SECONDS secondsNow = static_cast<UI_ANIMATION_SECONDS>(0);
if (SUCCEEDED(hr))
    {
hr = g_animationTimer->GetTime(&secondsNow);
    }

if (SUCCEEDED(hr))
    {
hr = storyboard->Schedule(secondsNow);
    }
}

IUIAnimationStoryBoard::Schedule メソッドは、ストーリーボードの実行をスケジュールします。同じ変数を使用する他のストーリーボードの実行がスケジュールされていない場合、このストーリーボードが直ちに開始されます。同じ変数のコレクションを使用するストーリーボードは、一度に 1 つしか実行できませんが、新しいストーリーボードを初期化して、競合するストーリーボードをキャンセル、切り取り、完結、または圧縮することができます。

変数の値の取得

この時点でストーリーボードが初期化され開始されました。ストーリーボードはすべての遷移の期間にわたって実行され、変数オブジェクトを更新していきます。アプリケーションは変数の値を取得し、その値を使ってウィンドウの描画方法を決定する必要があります。ストーリーボードは開始時点を指定してスケジュールされるので、アニメーション マネージャーは、それぞれの遷移が現在どの時点に達しているか (つまり、変数の値) を認識しています。したがって、アプリケーションが変数の値を取得するには、事前にアニメーション マネージャーを現在の時刻で更新する必要があります。リスト 6 は、この動作を実行するコードです。

リスト 6 アニメーション マネージャーを更新するコード

UI_ANIMATION_SECONDS secondsNow = 0;
hr = g_animationTimer->GetTime(&secondsNow);

if (SUCCEEDED(hr))
{
hr = g_animationManager->Update(secondsNow);
}

アプリケーションが IUIAnimationManager::Update メソッドを呼び出すと、アニメーション マネージャーはすべての変数を更新します。次に IUIAnimationVariable::GetValue メソッドを呼び出すことにより、変数の値を取得できます (リスト 7)。

リスト 7 変数の値の取得

double value;
m_variable->GetValue(&value);

ウィンドウ レンダリング コードはこの変数の値を使用して、アニメーションの新しいフレームを描画します。レンダリング コードは、アニメーションでまだ描画すべきフレームが残っているかどうかを知る必要があり、アニメーション マネージャーは現在の時刻からこれを計算することができます。さらにフレームを描画する時間が残っている場合、再びレンダリングを呼び出す必要があります。リスト 8 は、アニメーション マネージャーのステータスを取得することにより、描画するフレームが残っているかどうかを判別する方法を示しています。

リスト 8 アニメーションが終わったかどうかの判別

UI_ANIMATION_MANAGER_STATUS status;
hr = g_animationManager->GetStatus(&status);
if (SUCCEEDED(hr))
{
if (status == UI_ANIMATION_MANAGER_BUSY)
    {
InvalidateRect(hWnd, NULL, FALSE);
    }
}

ステータス コードには 2 種類あります。UI_ANIMATION_MANAGER_IDLE は、すべてのストーリーボードが完了し、レンダリング コードを再び呼び出す必要がないことを表します。UI_ANIMATION_MANAGER_BUSY は、実行中または実行予定のストーリーボードか 1 つ以上あるため、リスト 8 のコードはウィンドウを無効と判断します。その結果、レンダリング コードがさらに呼び出され、アニメーションの次のフレームが表示されます。

Hilo での Windows アニメーション マネージャーの使用

ここまで、Windows アニメーション マネージャーによるアニメーションの基礎を説明しました。Hilo Browser プロジェクトには、Browser アプリケーションで使用されるさまざまなアニメーションを提供するクラスがあります。各クラスは 1 つ以上のアニメーション変数をカプセル化し、各種メソッドを提供して、これらの変数の初期値を与え、ストーリーボードを作成してスケジュールします。Browser プロジェクトの主なアニメーション クラスを以下の表に示します。アニメーションで変化する値についてもこの表で説明します。

クラス

説明

CarouselAnimation

内側の軌道上でフォルダー サムネイルを回転させます。ユーザーがカルーセルを回転するときに呼び出されます。このアニメーションでは、軌道上のフォルダー サムネイルのサイズ、不透明度、角度位置が変化します。

CarouselThumbnailAnimation

フォルダー サムネイルを履歴スタックに移動します。ユーザーがフォルダーを選択したときに呼び出されます。このアニメーションでは、フォルダー サムネイルの x および y の位置と不透明度が変化します。

FlyerAnimation

メディア ペインに画像を書き込みます。フォルダーが初めて開かれ、古い画像が消え去って新しい画像が表示されるときに呼び出されます。このアニメーションでは、あらかじめ定義されたパス沿いに画像が移動します。

MoverAnimation

Browser ウィンドウのサイズが変更されるときに、メディア ペイン内の画像を移動します。ウィンドウの広がりに合わせて、空いたスペースに画像を移動して書き込みます。このアニメーションでは、x および y 位置が変化します。

OrbitAnimation

内側の軌道のサイズと位置をアニメーションします。ユーザーがフォルダーを選択して新しい内側の軌道が表示されるとき、および履歴スタックが展開されるときに呼び出されます。このアニメーションでは、楕円の位置、サイズ、不透明度が変化します。

SlideAnimation

メディア ペインに画像を書き込みます。ユーザーが別のページに移動し、新しい画像スライドが表示されるときに呼び出されます。このアニメーションでは、画像の x 位置が変化します。

各クラスの形式は似通っており、Initialize メソッドによってアニメーション変数を作成した後、クラス インスタンスの寿命全体を通じてその変数が存在するという、2 段階の構築パターンになっています。これらのクラスには、ストーリーボードの作成とスケジュールを行う (通常、Setup という名前の) メソッドがあります。プロセス中、このメソッドは遷移を作成し、それらの遷移をアニメーション変数に関連付けます。そしてどのクラスにも、アニメーション中にアニメーション変数の値を取得する 1 つ以上のメソッドがあります。

これらのクラスのインスタンスが、別のクラス AnimationFactoryImpl によって作成されます。このクラスに含まれるメソッドが、SharedObject<>::Create の呼び出しをラップし、該当するクラスのインスタンスを C++ ヒープに作成します。

Hilo Browser では、カルーセル (フォルダー サムネイルと内側の軌道)、履歴スタックのフォルダー サムネイル、およびメディア ペインの写真サムネイルの 3 種類のオブジェクトがアニメーションされます。

Windows アニメーション マネージャー オブジェクトの初期化

Hilo Common Library で提供されるクラス AnimationUtility には、主要なアニメーション オブジェクトへのアクセスを提供し、アニメーションに使用する標準的なコードを提供する静的メソッドがあります。AnimationUtility クラスには、アニメーション マネージャー、アニメーション タイマー、遷移ライブラリの静的なメンバーがあります。これらのオブジェクトがまだ作成されていない場合、プライベート メソッド Initialize によって 3 つのオブジェクトがすべて作成されます。アクセサー メソッドによってこれらのオブジェクトのインターフェイス ポインターが返されるので、1 つのアクセサー メソッドが初めて呼び出された時点で 3 つのオブジェクトがすべて作成されることになります。AnimationUtility クラスには、アニメーション マネージャーの状態をチェックするメソッドと、ストーリーボードをスケジュールするメソッドもあります。

すべての Hilo アニメーション クラスに、アニメーション マネージャーにアクセスし、アニメーションで使用するアニメーション変数を作成する Initialize メソッドがあります。アニメーション変数の寿命は、アニメーション オブジェクトの寿命と同じです。Browser カルーセル ペインおよびメディア ペインのハンドラー オブジェクトは、アニメーション ファクトリ オブジェクトを使用してこれらのアニメーション オブジェクトを作成します。アニメーション ファクトリ オブジェクトに含まれるメソッドは、適切なアニメーション クラスに対して SharedObject<>::Create メソッドを呼び出すだけです。アニメーション オブジェクトの寿命は、実行するアニメーションの種類によって異なります。

CarouselPaneMessageHandler クラスは、カルーセルの内側の軌道と履歴スタックのアニメーションを提供します。履歴スタックは 0 個以上の履歴アイテムで構成され、各アイテムのアニメーションが可能です。コードでは、それぞれの履歴アイテムは CarouselHistoryItem オブジェクトであり (リスト 9 を参照)、そのアイテムのアニメーションに使用するアニメーション オブジェクト (CarouselThumbnailAnimation オブジェクトおよび OrbitalAnimation オブジェクト) へのポインターを含んでいます。ユーザーが特定のフォルダーを選択すると、そのフォルダーが履歴スタックに追加され、そのフォルダーの CarouselHistoryItem オブジェクトが作成され、m_carouselHistoryItems というベクターに追加されます。ユーザーが前に戻ると、このベクターから最後の CarouselHistoryItem が削除され、このフォルダーのアニメーション オブジェクトが削除されます。

リスト 9 履歴アイテム

struct CarouselHistoryItem
{
ComPtr<IThumbnail> Thumbnail;
ComPtr<ICarouselThumbnailAnimation> ThumbnailAnimation;
ComPtr<IOrbitAnimation> OrbitAnimation;
};

内側の軌道では 2 つのアニメーションが使われます。まず、カルーセルの回転です。これは軌道を中心とするフォルダー サムネイルの回転であり、CarouselAnimation によって提供されます。もう 1 つは、ユーザーがフォルダーを選択したときのアニメーションであり、内側の軌道が広がってフォルダーにズームインするような印象を与えます。このアニメーションは、OrbitalAnimation オブジェクトによって提供されます。これら 2 つのオブジェクトは、CarouselPaneMessageHandler オブジェクトか初めて作成される時点で作成され、その寿命はハンドラー オブジェクトと同じです。

MediaPaneMessageHandler クラスは、メディア ペインのアニメーション コードを提供します。メディア ペインで実行されるアニメーションは 3 種類あります。ペインに初めて写真が書き込まれるときの Flyer、矢印ボタンを使ってペイン内で別のページの写真を表示するときの Slide、そしてウィンドウ サイズの変更によってペインに表示される写真が多く (少なく) なるときの Mover です。これらのアニメーションのいずれか 1 つしか、一度に実行することはできません。そのため MediaPaneMessageHandler オブジェクトで保持されるのは、現在のアニメーション用のオブジェクトの参照だけです。つまり、(FlyerAnimationSlideAnimation、または MoverAnimation クラスによって提供される) アニメーション オブジェクトは必要なときにだけ作成され、その寿命は次のアニメーションまでとなります。

カルーセルのアニメーション

CarouselPaneMessageHandler クラスは、カルーセル ウィンドウのメッセージ ハンドラー コードを提供します。このクラスには、内側の軌道と履歴スタックのアニメーション用のオブジェクトがあります。CarouselAnimation クラスのインスタンスである m_carouselAnimation オブジェクトは、内側の軌道のサムネイル用に 2 種類のアニメーションを提供します。OrbitalAnimation クラスのインスタンスである m_innerOrbitalAnimation オブジェクトは、内側の軌道として表示される楕円のアニメーションを提供します。履歴スタックのフォルダーごとに CarouselThumbnailAnimation および OrbitalAnimation クラスのインスタンスが作成され、スタックの展開と縮小のアニメーションを提供します。CarouselThumbnailAnimation クラスはサムネイルのアニメーションを提供し、OrbitalAnimation クラスは軌道のアニメーションを提供します。図 5 に、CarouselPaneMessageHandler クラスのメンバーを示します。

図 5 CarouselPaneMessageHandler クラスのアニメーション メンバー

Ff934859.09abfa14-eca1-4d2a-8997-78af75592c5e-thumb(ja-jp,MSDN.10).png

CarouselAnimation クラスによって提供される最初のアニメーションの種類は、内側の軌道上のアイテムが回転するアニメーションです。内側の軌道上のいずれかのフォルダーをドラッグすると、アイテムをドラッグした方向にカルーセルが回転しますが、指を離した後も回転が続きます。回転を続けながら速度を落としていき、最終的にカルーセルの回転が止まります。このときに使われる遷移は、加速/減速のある遷移 (全体的には減速) です。CarouselAnimation::SetupRotation の関連するコード部分をリスト 10 に示します。3 番目のパラメーターが 0、4 番目のパラメーターが 1 となっているため、加速がまったくない点にご注意ください。つまりこれは、現在値から rotation パラメーターで表される値までの減速を意味します。減速する期間は、duration パラメーターで与えられます。rotation 値はカルーセルを回転する量を表します。この値は角度なので、回転が時計回りであれば transition オブジェクトに添付されるアニメーション変数は小さくなり、反時計回りであれば大きくなります。減速とは、この最終値に達するまでの変化の速度を指します。カルーセルを描画するときに、CarouselPaneMessageHandler::DrawOrbitalItems メソッドが呼び出され、それによって m_carouselAnimation オブジェクトから rotation 値が取得され、内側の軌道上で指定の位置に、現在選択されているフォルダーが描画されます。その後 DrawOrbitalItems メソッドにより、軌道上に等間隔で他のアイテムが描画されます。

リスト 10 カルーセルの回転の初期化

hr = m_transitionLibrary->CreateAccelerateDecelerateTransition(
duration,
rotation,
    0.0,
    1.0,
&transition);

CarouselAnimation クラスが提供するもう 1 つのアニメーションは、ユーザーが履歴スタックを展開したときに実行されます。この状況では、内側の軌道が縮小して右側へ移動し、フェードします。このアニメーション中、軌道上のフォルダー サムネイルは半分のサイズに縮小し、不透明度が 60% まで減少します。展開された履歴リストが再スタックされるときは、内側の軌道は元のフル サイズに拡大され、ペインの中央に移動し、不透明度が 100% に戻ります。またこのアニメーション中、サムネイルは元のフル サイズに拡大され、不透明度が 100% に戻ります。CarouselAnimation クラスは、サムネイルのサイズと不透明度に対応する 2 つの遷移を提供します。どちらの遷移も直線的に行われます。つまり、サイズまたは不透明度の変数が一定の速度で変化します。リスト 11 は、CarouselAnimation::SetupScale メソッドでこの処理を実行するコードを示しています。CarouselAnimation::SetupOpacity メソッドでも、同じ遷移が作成されています。SetupScale および SetupOpacity メソッドはどちらもストーリーボードを作成し、そのストーリーボードに遷移を追加し、ストーリーボードをスケジュールします。ただし、CarouselPaneMessageHandler のコードはこれらのメソッドを常にペアで呼び出しています。つまり、2 つのアニメーションは常に同時に実行されます。内側の軌道が縮小すると同時に透明になっていくか、または軌道が拡大すると同時に不透明になっていくかのどちらかです。

リスト 11 カルーセルの拡大縮小の初期化

hr = m_transitionLibrary->CreateLinearTransition(
duration,
thumbnailScale,
&transition);

カルーセル ペイン オブジェクトには、OrbitalAnimation クラスのインスタンスである m_innerOrbitalAnimation というメンバーがあります。名前が示すように、このオブジェクトは内側の軌道のサイズと位置を提供します。ユーザーがカルーセル上でフォルダーを選択すると、そのフォルダーは履歴スタックに追加されます。フォルダーにアイテムがある場合、内側の軌道が中央から拡大され、そのフォルダーにズーム インするような印象を与えます。このアニメーションは、軌道のサイズと不透明度をアニメーションする m_innerOrbitalAnimation オブジェクトによって提供されます。この変化は線形的に行われます。したがって OrbitalAnimation クラスは直線的な遷移オブジェクトを作成します。

履歴スタックのアニメーション

カルーセル上のフォルダーをクリックすると、そのフォルダーは m_carouselHistoryItems というベクターで表される履歴リストに追加されます (図 5)。このベクターには CarouselHistoryItem アイテムが含まれます (リスト 9)。CarouselHistoryItem 構造での IThumbnail の参照は、サムネイル画像のビットマップ画像および現在位置へのアクセスを提供するオブジェクトを表します。残り 2 つのメンバーは、履歴リストのメンバーをアニメーションする目的で使用され、CarouselThumbnailAnimation および OrbitalAnimation クラスのインスタンスを参照します。

カルーセル上のフォルダーをクリックすると、そのフォルダーが開かれ、内側の軌道にサブフォルダーが表示されます。この時点で、履歴リストに関連する 2 つのアニメーションが実行されます。履歴リストに追加されたフォルダーのある軌道を表す楕円が拡大され、フォルダーの内容にズーム インするような印象がユーザーに与えられます。このアニメーションは、前述の OrbitalAnimation クラスによって実行されます。2 番目のアニメーションでは、内側の軌道から履歴スタックにフォルダー アイコンが移動します。このアニメーションは CarouselThumbnailAnimation クラスによって実行されます。

履歴スタックを拡大するとき、すべての軌道が偏心的に拡大、つまり、軌道が拡大すると同時に楕円の中心が移動します。これは 5 つの変数を持つ OrbitalAnimation クラスによって実行されます。このうち 2 つの変数は楕円の寸法を表し、別の 2 つの変数は楕円の中心を表します。5 つ目の変数は、楕円の不透明度を表します。スタックが拡大または縮小されるとき、5 つの変数すべてが線形的な遷移によって変化します。

CarouselThumbnailAnimation クラスは、スタック上のサムネイルのアニメーションを提供します。このクラスは 3 つの変数 (サムネイルの x と y 位置、および不透明度) をカプセル化しています。このクラスが提供する 2 種類のアニメーションのうち、一方では 3 つの変数すべてが直線的に遷移しますが、もう一方では 3 つの変数すべてが加速/減速により遷移します。後者のアニメーションでは、加速の後に減速が行われます。これはキー フレームを使ってスケジュールされたいくつかの遷移によって実行されます。

メディア ペインのアニメーション

メディア ペインは 3 つの状況でアニメーションを使用し、FlyerAnimationSlideAnimation、および MoveAnimation の各クラスがそれぞれのアニメーションを提供します。図 6 はこれらのクラスの関係を示しています。どの時点でも表示できるアニメーションはこのうち 1 つだけなので、メディア ペインのメッセージ ハンドラー クラスは、これらのクラスのいずれか 1 つのインスタンス m_animationController の参照だけを保持し、アニメーションの種類は m_currentAnimation というフィールドに保存されます。

図 6 MediaPaneMessageHandler クラスのアニメーション メンバー

Ff934859.265d3056-8f83-486d-a92f-156b587006df-thumb(ja-jp,MSDN.10).png

MediaPaneAnimation 基本クラスには CreateAnimatedThumbnailCells および BuildStoryboard という 2 つの抽象メソッドがあります。これらの抽象メソッドは Setup メソッドで呼び出され、派生したクラス アニメーション固有のストーリーボードを作成します。図 6 に示されているもう 1 つの抽象クラス LineAnimation は、アニメーションされる写真サムネイルの位置を取得するための基本実装を提供します。

BuildStoryboard メソッドの派生クラスの実装によって、ストーリーボード、遷移、アニメーション変数オブジェクトが作成されます。各クラスのインスタンスによって、メディア ペインに表示される全アイテムの位置がアニメーションされます。これらの各インスタンスには、その写真のアニメーション変数など、各写真に関する情報を含むコレクションがあります。

3 つのアニメーションのうち最も単純なのは、SlideAnimation です。このアニメーションは、ユーザーが別のページの写真を表示するときに使用され、左または右から新しいページの写真がスライドしてきます。アニメーションされる写真ごとに、x および y 座標を表す 2 つのアニメーション変数があります。スライドは水平方向なので、y 座標は変化しません。そのため IUIAnimationTransitionLibrary::CreateConstantTransition メソッドを呼び出して一定速度の遷移を作成しています。x 座標を表すアニメーション変数は、加速/減速のある遷移 (半分が加速、残り半分が減速) に関連付けられます。

MoverAnimation クラスは、ウィンドウのサイズが変更された場合にメディア ペインの写真を並べ替えるアニメーションを提供します。空いたスペース一杯に写真が表示されるようサムネイルが並べ替えられ、そのために行数が増える場合もあります。MoverAnimation クラスは計算によって写真の移動方法を決定し、各写真の x および y 座標を表す変数を作成します。AddTransition プライベート メソッドは、1 つの座標では放物線状の遷移を作成して (IUIAnimationTransitionLibrary::CreateParabolicTransitionFromAcceleration メソッドの呼び出しによって) 値をゼロまで減速し、もう 1 つの座標では加速/減速のある遷移 (最初の 30% は加速、最後の 30% は減速) を作成します。

最後のアニメーションは FlyerAnimation クラスによって提供されます。このアニメーションはフォルダーを開いたとき呼び出され、それによって新しい写真が左から飛び込み、メディア ペインの写真が右へ飛び出します。各サムネイルには Direct2D グラフィックス パスが定義されています。このパス上での写真の現在位置が、アニメーション変数によって決定されます。このグラフィックス パスは、ID2D1Geometry インターフェイス付きのオブジェクトであり、円弧です。アニメーション変数はこの円弧上での位置を決定し、放物線状の遷移に従って変化します。この位置は、ID2D1Geometry::ComputePointAtLength メソッドの呼び出しによって実際の x-y 座標に変換されます。

まとめ

この記事では、時間の経過に伴って変数をどのように変化させるかを定義する遷移オブジェクトの作成方法と、ストーリーボード オブジェクトを使って遷移どうしの関係を指定する方法を説明しました。さらに、アニメーションを描画するために、アニメーション マネージャー オブジェクトを使用してアニメーション オブジェクトの値を取得する方法についても説明しました。アニメーションでは非常に速く画面を再描画しなければならない場合が多いので、ちらつきを防止するには、Direct2D のように、モニターの更新と同期して描画を実行するグラフィックス API を使用する必要があります。Windows 7 でアニメーションを実現するには、Windows アニメーション マネージャーと Direct2D API が理想的な組み合わせです。次の章では、Hilo でコンピューター上の写真ファイルへのアクセスを得るために、Windows ライブラリ API がどのように使われているかを検証します。

前へ | 次へ | ホーム