Skip to content

Commit

Permalink
Refine ProgressBar Animation (#1564)
Browse files Browse the repository at this point in the history
* add updating state for transitions

* refactor isUpdating animation logic

* fix clipping bug and refactor visual state change logic

* add IndicatorLengthDelta in TemplateSettings properties

* add ContainerAnimationStartPosition in TemplateSettings properties

* Refine animation for Indeterminate

* remove m_isUpdating member variable

* changes from comments
  • Loading branch information
karkarl authored Nov 19, 2019
1 parent 6ae29b7 commit 9d0819d
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 13 deletions.
46 changes: 46 additions & 0 deletions dev/Generated/ProgressBarTemplateSettings.properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ CppWinRTActivatableClassWithDPFactory(ProgressBarTemplateSettings)

GlobalDependencyProperty ProgressBarTemplateSettingsProperties::s_ClipRectProperty{ nullptr };
GlobalDependencyProperty ProgressBarTemplateSettingsProperties::s_ContainerAnimationEndPositionProperty{ nullptr };
GlobalDependencyProperty ProgressBarTemplateSettingsProperties::s_ContainerAnimationStartPositionProperty{ nullptr };
GlobalDependencyProperty ProgressBarTemplateSettingsProperties::s_IndicatorLengthDeltaProperty{ nullptr };

ProgressBarTemplateSettingsProperties::ProgressBarTemplateSettingsProperties()
{
Expand Down Expand Up @@ -40,12 +42,36 @@ void ProgressBarTemplateSettingsProperties::EnsureProperties()
ValueHelper<double>::BoxedDefaultValue(),
nullptr);
}
if (!s_ContainerAnimationStartPositionProperty)
{
s_ContainerAnimationStartPositionProperty =
InitializeDependencyProperty(
L"ContainerAnimationStartPosition",
winrt::name_of<double>(),
winrt::name_of<winrt::ProgressBarTemplateSettings>(),
false /* isAttached */,
ValueHelper<double>::BoxedDefaultValue(),
nullptr);
}
if (!s_IndicatorLengthDeltaProperty)
{
s_IndicatorLengthDeltaProperty =
InitializeDependencyProperty(
L"IndicatorLengthDelta",
winrt::name_of<double>(),
winrt::name_of<winrt::ProgressBarTemplateSettings>(),
false /* isAttached */,
ValueHelper<double>::BoxedDefaultValue(),
nullptr);
}
}

void ProgressBarTemplateSettingsProperties::ClearProperties()
{
s_ClipRectProperty = nullptr;
s_ContainerAnimationEndPositionProperty = nullptr;
s_ContainerAnimationStartPositionProperty = nullptr;
s_IndicatorLengthDeltaProperty = nullptr;
}

void ProgressBarTemplateSettingsProperties::ClipRect(winrt::RectangleGeometry const& value)
Expand All @@ -67,3 +93,23 @@ double ProgressBarTemplateSettingsProperties::ContainerAnimationEndPosition()
{
return ValueHelper<double>::CastOrUnbox(static_cast<ProgressBarTemplateSettings*>(this)->GetValue(s_ContainerAnimationEndPositionProperty));
}

void ProgressBarTemplateSettingsProperties::ContainerAnimationStartPosition(double value)
{
static_cast<ProgressBarTemplateSettings*>(this)->SetValue(s_ContainerAnimationStartPositionProperty, ValueHelper<double>::BoxValueIfNecessary(value));
}

double ProgressBarTemplateSettingsProperties::ContainerAnimationStartPosition()
{
return ValueHelper<double>::CastOrUnbox(static_cast<ProgressBarTemplateSettings*>(this)->GetValue(s_ContainerAnimationStartPositionProperty));
}

void ProgressBarTemplateSettingsProperties::IndicatorLengthDelta(double value)
{
static_cast<ProgressBarTemplateSettings*>(this)->SetValue(s_IndicatorLengthDeltaProperty, ValueHelper<double>::BoxValueIfNecessary(value));
}

