UWP Composition API, uwpcompositionapi
Background:
Previously, ScrollViewer was used to control PullToRefresh. There are always some problems in some special conditions of the project. For example, ScrollViewer does not arrive at the specified position in time.
So we can use the Composition API to re-implement the PullToRefresh control. The difficulty of this control is not to implement, but to explore the Composition API.
Some of the ideas or conclusions in this article are not necessarily all right. They are obtained through experiments, and there are too few materials available for the Composition API.
Finished product:
MATERIALS:
Composition API Materials
1. Official Sample
2. The original author Nick Waggoner works for Microsoft's native Windows UI platform link to translate the article VALID VOID
3. Old documents
I checked some information on the Internet and saw that some great gods on the Internet have already achieved this. I understand the general implementation process, because the effect I want to do is still different from that of the great god, so
Or manually encapsulate them into controls.
Implementation principle:
Here we reference the words in VALID VOID:
Input driver Animation
Since touch became mainstream about five years ago, creating a low-latency experience has become a common demand. Using fingers or pens on the screen allows the human eye to gain a more intuitive reference point to identify the operation delay and smoothness. To ensure smooth operation, mainstream operating system companies all hand over more operations to the system and GPU (such as Chrome and IE) for execution. In Windows, DirectManipulation is more or less implemented by animation engines built on touch. It solves the key latency challenge, that is, how to naturally demonstrate the dynamic effects of the transition from input-driven to event-driven. But on the other hand, it almost does not provide support for custom inertial views, just like a Ford T-car-"as long as the car is black, you can paint it in any color you like ". 2
ElementCompositionPreview.GetScrollViewerManipulationPropertySetIt is the first step that allows you to drive the effects of game input. Although it still does not give you any additional control over the view when the content is scrolling, it does allow you to apply Expression Animation to the secondary content. For example, we can finally complete our basic parallax rolling code:
// Create an animation for the expression that drives the parallax scroll. ExpressionAnimation parallaxAnimation = compositor. CreateExpressionAnimation ("MyForeground. Translation. Y/MyParallaxRatio"); // you can specify a reference to a foreground object. CompositionPropertySet MyPropertySet = ElementCompositionPreview. ParallaxAnimation. SetScalarParameter ("MyParallaxRatio", 0.5f); // starts a parallax animation on the background object. BackgroundVisual. StartAnimation ("Offset. Y", parallaxAnimation );
With this technique, you can achieve a variety of excellent effects: parallax scroll, viscous header, custom scroll bar, and so on. The only thing missing is the perception of custom operations ......
Let me explain my understanding:All the shoes that have used ScrollViewer and Manipulation related events know that it is too difficult to obtain details of ScrollViewer touch,
DirectManipulationStarted and DirectManipulationCompleted get too little information, and other Manipulation events need to be set to ManipulationMode, so that all the situations should be handled by yourself. When you see
ElementCompositionPreview.GetScrollViewerManipulationPropertySet(MyScrollViewer);
When, do you feel a little kind. It seems that you have obtained some Manipulation information of ScrollViewer .. This is the most pitfall,
MyForeground is actually the result returned by GetScrollViewerManipulationPropertySet,
However, MyForeground. Translation. Y is a ghost .. Manipulation's Translation ??? Then I searched the internet,
But I checkedCompositionPropertySet didn't find any relevant Translation attribute?
Is it the same as the Translation parameter in the Manipulation event ?? This is my speculation.
This is the case on the Internet, but there is no document .. Except for Translation, you do not know whether other attributes can be used.
I have not found any answers for the moment. I am very grateful to you for leaving a message if you want to know about my shoes...
The above code indicates ing Manipulation. Translation. YThe Offset. Y of backgroundVisual.
That is to say, you can now find some useful values when ScrollViewer is rolling .. It is worth noting that the ing still takes effect when you scroll with the mouse.
This ing is real-time and has inertial effects.
Implementation process:
Because ScrollViewer is used, I have made two types. One is to refresh the content, the first element is ScrollViewer, and the other is not ScrollViewer.
If the first element of the refreshed content is ScrollViewer, the template is as follows:
<ControlTemplate TargetType="local:PullToRefreshGrid1"> <Grid> <ContentControl x:Name="Header" Opacity="0" VerticalAlignment="Top" ContentTemplate="{TemplateBinding HeaderTemplate}" HorizontalContentAlignment="Center" VerticalContentAlignment="Bottom" /> <ContentPresenter x:Name="Content" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </Grid> </ControlTemplate>
If the first element of the refreshed content is not ScrollViewer, I added a ScrollViewer for it:
<ControlTemplate TargetType="local:PullToRefreshGrid"> <Grid> <ContentControl x:Name="Header" Opacity="0" VerticalAlignment="Top" ContentTemplate="{TemplateBinding HeaderTemplate}" HorizontalContentAlignment="Center" VerticalContentAlignment="Bottom" /> <ScrollViewer x:Name="ScrollViewer" VerticalSnapPointsType="MandatorySingle" VerticalSnapPointsAlignment="Near" VerticalScrollMode="Enabled" VerticalScrollBarVisibility="Hidden" VerticalContentAlignment="Stretch" VerticalAlignment="Stretch"> <ContentPresenter x:Name="Content" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> </ScrollViewer> </Grid> </ControlTemplate>
Of course, in fact, 2nd types are also used. I just want to reduce the Child of the control. If you don't know whether there is ScrollViewer In the refresh content, use 2nd types. Some people may say that the control name is so strange .. That's because I have previously written the PullToRefresh control (PullToRefreshControl, PullToRefreshPanel), and I have all my names .. I can't think of anything better .. Everyone understands .. (Optional □items)
Well, the point is to get this ScrollViewer and then have a certain relationship with the Header (You know )...
if (RefreshThreshold == 0.0) { RefreshThreshold = headerHeight; } ratio = RefreshThreshold / headerHeight; _offsetAnimation = _compositor.CreateExpressionAnimation("(min(max(0, ScrollManipulation.Translation.Y * ratio) / Divider, 1)) * MaxOffsetY"); _offsetAnimation.SetScalarParameter("Divider", (float)RefreshThreshold); _offsetAnimation.SetScalarParameter("MaxOffsetY", (float)RefreshThreshold * 5 / 4); _offsetAnimation.SetScalarParameter("ratio", (float)ratio); _offsetAnimation.SetReferenceParameter("ScrollManipulation", _scrollerViewerManipulation); _opacityAnimation = _compositor.CreateExpressionAnimation("min((max(0, ScrollManipulation.Translation.Y * ratio) / Divider), 1)"); _opacityAnimation.SetScalarParameter("Divider", (float)headerHeight); _opacityAnimation.SetScalarParameter("ratio", (float)1); _opacityAnimation.SetReferenceParameter("ScrollManipulation", _scrollerViewerManipulation); _headerVisual = ElementCompositionPreview.GetElementVisual(_header); _contentVisual = ElementCompositionPreview.GetElementVisual(_scrollViewerBorder); _headerVisual.StartAnimation("Offset.Y", _offsetAnimation); _headerVisual.StartAnimation("Opacity", _opacityAnimation); _contentVisual.StartAnimation("Offset.Y", _offsetAnimation);
RefreshThreshold is a point that reaches Release to refresh .. You can set the height of the header by default.
MaxOffsetY is the maximum value that can be dragged when RefreshThreshold is reached. It is set to 5/4 of RefreshThreshold.
(min(max(0, ScrollManipulation.Translation.Y * ratio) / Divider, 1)) * MaxOffsetY
Let me talk about the meaning of this expression. We all know about ScrollManipulation. Translation. Y. Is the Y value of Manipulation performed by ScrollViewer. The downward value is a positive value, and the upward value is a negative value. The initial value is 0.
In combination, the maximum value is MaxOffsetY and the minimum value is 0 .. This speed is based on RefreshThreshold/headerHeight, because you will find that there is a certain maximum value when you go down the drag scrollViewer. When the RefreshThreshold is large, it is difficult for you to ScrollManipulation. translation. the Y value reaches RefreshThreshold.
min((max(0, ScrollManipulation.Translation.Y * ratio) / Divider), 1)
This is also relatively simple, it is to give the Opaicty of the header an animation.
_headerVisual.StartAnimation("Offset.Y", _offsetAnimation); _headerVisual.StartAnimation("Opacity", _opacityAnimation); _contentVisual.StartAnimation("Offset.Y", _offsetAnimation);
After that, we can start the animation. In this way, when scrollviewer is dragged down, both scrollviewer and header will move down.
Next we need to listen for offset.
private void ScrollViewer_DirectManipulationStarted(object sender, object e) { Windows.UI.Xaml.Media.CompositionTarget.Rendering += OnCompositionTargetRendering; _refresh = false; _header.Opacity = 1; }
Register the CompositionTarget. Rendering event when starting manipulat. In this event, we can get the offset changes in real time.
private void OnCompositionTargetRendering(object sender, object e) { _headerVisual.StopAnimation("Offset.Y"); var offsetY = _headerVisual.Offset.Y; IsReachThreshold = offsetY >= RefreshThreshold; _scrollViewerBorder.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, _content.Width, _content.Height - offsetY) }; Debug.WriteLine(IsReachThreshold + "," + _headerVisual.Offset.Y + "," + RefreshThreshold); _headerVisual.StartAnimation("Offset.Y", _offsetAnimation); if (!_refresh) { _refresh = IsReachThreshold; } if (_refresh) { _pulledDownTime = DateTime.Now; } if (_refresh && offsetY <= 1) { _releaseTime = DateTime.Now; } }
Here, we find that if you do not stop animation, the Offset. Y will always be 0... Very sorry ..
Finally, we can handle the PullToRefresh event in the ScrollViewer_DirectManipulationCompleted event.
private void ScrollViewer_DirectManipulationCompleted(object sender, object e) { Windows.UI.Xaml.Media.CompositionTarget.Rendering -= OnCompositionTargetRendering; var cancelled = (_releaseTime - _pulledDownTime) > TimeSpan.FromMilliseconds(250); if (_refresh) { _refresh = false; if (cancelled) { Debug.WriteLine("Refresh cancelled..."); } else { Debug.WriteLine("Refresh now!!!"); if (PullToRefresh != null) { _headerVisual.StopAnimation("Offset.Y"); LastRefreshTime = DateTime.Now; _headerVisual.StartAnimation("Offset.Y", _offsetAnimation); PullToRefresh(this, null); } } } }
Finally, the Header template can be defined .. Its DataContext is bound to this control. Useful attributes include LastRefreshTime and IsReachThreshold. You can use them to create your favorite Header styles.
This control gives you a preliminary understanding of some usage of the Composition API. Next, I will talk about more exploration ..
Open source is helpful. The source code is GitHub address.
Problem:
1. Visual inherits IDisposable. When do we need to Dispose it? Or is it managed by itself?
I tried to dispose it during unload. However, a Win32 exception occurs .. This is also unclear in the official sample.
Thank you very much for leaving a message on the children's shoes you want to know. In addition, I hope that some samples and materials will be provided for the children's shoes I know better. Thank you again.