diff --git a/change/react-native-windows-c060b99f-d2ee-460d-9ba1-c681a5405039.json b/change/react-native-windows-c060b99f-d2ee-460d-9ba1-c681a5405039.json new file mode 100644 index 00000000000..0c14a77d847 --- /dev/null +++ b/change/react-native-windows-c060b99f-d2ee-460d-9ba1-c681a5405039.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Adds fabric switch thumb animation", + "packageName": "react-native-windows", + "email": "tatianakapos@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/vnext/Microsoft.ReactNative/CompositionSwitcher.idl b/vnext/Microsoft.ReactNative/CompositionSwitcher.idl index 589617a7db7..f54f79094be 100644 --- a/vnext/Microsoft.ReactNative/CompositionSwitcher.idl +++ b/vnext/Microsoft.ReactNative/CompositionSwitcher.idl @@ -115,8 +115,9 @@ namespace Microsoft.ReactNative.Composition interface ISwitchThumbVisual { IVisual InnerVisual { get; }; - void Size(Windows.Foundation.Numerics.Vector2 size); - void Position(Windows.Foundation.Numerics.Vector2 position); + Windows.Foundation.Numerics.Vector2 Size{ get; set; }; + Windows.Foundation.Numerics.Vector2 Position{ get; set; }; + void AnimatePosition(Windows.Foundation.Numerics.Vector2 position); Boolean IsVisible { get; set; }; void Brush(IBrush brush); } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp index 71efe139c0d..e959614d68b 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp @@ -1114,12 +1114,31 @@ struct CompSwitchThumbVisual : winrt::implements< } void Size(winrt::Windows::Foundation::Numerics::float2 size) noexcept { - m_geometry.Radius(size); - m_spiritShape.Offset(size); - m_compVisual.Size(size); + assert(m_size.x == m_size.y); + // if the size has changed, update the position to the new center + if (m_size.x != 0.0f && m_size.x != size.x) { + Position({m_pos.x + (m_size.x - size.x), m_pos.y + (m_size.x - size.x)}); + } + m_size = size; + m_geometry.Radius(m_size); + m_spiritShape.Offset(m_size); + m_compVisual.Size(m_size); + } + + winrt::Windows::Foundation::Numerics::float2 Size() noexcept { + return m_size; } void Position(winrt::Windows::Foundation::Numerics::float2 position) noexcept { + m_pos = position; + m_compVisual.Offset({position.x, position.y, 0.0f}); + } + + winrt::Windows::Foundation::Numerics::float2 Position() noexcept { + return m_pos; + } + + void AnimatePosition(winrt::Windows::Foundation::Numerics::float2 position) noexcept { if (!isDrawn) { // we don't want to animate if this is the first time the switch is drawn on screen isDrawn = true; @@ -1132,6 +1151,7 @@ struct CompSwitchThumbVisual : winrt::implements< m_compVisual.StartAnimation(L"Offset", animation); } + m_pos = position; } bool IsVisible() const noexcept { @@ -1145,6 +1165,8 @@ struct CompSwitchThumbVisual : winrt::implements< bool isDrawn{false}; typename TTypeRedirects::SpriteVisual m_compVisual; winrt::Microsoft::ReactNative::Composition::IVisual m_visual; + winrt::Windows::Foundation::Numerics::float2 m_size{0.0f, 0.0f}; + winrt::Windows::Foundation::Numerics::float2 m_pos; typename TTypeRedirects::Compositor m_compositor{nullptr}; typename TTypeRedirects::CompositionSpriteShape m_spiritShape{nullptr}; typename TTypeRedirects::CompositionEllipseGeometry m_geometry{nullptr}; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp index 678c5d56898..4f8ebfee34f 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp @@ -106,7 +106,7 @@ void SwitchComponentView::Draw() noexcept { float offsetX = static_cast(offset.x / m_layoutMetrics.pointScaleFactor); float offsetY = static_cast(offset.y / m_layoutMetrics.pointScaleFactor); - // https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/CommonStyles/ToggleSwitch_themeresources.xaml + // https://github.com/microsoft/microsoft-ui-xaml/blob/winui2/main/dev/CommonStyles/ToggleSwitch_themeresources.xaml constexpr float thumbMargin = 3.0f; constexpr float thumbRadius = 7.0f; constexpr float trackWidth = 40.0f; @@ -203,9 +203,19 @@ void SwitchComponentView::Draw() noexcept { thumbX = (trackMarginX + trackWidth - thumbRadius - thumbRadius - thumbMargin) * m_layoutMetrics.pointScaleFactor; } - m_thumbVisual.Size( - {thumbRadius * m_layoutMetrics.pointScaleFactor, thumbRadius * m_layoutMetrics.pointScaleFactor}); - m_thumbVisual.Position({thumbX, thumbY}); + // handles various mouse events + if (m_pressed && !switchProps->disabled) { + m_thumbVisual.AnimatePosition({thumbX - 0.8f, thumbY - 0.8f}); + } else if (m_hovered && !switchProps->disabled) { + m_thumbVisual.Size( + {thumbRadius * m_layoutMetrics.pointScaleFactor + 0.8f, + thumbRadius * m_layoutMetrics.pointScaleFactor + 0.8f}); + } else { + m_thumbVisual.Size( + {thumbRadius * m_layoutMetrics.pointScaleFactor, thumbRadius * m_layoutMetrics.pointScaleFactor}); + m_thumbVisual.AnimatePosition({thumbX, thumbY}); + } + m_thumbVisual.Brush(thumbFill); // Restore old dpi setting @@ -301,7 +311,6 @@ void SwitchComponentView::onPointerReleased( if (!args.GetCurrentPoint(-1).Properties().IsPrimary()) { return; } - m_pressed = false; }