double ProgressBarTemplateSettingsProperties::IndicatorLengthDelta()
{
return ValueHelper<double>::CastOrUnbox(static_cast<ProgressBarTemplateSettings*>(this)->GetValue(s_IndicatorLengthDeltaProperty));
}
10 changes: 10 additions & 0 deletions dev/Generated/ProgressBarTemplateSettings.properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,21 @@ class ProgressBarTemplateSettingsProperties
void ContainerAnimationEndPosition(double value);
double ContainerAnimationEndPosition();

void ContainerAnimationStartPosition(double value);
double ContainerAnimationStartPosition();

void IndicatorLengthDelta(double value);
double IndicatorLengthDelta();

static winrt::DependencyProperty ClipRectProperty() { return s_ClipRectProperty; }
static winrt::DependencyProperty ContainerAnimationEndPositionProperty() { return s_ContainerAnimationEndPositionProperty; }
static winrt::DependencyProperty ContainerAnimationStartPositionProperty() { return s_ContainerAnimationStartPositionProperty; }
static winrt::DependencyProperty IndicatorLengthDeltaProperty() { return s_IndicatorLengthDeltaProperty; }

static GlobalDependencyProperty s_ClipRectProperty;
static GlobalDependencyProperty s_ContainerAnimationEndPositionProperty;
static GlobalDependencyProperty s_ContainerAnimationStartPositionProperty;
static GlobalDependencyProperty s_IndicatorLengthDeltaProperty;

static void EnsureProperties();
static void ClearProperties();
Expand Down
37 changes: 25 additions & 12 deletions dev/ProgressBar/ProgressBar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,20 @@ void ProgressBar::OnApplyTemplate()
void ProgressBar::OnSizeChanged(const winrt::IInspectable&, const winrt::IInspectable&)
{
SetProgressBarIndicatorWidth();
if (m_shouldUpdateWidthBasedTemplateSettings)
{
UpdateWidthBasedTemplateSettings();
}
UpdateWidthBasedTemplateSettings();
}

void ProgressBar::OnRangeBasePropertyChanged(const winrt::DependencyObject& sender, const winrt::DependencyProperty& args)
{
// NOTE: This hits when the Value property changes, because we called RegisterPropertyChangedCallback.

SetProgressBarIndicatorWidth();
}

void ProgressBar::OnIsIndeterminatePropertyChanged(const winrt::DependencyPropertyChangedEventArgs& args)
{
// NOTE: This hits when IsIndeterminate changes because we set MUX_PROPERTY_CHANGED_CALLBACK to true in the idl.

UpdateStates();
SetProgressBarIndicatorWidth();
UpdateStates();
}

