void ParallaxView::UpdateExpressionAnimation()

in dev/ParallaxView/ParallaxView.cpp [562:765]


void ParallaxView::UpdateExpressionAnimation(winrt::Orientation orientation)
{
    if (SharedHelpers::IsTH2OrLower())
    {
        // The ParallaxView control is not supported on Windows versions prior to RS1.
        return;
    }

    if (m_scrollInputHelper && m_scrollInputHelper->TargetElement() && m_scrollInputHelper->SourcePropertySet())
    {
        winrt::Visual targetVisual = winrt::ElementCompositionPreview::GetElementVisual(m_scrollInputHelper->TargetElement());
        if (m_targetVisual != targetVisual)
        {
            m_targetVisual = targetVisual;
            if (IsVisualTranslationPropertyAvailable())
            {
                winrt::ElementCompositionPreview::SetIsTranslationEnabled(m_scrollInputHelper->TargetElement(), true);
            }
            EnsureAnimatedVariables();
        }
    }
    else if (m_targetVisual)
    {
        // Stop prior parallaxing animations.        
        m_targetVisual.StopAnimation(GetVisualTargetedPropertyName(winrt::Orientation::Horizontal));
        m_targetVisual.StopAnimation(GetVisualTargetedPropertyName(winrt::Orientation::Vertical));
        m_isHorizontalAnimationStarted = m_isVerticalAnimationStarted = false;

        if (IsVisualTranslationPropertyAvailable())
        {
            m_targetVisual.Properties().InsertVector3(s_translationPropertyName, { 0.0f, 0.0f, 0.0f });
        }
        else
        {
            auto m = m_targetVisual.TransformMatrix();
            m.m41 = m.m42 = 0.0f;
            m_targetVisual.TransformMatrix(m);
        }

        m_targetVisual = nullptr;
    }

    if (m_targetVisual)
    {
        if ((orientation == winrt::Orientation::Horizontal && HorizontalShift() == 0.0) ||
            (orientation == winrt::Orientation::Vertical && VerticalShift() == 0.0))
        {
            if (orientation == winrt::Orientation::Horizontal)
            {
                if (m_isHorizontalAnimationStarted)
                {
                    // Stop prior horizontal parallaxing animation.
                    m_targetVisual.StopAnimation(GetVisualTargetedPropertyName(winrt::Orientation::Horizontal));
                    m_isHorizontalAnimationStarted = false;

                    if (IsVisualTranslationPropertyAvailable())
                    {
                        m_targetVisual.Properties().InsertVector3(s_translationPropertyName, { 0.0f, 0.0f, 0.0f });
                    }
                    else
                    {
                        auto m = m_targetVisual.TransformMatrix();
                        m.m41 = 0.0f;
                        m_targetVisual.TransformMatrix(m);
                    }

                    if (m_isVerticalAnimationStarted)
                    {
                        m_targetVisual.StartAnimation(GetVisualTargetedPropertyName(winrt::Orientation::Vertical), m_verticalParallaxExpressionInternal);
                    }
                }
            }
            else if (m_isVerticalAnimationStarted)
            {
                // Stop prior vertical parallaxing animation.
                m_targetVisual.StopAnimation(GetVisualTargetedPropertyName(winrt::Orientation::Vertical));
                m_isVerticalAnimationStarted = false;

                if (IsVisualTranslationPropertyAvailable())
                {
                    m_targetVisual.Properties().InsertVector3(s_translationPropertyName, { 0.0f, 0.0f, 0.0f });
                }
                else
                {
                    auto m = m_targetVisual.TransformMatrix();
                    m.m42 = 0.0f;
                    m_targetVisual.TransformMatrix(m);
                }

                if (m_isHorizontalAnimationStarted)
                {
                    m_targetVisual.StartAnimation(GetVisualTargetedPropertyName(winrt::Orientation::Horizontal), m_horizontalParallaxExpressionInternal);
                }
            }
        }
        else
        {
            UpdateStartOffsetExpression(orientation);
            UpdateEndOffsetExpression(orientation);

            winrt::ExpressionAnimation parallaxExpressionInternal = (orientation == winrt::Orientation::Horizontal) ? m_horizontalParallaxExpressionInternal : m_verticalParallaxExpressionInternal;
            std::wstring source = L"source." + static_cast<std::wstring>(m_scrollInputHelper->GetSourceOffsetPropertyName(orientation));
            std::wstring startOffset = (orientation == winrt::Orientation::Horizontal) ? L"animatedVariables.HorizontalSourceStartOffset" : L"animatedVariables.VerticalSourceStartOffset";
            std::wstring endOffset = (orientation == winrt::Orientation::Horizontal) ? L"animatedVariables.HorizontalSourceEndOffset" : L"animatedVariables.VerticalSourceEndOffset";
            std::wstring parallaxExpression;
            const float shift = (float)(orientation == winrt::Orientation::Horizontal ? HorizontalShift() : VerticalShift());

            if ((orientation == winrt::Orientation::Horizontal && IsHorizontalShiftClamped()) ||
                (orientation == winrt::Orientation::Vertical && IsVerticalShiftClamped()))
            {
                // Clamped parallax offset case.

                if (shift > 0.0)
                {
                    // X <= startOffset --> P(X) = 0
                    parallaxExpression = L"(-" + static_cast<std::wstring>(source) + L" <= " + static_cast<std::wstring>(startOffset) + L") ? 0.0f : ";

                    // startOffset < X < endOffset --> P(X) = -Min(MaxRatio, shift / (endOffset - startOffset)) * (X - startOffset)
                    parallaxExpression += L"((-" + static_cast<std::wstring>(source) + L" < " + static_cast<std::wstring>(endOffset) + L") ? ";
                    parallaxExpression += L"(-Min(maxRatio, (shift / (" + static_cast<std::wstring>(endOffset) + L" - " + static_cast<std::wstring>(startOffset) + L"))) * (-" + static_cast<std::wstring>(source) + L" - " + static_cast<std::wstring>(startOffset) + L")) : ";

                    // X >= endOffset --> P(X) = -Min(MaxRatio * Max(0 , endOffset - startOffset), shift)
                    parallaxExpression += L"-Min(maxRatio * Max(0.0f, " + static_cast<std::wstring>(endOffset) + L" - " + static_cast<std::wstring>(startOffset) + L"), shift))";
                }
                else
                {
                    // shift < 0.0

                    // X <= startOffset --> P(X) = -Min(MaxRatio * Max(0 , endOffset - startOffset), -shift)
                    parallaxExpression = L"(-" + static_cast<std::wstring>(source) + L" <= " + static_cast<std::wstring>(startOffset) + L") ? -Min(maxRatio * Max(0.0f, " + static_cast<std::wstring>(endOffset) + L" - " + static_cast<std::wstring>(startOffset) + L"), -shift) : ";

                    // startOffset < X < endOffset --> P(X) = Min(MaxRatio, shift / (startOffset - endOffset)) * (X - endOffset)
                    parallaxExpression += L"((-" + static_cast<std::wstring>(source) + L" < " + static_cast<std::wstring>(endOffset) + L") ? ";
                    parallaxExpression += L"(Min(maxRatio, (shift / (" + static_cast<std::wstring>(startOffset) + L" - " + static_cast<std::wstring>(endOffset) + L"))) * (-" + static_cast<std::wstring>(source) + L" - " + static_cast<std::wstring>(endOffset) + L")) : ";

                    // X >= endOffset --> P(X) = 0
                    parallaxExpression += L"0.0f)";
                }
            }
            else
            {
                // Unclamped parallax offset case.

                if (shift > 0.0)
                {
                    // startOffset == endOffset --> P(X) = 0
                    parallaxExpression = L"(" + static_cast<std::wstring>(startOffset) + L" == " + static_cast<std::wstring>(endOffset) + L") ? 0.0f : ";

                    // startOffset != endOffset --> P(X) = -Min(MaxRatio, shift / (endOffset - startOffset)) * (X - startOffset)
                    parallaxExpression += L"-Min(maxRatio, shift / (" + static_cast<std::wstring>(endOffset) + L" - " + static_cast<std::wstring>(startOffset) + L")) * (-" + static_cast<std::wstring>(source) + L" - " + static_cast<std::wstring>(startOffset) + L")";
                }
                else
                {
                    // startOffset == endOffset --> P(X) = 0
                    parallaxExpression = L"(" + static_cast<std::wstring>(startOffset) + L" == " + static_cast<std::wstring>(endOffset) + L") ? 0.0f : ";

                    // startOffset != endOffset --> P(X) = Min(MaxRatio, shift / (startOffset - endOffset)) * (X - endOffset)
                    parallaxExpression += L"Min(maxRatio, shift / (" + static_cast<std::wstring>(startOffset) + L" - " + static_cast<std::wstring>(endOffset) + L")) * (-" + static_cast<std::wstring>(source) + L" - " + static_cast<std::wstring>(endOffset) + L")";
                }
            }

            if (!parallaxExpressionInternal)
            {
                parallaxExpressionInternal = m_targetVisual.Compositor().CreateExpressionAnimation(parallaxExpression);
                if (orientation == winrt::Orientation::Horizontal)
                {
                    m_horizontalParallaxExpressionInternal = parallaxExpressionInternal;
                }
                else
                {
                    m_verticalParallaxExpressionInternal = parallaxExpressionInternal;
                }
            }
            else if (parallaxExpressionInternal.Expression() != parallaxExpression)
            {
                parallaxExpressionInternal.Expression(parallaxExpression);
            }

            parallaxExpressionInternal.SetReferenceParameter(L"source", m_scrollInputHelper->SourcePropertySet());
            parallaxExpressionInternal.SetReferenceParameter(L"animatedVariables", m_animatedVariables);
            parallaxExpressionInternal.SetScalarParameter(L"maxRatio", static_cast<float>(max(0.0, (orientation == winrt::Orientation::Horizontal ? MaxHorizontalShiftRatio() : MaxVerticalShiftRatio()))));
            parallaxExpressionInternal.SetScalarParameter(L"shift", shift);

            if (orientation == winrt::Orientation::Horizontal)
            {
                if (m_isHorizontalAnimationStarted)
                {
                    m_targetVisual.StopAnimation(GetVisualTargetedPropertyName(winrt::Orientation::Horizontal));
                }
                m_targetVisual.StartAnimation(GetVisualTargetedPropertyName(winrt::Orientation::Horizontal), parallaxExpressionInternal);
                m_isHorizontalAnimationStarted = true;
            }
            else
            {
                if (m_isVerticalAnimationStarted)
                {
                    m_targetVisual.StopAnimation(GetVisualTargetedPropertyName(winrt::Orientation::Vertical));
                }
                m_targetVisual.StartAnimation(GetVisualTargetedPropertyName(winrt::Orientation::Vertical), parallaxExpressionInternal);
                m_isVerticalAnimationStarted = true;
            }
        }
    }
}