Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Neste artigo, aprofundamos como usar a funcionalidade SourceModifier do InteractionTracker e demonstramos a sua utilização, criando um controlo personalizado de pull-to-refresh.
Pré-requisitos
Aqui, assumimos que está familiarizado com os conceitos discutidos nestes artigos:
- Animações orientadas por entrada
- Experiências de manipulação personalizada com o InteractionTracker
- Animações baseadas em relações
O que é um SourceModifier e por que são úteis?
Tal como os Modificadores de Inércia, os Modificadores de Fonte oferecem-te um controlo mais preciso sobre o movimento de um InteractionTracker. Mas, ao contrário dos InertiaModifiers que definem o movimento após o InteractionTracker entrar em inércia, os SourceModifiers definem o movimento enquanto o InteractionTracker ainda está no seu estado de interação. Nestes casos, vocês querem uma experiência diferente da tradicional "agarra-se ao dedo".
Um exemplo clássico disto é a experiência de puxar para atualizar – quando o utilizador puxa a lista para atualizar o conteúdo e a lista se move à mesma velocidade do 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 utilizador interage ativamente com a lista. Esta pequena nuance ajuda a tornar a experiência global do utilizador final ao interagir com uma lista mais dinâmica e apelativa. Na secção de Exemplos, detalhamos como construir isto.
Existem 2 tipos de Modificadores de Fonte:
- DeltaPosição – é o delta entre a posição atual do quadro e a posição anterior do dedo durante a interação com a panagem tátil. Este modificador de fonte permite modificar a posição delta da interação antes de a enviar para processamento posterior. Este é um parâmetro do tipo Vector3 e o programador pode escolher modificar qualquer um dos atributos X, Y ou Z da posição antes de o passar para o InteractionTracker.
- DeltaScale - é a diferença entre a escala de frames atual e a escala de frames anterior que foi aplicada durante a interação de zoom por toque. Este modificador de fonte permite modificar o nível de zoom da interação. Este é um atributo do tipo float que o programador pode modificar antes de o passar para o InteractionTracker.
Quando o InteractionTracker está no seu estado de Interação, avalia cada um dos Modificadores de Fonte atribuídos a ele e determina se algum deles se aplica. Isto significa que pode criar e atribuir múltiplos Modificadores de Fonte a um InteractionTracker. Mas, ao definir cada uma, precisa de fazer o seguinte:
- Defina a Condição – uma Expressão que define a afirmação condicional quando este Modificador de Fonte específico deve ser aplicado.
- Defina a DeltaPosição/DeltaEscala – A expressão modificadora da fonte que altera a DeltaPosição ou DeltaEscala quando a condição acima definida é cumprida.
Exemplo
Agora vejamos como pode usar Modificadores de Fonte para criar uma experiência personalizada de puxar para atualizar com um controlo ListView XAML do WinUI existente. Vamos usar um Canvas como o "Painel de Atualização" que será empilhado sobre um ListView XAML para construir esta experiência.
Para a experiência do utilizador final, queremos criar o efeito de "resistência" enquanto o utilizador está ativamente a deslizar a lista (com toque) e parar depois da posição ultrapassar um certo ponto.
O código funcional desta 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 seu código de marcação XAML, 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 controlo XAML que já faz deslocamento, é necessário que o deslocamento esteja encadeado até ao seu elemento pai (ContentPanel) quando chegar ao elemento mais alto e já não conseguir deslocar-se mais. (O ContentPanel é onde irá aplicar os Modificadores de Fonte.) Para que isto aconteça, precisa de definir o ScrollViewer.IsVerticalScrollChainingEnabled para true na marcação ListView. Também terá de definir o modo de encadeamento no VisualInteractionSource para sempre.
Tens de definir o handler PointerPressedEvent com o parâmetro handledEventsToo como true. Sem esta opção, o PointerPressedEvent não estará encadeado ao ContentPanel, pois o controlo ListView marcará esses eventos como tratados e não serão enviados pela 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, está pronto para ligar isto ao InteractionTracker. Comece por configurar o InteractionTracker, o VisualInteractionSource e a Expressão, que vão aproveitar 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 esta configuração, na sua posição inicial, o painel de atualização sai da viewport e tudo o que o utilizador vê é a listView. Quando a deslocação atinge o Painel de Conteúdo, o evento PointerPressed é acionado, onde se pede ao Sistema para usar o InteractionTracker para gerar 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 Handled, adicionar o handler PointerPressedEvent pode ser feito diretamente através da marcação XAML usando o atributo (PointerPressed="Window_PointerPressed").
O passo seguinte é configurar os modificadores de origem. Vais usar 2 modificadores de fonte para obter este comportamento; Resistência e Stop.
- 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 movimentar depois de o RefreshPanel estar completamente visível no ecrã.
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 apresenta uma visualização da configuração dos SourceModifiers.
Agora, com os SourceModifiers, vai notar que, ao descer o ListView e alcançar o item mais alto, o painel de atualização é puxado para baixo à metade do ritmo do movimento de rolagem até atingir a altura do Painel de Atualização e depois para de se mover.
Na amostra completa, é usada uma animação por keyframe para rodar um ícone durante a interação na tela do RefreshPanel. Qualquer conteúdo pode ser usado no seu lugar ou utilizar a posição do InteractionTracker para conduzir essa animação separadamente.
Windows developer