void ProgressBar::OnShowPausedPropertyChanged(const winrt::DependencyPropertyChangedEventArgs& args)
Expand All @@ -73,6 +69,7 @@ void ProgressBar::OnShowErrorPropertyChanged(const winrt::DependencyPropertyChan
void ProgressBar::UpdateStates()
{
m_shouldUpdateWidthBasedTemplateSettings = false;

if (ShowError())
{
winrt::VisualStateManager::GoToState(*this, s_ErrorStateName, true);
Expand All @@ -93,32 +90,47 @@ void ProgressBar::UpdateStates()
}
else if (!IsIndeterminate())
{
SetProgressBarIndicatorWidth();
winrt::VisualStateManager::GoToState(*this, s_DeterminateStateName, true);
}
}

void ProgressBar::SetProgressBarIndicatorWidth()
{
const auto templateSettings = winrt::get_self<::ProgressBarTemplateSettings>(TemplateSettings());

if (auto&& progressBar = m_layoutRoot.get())
{
if (auto&& progressBarIndicator = m_progressBarIndicator.get())
{
const double progressBarWidth = progressBar.ActualWidth();
const double prevIndicatorWidth = progressBarIndicator.ActualWidth();
const double maximum = Maximum();
const double minimum = Minimum();
const auto padding = Padding();

if (std::abs(maximum - minimum) > DBL_EPSILON)
// Adds "Updating" state in between to trigger RepositionThemeAnimation Visual Transition
// in ProgressBar.xaml when reverting back to previous state
winrt::VisualStateManager::GoToState(*this, s_UpdatingStateName, true);

if (IsIndeterminate())
{
progressBarIndicator.Width(progressBarWidth * 0.4);
}
else if (std::abs(maximum - minimum) > DBL_EPSILON)
{
const double maxIndicatorWidth = progressBarWidth - (padding.Left + padding.Right);
const double increment = maxIndicatorWidth / (maximum - minimum);
progressBarIndicator.Width(increment * (Value() - minimum));
const double indicatorWidth = increment * (Value() - minimum);
const double widthDelta = indicatorWidth - prevIndicatorWidth;
templateSettings->IndicatorLengthDelta(-widthDelta);
progressBarIndicator.Width(indicatorWidth);
}
else
{
progressBarIndicator.Width(0); // Error
}

UpdateStates(); // Reverts back to previous state
}
}
}
Expand All @@ -129,7 +141,7 @@ void ProgressBar::UpdateWidthBasedTemplateSettings()

if (auto&& progressBarIndicator = m_progressBarIndicator.get())
{
auto const [width, height] = [progressBar = m_layoutRoot.get()]()
const auto [width, height] = [progressBar = m_layoutRoot.get()]()
{
if (progressBar)
{
Expand All @@ -140,8 +152,9 @@ void ProgressBar::UpdateWidthBasedTemplateSettings()
return std::make_tuple(0.0f, 0.0f);
}();

progressBarIndicator.Width(width / 3);
const double indicatorWidthMultiplier = -0.4;

templateSettings->ContainerAnimationStartPosition(width * indicatorWidthMultiplier);
templateSettings->ContainerAnimationEndPosition(width);

const auto rectangle = [width, height, padding = Padding()]()
Expand Down
1 change: 1 addition & 0 deletions dev/ProgressBar/ProgressBar.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,5 @@ class ProgressBar :
static constexpr wstring_view s_PausedStateName{ L"Paused" };
static constexpr wstring_view s_IndeterminateStateName{ L"Indeterminate" };
static constexpr wstring_view s_DeterminateStateName{ L"Determinate" };
static constexpr wstring_view s_UpdatingStateName{ L"Updating" };
};
6 changes: 6 additions & 0 deletions dev/ProgressBar/ProgressBar.idl
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ unsealed runtimeclass ProgressBarTemplateSettings : Windows.UI.Xaml.DependencyOb
{
ProgressBarTemplateSettings();

Double ContainerAnimationStartPosition;
static Windows.UI.Xaml.DependencyProperty ContainerAnimationStartPositionProperty{ get; };

Double ContainerAnimationEndPosition;
static Windows.UI.Xaml.DependencyProperty ContainerAnimationEndPositionProperty{ get; };

Double IndicatorLengthDelta;
static Windows.UI.Xaml.DependencyProperty IndicatorLengthDeltaProperty{ get; };

Windows.UI.Xaml.Media.RectangleGeometry ClipRect;
static Windows.UI.Xaml.DependencyProperty ClipRectProperty{ get; };
}
Expand Down
8 changes: 7 additions & 1 deletion dev/ProgressBar/ProgressBar.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
<VisualStateGroup x:Name="CommonStates">

<VisualStateGroup.Transitions>
<VisualTransition From="Updating" To="Determinate">
<Storyboard>
<RepositionThemeAnimation TargetName="ProgressBarIndicator" FromHorizontalOffset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.IndicatorLengthDelta}" />
</Storyboard>
</VisualTransition>
<VisualTransition From="Paused" To="Determinate">
<Storyboard>
<DoubleAnimation
Expand All @@ -39,13 +44,14 @@

<VisualState x:Name="Normal" />
<VisualState x:Name="Determinate" />
<VisualState x:Name="Updating" />
<VisualState x:Name="Indeterminate">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetName="ProgressBarIndicator"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)"
Duration="0:0:1">
<EasingDoubleKeyFrame KeyTime="0" Value="0">
<EasingDoubleKeyFrame KeyTime="0" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.ContainerAnimationStartPosition}">
<EasingDoubleKeyFrame.EasingFunction>
<CubicEase/>
</EasingDoubleKeyFrame.EasingFunction>
Expand Down

0 comments on commit 9d0819d

Please sign in to comment.