Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Neste artigo, vamos nos aprofundar em como usar um recurso SourceModifier do InteractionTracker e demonstrar seu uso criando um controle personalizado de pull-to-refresh.
Pré-requisitos
Aqui, presumimos que você esteja familiarizado com os conceitos discutidos nestes artigos:
- Animações controladas por entrada
- Experiências de manipulação personalizadas com o InteractionTracker
- Animações baseadas em relação
O que é um SourceModifier e por que eles são úteis?
Assim como InertiaModifiers, SourceModifiers oferece controle de grãos mais fino sobre o movimento de um InteractionTracker. Mas ao contrário de InertiaModifiers que definem o movimento depois que InteractionTracker entra em inércia, SourceModifiers definem o movimento enquanto InteractionTracker ainda está em seu estado de interação. Nesses casos, você deseja uma experiência diferente da tradicional "grude no dedo".
Um exemplo clássico disso é a experiência de pull-to-refresh - quando o usuário puxa a lista para atualizar o conteúdo e a lista se movimenta na mesma velocidade que o dedo e para após uma certa distância, o movimento parece abrupto e mecânico. Uma experiência mais natural seria introduzir uma sensação de resistência enquanto o usuário interage ativamente com a lista. Essa pequena nuance ajuda a tornar a experiência geral do usuário final de interagir com uma lista mais dinâmica e atraente. Nesta seção de exemplo, entramos em mais detalhes sobre como criar isso.
Há dois tipos de modificadores de origem:
- DeltaPosition – é o delta entre a posição do quadro atual e a posição do quadro anterior do dedo durante a interação com o painel de toque. Esse modificador de origem permite modificar a posição delta da interação antes de enviá-la para processamento adicional. Esse é um parâmetro de tipo Vector3 e o desenvolvedor pode optar por modificar qualquer um dos atributos X ou Y ou Z da posição antes de passá-lo para o InteractionTracker.
- DeltaScale - é o delta entre a escala do quadro atual e a escala do quadro anterior que foi aplicada durante a interação de zoom de toque. Esse modificador de origem permite modificar o nível de zoom da interação. Esse é um atributo de tipo float que o desenvolvedor pode modificar antes de passá-lo para o InteractionTracker.
Quando InteractionTracker está em seu estado de Interação, ele avalia cada um dos Modificadores de Origem atribuídos a ele e determina se algum deles se aplica. Isso significa que você pode criar e atribuir vários Modificadores de Origem a um InteractionTracker. Mas, ao definir cada um, você precisa fazer o seguinte:
- Definir a condição – uma expressão que define a instrução condicional quando esse modificador de origem específico deve ser aplicado.
- Definir a DeltaPosition/DeltaScale – a expressão modificadora de origem que altera DeltaPosition ou DeltaScale quando a condição definida acima é atendida.
Exemplo
Agora, vamos examinar como você pode usar Source Modifiers para criar uma experiência personalizada de "pull-to-refresh" com um controle WinUI XAML ListView existente. Usaremos um Canvas como o "Painel de Atualização" que será empilhado sobre um ListView XAML para criar essa experiência.
Para a experiência do usuário final, queremos criar o efeito de "resistência" enquanto o usuário está ativamente deslizando a lista (com toque) e parar o movimento quando a posição ultrapassa um determinado ponto.
O código funcional para essa experiência pode ser encontrado no repositório Window UI Dev Labs no GitHub. Aqui está o passo a passo para construir essa experiência. No código de marcação XAML, você tem o seguinte:
<StackPanel Height="500" MaxHeight="500" x:Name="ContentPanel" HorizontalAlignment="Left" VerticalAlignment="Top" >
<Canvas Width="400" Height="100" x:Name="RefreshPanel" >
<Image x:Name="FirstGear" Source="ms-appx:///Assets/Loading.png" Width="20" Height="20" Canvas.Left="200" Canvas.Top="70"/>
</Canvas>
<ListView x:Name="ThumbnailList"
MaxWidth="400"
Height="500"
ScrollViewer.VerticalScrollMode="Enabled" ScrollViewer.IsScrollInertiaEnabled="False" ScrollViewer.IsVerticalScrollChainingEnabled="True" >
<ListView.ItemTemplate>
……
</ListView.ItemTemplate>
</ListView>
</StackPanel>
Como o ListView (ThumbnailList) é um controle XAML que já rola, é necessário que a rolagem seja repassada ao controle pai (ContentPanel) quando atingir o item superior e não puder mais rolar. (ContentPanel é onde você aplicará os Modificadores de Origem.) Para isso acontecer, você precisa definir ScrollViewer.IsVerticalScrollChainingEnabled como true na marcação ListView. Você também precisará definir o modo de encadeamento no VisualInteractionSource como Always.
Você precisa definir o manipulador PointerPressedEvent com o parâmetro handledEventsToo como true. Sem essa opção, o PointerPressedEvent não será encadeado ao ContentPanel, pois o controle ListView marcará esses eventos como manipulados e eles não serão enviados para a cadeia visual.
//The PointerPressed handler needs to be added using AddHandler method with the //handledEventsToo boolean set to "true"
//instead of the XAML element's "PointerPressed=Window_PointerPressed",
//because the list view needs to chain PointerPressed handled events as well.
ContentPanel.AddHandler(PointerPressedEvent, new PointerEventHandler( Window_PointerPressed), true);
Agora, você está pronto para vincular isso ao InteractionTracker. Comece configurando o InteractionTracker, o VisualInteractionSource e a Expressão que aproveitarão a posição do InteractionTracker.
// InteractionTracker and VisualInteractionSource setup.
_root = ElementCompositionPreview.GetElementVisual(Root);
_compositor = _root.Compositor;
_tracker = InteractionTracker.Create(_compositor);
_interactionSource = VisualInteractionSource.Create(_root);
_interactionSource.PositionYSourceMode = InteractionSourceMode.EnabledWithInertia;
_interactionSource.PositionYChainingMode = InteractionChainingMode.Always;
_tracker.InteractionSources.Add(_interactionSource);
float refreshPanelHeight = (float)RefreshPanel.ActualHeight;
_tracker.MaxPosition = new Vector3((float)Root.ActualWidth, 0, 0);
_tracker.MinPosition = new Vector3(-(float)Root.ActualWidth, -refreshPanelHeight, 0);
// Use the Tacker's Position (negated) to apply to the Offset of the Image.
// The -{refreshPanelHeight} is to hide the refresh panel
m_positionExpression = _compositor.CreateExpressionAnimation($"-tracker.Position.Y - {refreshPanelHeight} ");
m_positionExpression.SetReferenceParameter("tracker", _tracker);
_contentPanelVisual.StartAnimation("Offset.Y", m_positionExpression);
Com essa configuração, o painel de atualização está fora do visor em sua posição inicial, e tudo o que o usuário vê é a ListView. Quando o movimento panorâmico atinge o ContentPanel, o evento PointerPressed será acionado, momento em que se solicita ao sistema para usar o InteractionTracker para conduzir a experiência de manipulação.
private void Window_PointerPressed(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Touch) {
// Tell the system to use the gestures from this pointer point (if it can).
_interactionSource.TryRedirectForManipulation(e.GetCurrentPoint(null));
}
}
Observação
Se não for necessário encadear eventos manipulados, a adição do manipulador PointerPressedEvent pode ser feita diretamente por meio da marcação XAML usando o atributo (PointerPressed="Window_PointerPressed").
A próxima etapa é configurar os modificadores de origem. Você usará dois modificadores de origem para obter esse comportamento; Resistência e Parada.
- Resistência – mova o DeltaPosition.Y a metade da velocidade até atingir a altura do RefreshPanel.
CompositionConditionalValue resistanceModifier = CompositionConditionalValue.Create (_compositor);
ExpressionAnimation resistanceCondition = _compositor.CreateExpressionAnimation(
$"-tracker.Position.Y < {pullToRefreshDistance}");
resistanceCondition.SetReferenceParameter("tracker", _tracker);
ExpressionAnimation resistanceAlternateValue = _compositor.CreateExpressionAnimation(
"source.DeltaPosition.Y / 3");
resistanceAlternateValue.SetReferenceParameter("source", _interactionSource);
resistanceModifier.Condition = resistanceCondition;
resistanceModifier.Value = resistanceAlternateValue;
- Parar – parar de se mover depois que o RefreshPanel inteiro estiver na tela.
CompositionConditionalValue stoppingModifier = CompositionConditionalValue.Create (_compositor);
ExpressionAnimation stoppingCondition = _compositor.CreateExpressionAnimation(
$"-tracker.Position.Y >= {pullToRefreshDistance}");
stoppingCondition.SetReferenceParameter("tracker", _tracker);
ExpressionAnimation stoppingAlternateValue = _compositor.CreateExpressionAnimation("0");
stoppingModifier.Condition = stoppingCondition;
stoppingModifier.Value = stoppingAlternateValue;
Now add the 2 source modifiers to the InteractionTracker.
List<CompositionConditionalValue> modifierList = new List<CompositionConditionalValue>()
{ resistanceModifier, stoppingModifier };
_interactionSource.ConfigureDeltaPositionYModifiers(modifierList);
Este diagrama fornece uma visualização da instalação do SourceModifiers.
Agora, com os SourceModifiers, você observará que ao mover o ListView para baixo e alcançar o item mais superior, o painel de atualização é puxado para baixo a metade da velocidade do movimento até atingir a altura do RefreshPanel e, em seguida, para de se mover.
No exemplo completo, uma animação de quadro-chave é usada para girar um ícone durante a interação na tela RefreshPanel. Qualquer conteúdo pode ser usado em seu lugar ou utilizar a posição do InteractionTracker para conduzir essa animação separadamente.
Windows developer