【解決方法】停止タイマーを使用して色の遷移アニメーションを作成する最良の方法は何ですか?


この問題を非常に簡単に説明します。カラー アニメーションを取得するには、色をスムーズに変更するために lerp アルゴリズムを使用する必要があります。これには、アニメーションの進行状況 (std::min)((deltaTime /duration), 1.0f が必要です。 ) しかし、このアニメーション手法を使用すると、アニメーション期間中にアニメーションが実行されますが、進行状況が 1 に達しないという問題があり、追加の CPU 消費を避けるためにタイマーを終了できます。

ウィンドウ クラスの処理には Direct2D API と Win32 API を使用していることに注意してください。

コードを紹介する前に、次のコードにあるウィンドウを更新する方法を説明します。

void Window::CreateTimer(bool update, std::uint16_t frameRate, std::uint64_t timerId) 
{ 
    SetTimer(m_Hwnd, timerId, 1000 / frameRate, nullptr); 
    if (update) 
    { 
        HandleTimerEvent([&](const Event& event)
            {
                InvalidateRect(m_hWnd, nullptr, false); 
            });
    }
}

更新フラグが true の場合、WM_TIMER を処理して、フレーム レート (この場合は 60 Hz) に基づいてウィンドウを無効にします。

ColorTransition の実装は次のとおりです。

C++
class Transition
{
protected:
    Transition(Window* window, float duration)
        : m_Window(window), m_Duration(duration)
    {
        srand(static_cast<std::uint32_t>(time(nullptr)));
        QueryPerformanceFrequency(&m_Frequency);
        QueryPerformanceCounter(&m_LastFrameTime);
        SetTimerId();
        SetFrameRate();
        m_Window->CreateTimer(true, m_FrameRate, m_TimerId);
    }

public:
    virtual void Update() = 0;

public:
    void SetLastFrameTime()
    {
        m_LastFrameTime = m_CurrentTime;
    }

    void SetTimerId()
    {
        m_TimerId = static_cast<std::uint32_t>(rand());
    }

    void SetFrameRate(std::uint32_t frameRate = 60)
    {
        m_FrameRate = frameRate;
        KillTimer(m_Window->GetWindow(), m_TimerId);
        m_Window->CreateTimer(true, m_FrameRate, m_TimerId);
    }

protected:
    float m_Duration;
    Window* m_Window;
    LARGE_INTEGER m_Frequency, m_LastFrameTime, m_CurrentTime;
    std::uint32_t m_FrameRate, m_TimerId;
    bool m_IsDone;
};

class ColorTransition : public Transition
{
public:
    ColorTransition(Window* window, float duration, D2D1::ColorF start, D2D1::ColorF end)
        : Transition(window, duration), m_Start(start), m_End(end), m_Color(start)
    {
    }

    D2D1::ColorF GetColor() const
    {
        return D2D1::ColorF(
                        m_Color.r / 255.0f,
                        m_Color.g / 255.0f,
                        m_Color.b / 255.0f,
                        m_Color.a / 255.0f);
    }

    void Update() override
    {
        QueryPerformanceCounter(&m_CurrentTime);

        float deltaTime = static_cast<float>(m_CurrentTime.QuadPart - m_LastFrameTime.QuadPart) /
            static_cast<float>(m_Frequency.QuadPart);
        float progress = (std::min)((deltaTime / m_Duration), 1.0f);
        
        m_Color.r = Lerp(m_Color.r, m_End.r, progress);
        m_Color.g = Lerp(m_Color.g, m_End.g, progress);
        m_Color.b = Lerp(m_Color.b, m_End.b, progress);
        m_Color.a = Lerp(m_Color.a, m_End.a, progress);

        if (progress >= 1.0f)
        {
            KillTimer(m_Window->GetWindow(), m_TimerId);
            m_IsDone = true;
        }
    }

    float Lerp(float start, float end, float progress)
    {
        return start + (end - start) * progress;
    }

private:
    D2D1::ColorF m_Start, m_End, m_Color;
};

ColorTransition.h を含む main.cpp:

C++
ColorTransition transition(&window, 1.0f, D2D1::ColorF(0, 55, 255), D2D1::ColorF(255, 0, 0));

window.HandlePaintEvent[&](const Event& event)
    {
        ID2D1HwndRenderTarget* renderTarget = event.GetRenderTarget();
        ID2D1SolidColorBrush* brush = nullptr;
        renderTarget->CreateSolidColorBrush(
            transition.GetColor(),
            &brush
        );

        transition.Update();
        renderTarget->FillRectangle(
            D2D1::RectF(10, 10, 110, 110),
            brush
        );
        transition.SetLastFrameTime();
    });

コードは理解するのが非常に簡単だと思うので、コードの説明は省略します。そして、私の問題が実際に何であるかを理解するのに十分なコードを提供したと思います。

私が試したこと:

たとえば、m_ElapsedTime という変数を使用して、最後のフレームからの経過時間を保存し、それが動作するトランジションアニメーションと等しいかどうかを確認できますが、トランジションがほぼ完了する前に停止し、それに対する解決策が見つかりませんでした。 。 私の場合、解決策を見つけるためにしばらく調べていたため、この種のアニメーションについての詳細はあまりありませんでした。そのため、ここで質問する必要がありました。 より良いアルゴリズムやその解決策を知っている場合は、私を助けてください。そして、私のコードが私が言ったように機能しない理由を説明してください。

コメント

タイトルとURLをコピーしました