Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье мы более подробно рассмотрим, как использовать функцию SourceModifier в InteractionTracker и продемонстрируем ее применение, создав пользовательский элемент управления "потяни, чтобы обновить".
Необходимые условия
Здесь предполагается, что вы знакомы с понятиями, описанными в следующих статьях:
- Анимации на основе входных данных
- Пользовательские операции с помощью InteractionTracker
- Анимации на основе реляционных данных
Что такое SourceModifier и почему они полезны?
Как и инерционные модификаторы, SourceModifiers предоставляют более детальный контроль над движением InteractionTracker. Но в отличие от InertiaModifiers, которые определяют движение после того, как InteractionTracker входит в состояние инерции, SourceModifiers определяют движение, пока InteractionTracker находится в состоянии активного взаимодействия. В таких случаях вы хотите получить другое впечатление, нежели традиционное "прилипание к пальцам".
Классическим примером этого является опыт обновления списка движением - когда пользователь тянет список вниз, чтобы обновить содержимое, и список движется с той же скоростью, что и палец, а затем останавливается через определённое расстояние, движение кажется резким и механическим. Более естественным взаимодействием будет создать ощущение сопротивления, пока пользователь активно взаимодействует со списком. Этот маленький нюанс помогает сделать общий интерфейс пользователя взаимодействия со списком более динамичным и привлекательным. В разделе "Пример" мы подробно рассмотрим, как создать это.
Существует 2 типа модификаторов источника:
- DeltaPosition — это разность между текущей позицией пальца и предыдущей позицией пальца во время взаимодействия касания с панорамированием. Этот модификатор источника позволяет изменить разностную позицию взаимодействия перед отправкой его для дальнейшей обработки. Это параметр типа Vector3, и разработчик может изменить любой из атрибутов позиции X или Y или Z, прежде чем передать его в InteractionTracker.
- DeltaScale — это разностная разница между текущим масштабом кадра и предыдущим масштабом кадра, который был применен во время сенсорного масштабирования. Этот модификатор источника позволяет изменить уровень масштабирования взаимодействия. Это атрибут типа с плавающей запятой, который разработчик может изменить перед передачей в InteractionTracker.
Когда InteractionTracker находится в состоянии взаимодействия, он оценивает каждый из модификаторов источника, назначенных ему, и определяет, применяются ли какие-либо из них. Это означает, что вы можете создать и назначить несколько модификаторов источника элементу InteractionTracker. Но при определении каждого необходимо сделать следующее:
- Определите условие — выражение, определяющее условную инструкцию при применении этого конкретного модификатора источника.
- Определите deltaPosition/DeltaScale — исходное модификаторное выражение, которое изменяет DeltaPosition или DeltaScale при выполнении указанного выше условия.
Пример
Теперь давайте рассмотрим, как использовать модификаторы источника для создания пользовательского интерфейса по запросу к обновлению с помощью существующего элемента управления WinUI XAML ListView. Мы будем использовать холст в качестве панели обновления, которая будет размещена на вершине XAML ListView для создания этого интерфейса.
Для взаимодействия с конечным пользователем мы хотим создать эффект "сопротивления", когда пользователь активно листает список с помощью касания и прекращает движение, когда позиция выходит за пределы определенной точки.
Рабочий код для этого интерфейса можно найти в репозитории Windows UI Dev Labs на GitHub. Вот пошаговые инструкции по созданию этого опыта. В коде разметки XAML есть следующее:
<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>
Так как ListView (ThumbnailList) — это элемент управления XAML, который уже прокручивается, прокрутка должна быть привязана к родительскому элементу (ContentPanel) при достижении самого верхнего элемента и больше не может прокручиваться. (ContentPanel — это место, где будут применяться модификаторы источника.) Для этого необходимо задать ScrollViewer.IsVerticalScrollChainingEnabled true в разметке ListView. Кроме того, необходимо задать режим цепочки в VisualInteractionSource на Always.
Необходимо задать обработчик PointerPressedEvent с параметром handledEventsToo, равным true. Без этого параметра pointerPressedEvent не будет привязан к ContentPanel, так как элемент управления ListView помечает эти события как обработанные, и они не будут отправляться вверх по визуальной цепочке.
//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);
Теперь вы готовы связать это с InteractionTracker. Начните с настройки InteractionTracker, VisualInteractionSource и выражения, которое будет использовать положение 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);
При этой настройке панель обновления в начальной позиции находится вне области просмотра, и пользователь видит только listView. Когда сдвиг достигает ContentPanel, будет сгенерировано событие PointerPressed, на котором вы запрашиваете у системы использование InteractionTracker для управления процессом манипуляции.
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));
}
}
Замечание
Если события обработки цепочки не требуются, добавление обработчика PointerPressedEvent можно выполнить непосредственно с помощью разметки XAML с помощью атрибута (PointerPressed="Window_PointerPressed").
Следующим шагом является настройка модификаторов источника. Для получения этого поведения вы будете использовать 2 модификатора источника; Сопротивление и остановка.
- Сопротивление — переместить DeltaPosition.Y на половину скорости, пока она не достигнет высоты 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;
- Остановка — завершите перемещение, когда весь элемент RefreshPanel окажется на экране.
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);
На этой схеме показана визуализация установки SourceModifiers.
Теперь с SourceModifiers вы заметите, что когда сдвигаете ListView вниз и достигаете самого верхнего элемента, панель обновления опускается с половинной скоростью прокрутки, пока она не достигнет высоты RefreshPanel, и затем прекращает движение.
В полном варианте используется анимация с ключевыми кадрами для вращения значка в процессе взаимодействия на холсте RefreshPanel. Любое содержимое можно использовать на месте или использовать положение InteractionTracker для управления анимацией отдельно.
Windows developer