你的位置:首页 > ASP.net教程

[ASP.net教程]15、导航滑动动画


 

  在触屏设备上,手指滑动页面,或者单击导航选项时,增加导航下横线滑动的效果:

  这个版本有点简单粗暴,同事在项目中优化了一下算法。这里只是简单记录一下大致思路:

 

1、导航使用 ListView 控件,下面使用 Pivot 控件

  大致结构为:

 

页面中的 ListView:

<ListView x:Name="listview" VerticalAlignment="Top" Margin="0,40,0,0" Background="White" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Enabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollMode="Disabled" SelectionChanged="listView_SelectionChanged">      <ListView.ItemContainerStyle>        <Style TargetType="ListViewItem">          <Setter Property="Template">            <Setter.Value>              <ControlTemplate TargetType="ListViewItem">                <!--选中色设置为 红色-->                <ListViewItemPresenter CheckBrush="{x:Null}" ContentMargin="{TemplateBinding Padding}" CheckMode="Inline" ContentTransitions="{x:Null}" CheckBoxBrush="{x:Null}" DragForeground="{x:Null}" DragOpacity="0" DragBackground="{x:Null}" DisabledOpacity="0" FocusBorderBrush="{x:Null}" FocusSecondaryBorderBrush="{x:Null}" HorizontalContentAlignment="Center" PointerOverForeground="{ThemeResource SystemControlHighlightAltBaseHighBrush}" PressedBackground="{x:Null}" PlaceholderBackground="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" PointerOverBackground="{x:Null}" ReorderHintOffset="{ThemeResource ListViewItemReorderHintThemeOffset}" SelectionCheckMarkVisualEnabled="True" SelectedForeground="Red" SelectedPointerOverBackground="{x:Null}" SelectedBackground="{x:Null}" VerticalContentAlignment="Center" SelectedPressedBackground="{x:Null}" Foreground="Black"/>              </ControlTemplate>            </Setter.Value>          </Setter>        </Style>      </ListView.ItemContainerStyle>      <ListView.ItemsPanel>        <ItemsPanelTemplate>          <ItemsStackPanel Orientation="Horizontal"/>        </ItemsPanelTemplate>      </ListView.ItemsPanel>      <ListView.ItemTemplate>        <DataTemplate>          <Grid>            <Grid.RowDefinitions>              <RowDefinition Height="Auto"/>              <RowDefinition Height="Auto"/>            </Grid.RowDefinitions>            <TextBlock Text="{Binding Text}" FontSize="20" Margin="20,10"/>

              <!--作为位移指示条--> <Rectangle x:Name="rect" Fill="Red" Height="2" HorizontalAlignment="Stretch" Grid.Row="1" Opacity="0" Loaded="rect_Loaded"> <Rectangle.RenderTransform> <CompositeTransform/> </Rectangle.RenderTransform> </Rectangle> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView>

 

选中时,播放的位移动画:

<Page.Resources>    <!--#region 导航下划线位移动画-->    <Storyboard x:Name="SB_Slide" Completed="SB_Slide_Completed">      <DoubleAnimationUsingKeyFrames x:Name="SB_Slide_TransX" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)" >        <!--Storyboard.TargetName="rect1"-->        <EasingDoubleKeyFrame KeyTime="0" Value="0"/>        <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="600">          <EasingDoubleKeyFrame.EasingFunction>            <!--回弹效果-->            <!--<BackEase EasingMode="EaseOut" Amplitude="0.3"/>-->            <QuarticEase EasingMode="EaseOut"/>          </EasingDoubleKeyFrame.EasingFunction>        </EasingDoubleKeyFrame>      </DoubleAnimationUsingKeyFrames>    </Storyboard>    <!--#endregion--></Page.Resources>

 

Pivot 中,重写 Template,去掉 Header 等多余对象:

   <Pivot x:Name="pivot" Grid.Row="1" SelectedItem="{Binding SelectedItem,ElementName=listview,Mode=TwoWay}">      <Pivot.ItemContainerStyle>        <Style TargetType="PivotItem">          <!--默认 Item 两侧有大约20px 的 Margin-->          <Setter Property="Margin" Value="0"/>        </Style>      </Pivot.ItemContainerStyle>      <Pivot.ItemTemplate>        <DataTemplate>          <Grid Background="LightBlue">            <TextBlock FontSize="30" Text="{Binding Text}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="20"/>          </Grid>        </DataTemplate>      </Pivot.ItemTemplate>      <Pivot.Template>        <!--去掉模板中多余的 UI(Header 等)和动画-->        <ControlTemplate TargetType="Pivot">          <Grid x:Name="RootElement" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">            <Grid.Resources>              <Style x:Key="BaseContentControlStyle" TargetType="ContentControl">                <Setter Property="FontFamily" Value="Segoe UI"/>                <Setter Property="FontWeight" Value="SemiBold"/>                <Setter Property="FontSize" Value="15"/>                <Setter Property="Template">                  <Setter.Value>                    <ControlTemplate TargetType="ContentControl">                      <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" OpticalMarginAlignment="TrimSideBearings" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>                    </ControlTemplate>                  </Setter.Value>                </Setter>              </Style>              <Style x:Key="TitleContentControlStyle" BasedOn="{StaticResource BaseContentControlStyle}" TargetType="ContentControl">                <Setter Property="FontFamily" Value="{ThemeResource PivotTitleFontFamily}"/>                <Setter Property="FontWeight" Value="{ThemeResource PivotTitleThemeFontWeight}"/>                <Setter Property="FontSize" Value="{ThemeResource PivotTitleFontSize}"/>              </Style>            </Grid.Resources>            <Grid.RowDefinitions>              <RowDefinition Height="Auto"/>              <RowDefinition Height="*"/>            </Grid.RowDefinitions>            <VisualStateManager.VisualStateGroups>              <VisualStateGroup x:Name="Orientation">                <VisualState x:Name="Portrait">                  <!--<Storyboard>                      <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="TitleContentControl">                        <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotPortraitThemePadding}"/>                      </ObjectAnimationUsingKeyFrames>                    </Storyboard>-->                </VisualState>                <VisualState x:Name="Landscape">                  <!--<Storyboard>                      <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Margin" Storyboard.TargetName="TitleContentControl">                        <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource PivotLandscapeThemePadding}"/>                      </ObjectAnimationUsingKeyFrames>                    </Storyboard>-->                </VisualState>              </VisualStateGroup>              <VisualStateGroup x:Name="NavigationButtonsVisibility">                <VisualState x:Name="NavigationButtonsHidden"/>                <VisualState x:Name="NavigationButtonsVisible">                  <!--<Storyboard>                      <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="NextButton">                        <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>                      </ObjectAnimationUsingKeyFrames>                      <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsEnabled" Storyboard.TargetName="NextButton">                        <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>                      </ObjectAnimationUsingKeyFrames>                      <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PreviousButton">                        <DiscreteObjectKeyFrame KeyTime="0" Value="1"/>                      </ObjectAnimationUsingKeyFrames>                      <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="IsEnabled" Storyboard.TargetName="PreviousButton">                        <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>                      </ObjectAnimationUsingKeyFrames>                    </Storyboard>-->                </VisualState>              </VisualStateGroup>              <VisualStateGroup x:Name="HeaderStates">                <VisualState x:Name="HeaderDynamic"/>                <VisualState x:Name="HeaderStatic">                  <!--<Storyboard>                      <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="Header">                        <DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>                      </ObjectAnimationUsingKeyFrames>                      <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="StaticHeader">                        <DiscreteObjectKeyFrame KeyTime="0" Value="Visible"/>                      </ObjectAnimationUsingKeyFrames>                    </Storyboard>-->                </VisualState>              </VisualStateGroup>            </VisualStateManager.VisualStateGroups>            <ContentControl x:Name="TitleContentControl" ContentTemplate="{TemplateBinding TitleTemplate}" Content="{TemplateBinding Title}" IsTabStop="False" Margin="{StaticResource PivotPortraitThemePadding}" Style="{StaticResource TitleContentControlStyle}" Visibility="Collapsed"/>            <Grid Grid.Row="1">              <!--<Grid.Resources>                  <ControlTemplate x:Key="NextTemplate" TargetType="Button">                    <Border x:Name="Root" BorderBrush="{ThemeResource SystemControlForegroundTransparentBrush}" BorderThickness="{ThemeResource PivotNavButtonBorderThemeThickness}" Background="{ThemeResource SystemControlBackgroundBaseMediumLowBrush}">                      <VisualStateManager.VisualStateGroups>                        <VisualStateGroup x:Name="CommonStates">                          <VisualState x:Name="Normal"/>                          <VisualState x:Name="PointerOver">                            <Storyboard>                              <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseMediumBrush}"/>                              </ObjectAnimationUsingKeyFrames>                              <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="Arrow">                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltAltMediumHighBrush}"/>                              </ObjectAnimationUsingKeyFrames>                            </Storyboard>                          </VisualState>                          <VisualState x:Name="Pressed">                            <Storyboard>                              <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseMediumHighBrush}"/>                              </ObjectAnimationUsingKeyFrames>                              <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="Arrow">                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltAltMediumHighBrush}"/>                              </ObjectAnimationUsingKeyFrames>                            </Storyboard>                          </VisualState>                        </VisualStateGroup>                      </VisualStateManager.VisualStateGroups>                      <FontIcon x:Name="Arrow" Foreground="{ThemeResource SystemControlForegroundAltMediumHighBrush}" FontSize="12" FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph="&#xE0E3;" HorizontalAlignment="Center" MirroredWhenRightToLeft="True" UseLayoutRounding="False" VerticalAlignment="Center"/>                    </Border>                  </ControlTemplate>                  <ControlTemplate x:Key="PreviousTemplate" TargetType="Button">                    <Border x:Name="Root" BorderBrush="{ThemeResource SystemControlForegroundTransparentBrush}" BorderThickness="{ThemeResource PivotNavButtonBorderThemeThickness}" Background="{ThemeResource SystemControlBackgroundBaseMediumLowBrush}">                      <VisualStateManager.VisualStateGroups>                        <VisualStateGroup x:Name="CommonStates">                          <VisualState x:Name="Normal"/>                          <VisualState x:Name="PointerOver">                            <Storyboard>                              <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseMediumBrush}"/>                              </ObjectAnimationUsingKeyFrames>                              <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="Arrow">                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltAltMediumHighBrush}"/>                              </ObjectAnimationUsingKeyFrames>                            </Storyboard>                          </VisualState>                          <VisualState x:Name="Pressed">                            <Storyboard>                              <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="Root">                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseMediumHighBrush}"/>                              </ObjectAnimationUsingKeyFrames>                              <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="Arrow">                                <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightAltAltMediumHighBrush}"/>                              </ObjectAnimationUsingKeyFrames>                            </Storyboard>                          </VisualState>                        </VisualStateGroup>                      </VisualStateManager.VisualStateGroups>                      <FontIcon x:Name="Arrow" Foreground="{ThemeResource SystemControlForegroundAltMediumHighBrush}" FontSize="12" FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph="&#xE0E2;" HorizontalAlignment="Center" MirroredWhenRightToLeft="True" UseLayoutRounding="False" VerticalAlignment="Center"/>                    </Border>                  </ControlTemplate>                </Grid.Resources>-->              <ScrollViewer x:Name="ScrollViewer" BringIntoViewOnFocusChange="False" HorizontalSnapPointsAlignment="Center" HorizontalSnapPointsType="MandatorySingle" HorizontalScrollBarVisibility="Hidden" Margin="{TemplateBinding Padding}" Template="{StaticResource ScrollViewerScrollBarlessTemplate}" VerticalSnapPointsType="None" VerticalScrollBarVisibility="Disabled" VerticalScrollMode="Disabled" VerticalContentAlignment="Stretch" ZoomMode="Disabled"                     ViewChanging="ScrollViewer_ViewChanging" DirectManipulationStarted="ScrollViewer_DirectManipulationStarted" >                <PivotPanel x:Name="Panel" VerticalAlignment="Stretch">                  <Grid x:Name="PivotLayoutElement">                    <Grid.ColumnDefinitions>                      <ColumnDefinition Width="Auto"/>                      <ColumnDefinition Width="*"/>                      <ColumnDefinition Width="Auto"/>                    </Grid.ColumnDefinitions>                    <Grid.RowDefinitions>                      <RowDefinition Height="Auto"/>                      <RowDefinition Height="*"/>                    </Grid.RowDefinitions>                    <Grid.RenderTransform>                      <CompositeTransform x:Name="PivotLayoutElementTranslateTransform"/>                    </Grid.RenderTransform>                    <!--<ContentPresenter x:Name="LeftHeaderPresenter" ContentTemplate="{TemplateBinding LeftHeaderTemplate}" Content="{TemplateBinding LeftHeader}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>-->                    <ContentControl x:Name="HeaderClipper" Grid.Column="1" HorizontalContentAlignment="Stretch" UseSystemFocusVisuals="True" Visibility="Collapsed">                      <ContentControl.Clip>                        <RectangleGeometry x:Name="HeaderClipperGeometry"/>                      </ContentControl.Clip>                      <Grid Background="Transparent">                        <PivotHeaderPanel x:Name="StaticHeader" Visibility="Collapsed"/>                        <PivotHeaderPanel x:Name="Header">                          <PivotHeaderPanel.RenderTransform>                            <TransformGroup>                              <CompositeTransform x:Name="HeaderTranslateTransform"/>                              <CompositeTransform x:Name="HeaderOffsetTranslateTransform"/>                            </TransformGroup>                          </PivotHeaderPanel.RenderTransform>                        </PivotHeaderPanel>                      </Grid>                    </ContentControl>                    <!--<Button x:Name="PreviousButton" Background="Transparent" Grid.Column="1" HorizontalAlignment="Left" Height="36" IsTabStop="False" IsEnabled="False" Margin="{ThemeResource PivotNavButtonMargin}" Opacity="0" Template="{StaticResource PreviousTemplate}" UseSystemFocusVisuals="False" VerticalAlignment="Top" Width="20"/>                      <Button x:Name="NextButton" Background="Transparent" Grid.Column="1" HorizontalAlignment="Right" Height="36" IsTabStop="False" IsEnabled="False" Margin="{ThemeResource PivotNavButtonMargin}" Opacity="0" Template="{StaticResource NextTemplate}" UseSystemFocusVisuals="False" VerticalAlignment="Top" Width="20"/>                    <ContentPresenter x:Name="RightHeaderPresenter" ContentTemplate="{TemplateBinding RightHeaderTemplate}" Content="{TemplateBinding RightHeader}" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />-->                    <ItemsPresenter x:Name="PivotItemPresenter" Grid.ColumnSpan="3" Grid.Row="1">                      <ItemsPresenter.RenderTransform>                        <TransformGroup>                          <TranslateTransform x:Name="ItemsPresenterTranslateTransform"/>                          <CompositeTransform x:Name="ItemsPresenterCompositeTransform"/>                        </TransformGroup>                      </ItemsPresenter.RenderTransform>                    </ItemsPresenter>                  </Grid>                </PivotPanel>              </ScrollViewer>            </Grid>          </Grid>        </ControlTemplate>      </Pivot.Template>    </Pivot>

View Code

 

 

2、事件操作

1)大致思路是,当手指滑动屏幕时,通过 Pivot 中 ScrollViewer 的 ViewChanging 事件,来横向移动选中 Item 中红色的矩形(Rectangle),当动画结束时,再重置当前的 Rectangle 的位移。

如果单击 ListView 中的 Item,则直接在它的 SelectionChanged 事件中,计算前一个 Rectangle 和新 Rectangle 的相对位置,来播放位移动画。

2) code behind 中的代码:

  public sealed partial class MainPage : Page  {    public MainPage()    {      this.InitializeComponent();      this.Loaded += MainPage_Loaded;    }    private void MainPage_Loaded(object sender, RoutedEventArgs e)    {      this.Loaded -= MainPage_Loaded;      // 绑定到匿名类型上      pivot.ItemsSource = listview.ItemsSource = new []{ new { Text = "军事" } , new { Text = "科技" }, new { Text = "国内新闻" },new { Text = "娱乐" }, new { Text = "国际新闻" }, new { Text = "相声" }, new { Text = "体育赛事" }, new { Text = "综艺" } };      listview.SelectedIndex = 0;          }    #region 横向 Rectangle 的位移    Rectangle rect_old; // 上一次选中的 Rectangle    Rectangle rect_current; // 当前选中的 Rectangle    double posi_previous; // 手指点击屏幕时,记录当前的 pivot 中 ScrollViewer.HorizontalOffset    ScrollViewer pivot_sv;    bool IsMoving = false;//手指是否在滑动中    private void listView_SelectionChanged(object sender, SelectionChangedEventArgs e)    {      if (firstRectInited == false) return;      // 如果是当前显示项,则显示“下横线”      for (int i = 0; i < listview.Items.Count; i++)      {        if (listview.ContainerFromIndex(i) != null)        {          var grid = (listview.ContainerFromIndex(i) as ListViewItem).ContentTemplateRoot as Grid;          var rect = grid.FindName("rect") as Rectangle;          (rect.RenderTransform as CompositeTransform).TranslateX = 0;// 重置横向位移为 0          if (listview.SelectedIndex == i) // 当前选中项          {            listview.ScrollIntoView(listview.SelectedItem);//当滑动 pivot 时,如果 ListView选中项不在视图内,则显示            rect_old = rect_current;            rect_current = rect;            if (IsMoving == false) // 非手指划动 pivot            {              Rect_Slide();              listview.IsHitTestVisible = false; // 当“划动动画”在播放的时候,不再接受单击事件            }          }          if (IsMoving)            rect.Opacity = listview.SelectedIndex == i ? 1 : 0;//选中项显示下横向,否则隐藏        }      }      IsMoving = false;    }    //播放划动动画    void Rect_Slide()    {      if (rect_old != null && rect_current != null)      {        // 如果设置 Width 属性,可能会导致列表宽度发生变化,所以这里使用 Scale来缩放下横线        (rect_old.RenderTransform as CompositeTransform).ScaleX = rect_current.ActualWidth / rect_old.ActualWidth;        var old_rect = GetBounds(rect_old, listview);        var new_rect = GetBounds(rect_current, listview);        // 获取 ListView 单击后,两个 Item之间的距离        SB_Slide_TransX.KeyFrames[1].Value = new_rect.X - old_rect.X;        Storyboard.SetTarget(SB_Slide_TransX, rect_old);        SB_Slide.Begin();      }    }    // 获取子元素相对于父元素的左边    public Rect GetBounds(FrameworkElement childElement, FrameworkElement parentElement)    {      GeneralTransform transform = childElement.TransformToVisual(parentElement);      return transform.TransformBounds(new Rect(0, 0, childElement.ActualWidth, childElement.ActualHeight));    }    // 滑动动画结束时,重置参数    private void SB_Slide_Completed(object sender, object e)    {      listview.IsHitTestVisible = true;      rect_old.Opacity = 0;      (rect_old.RenderTransform as CompositeTransform).ScaleX = 1;      rect_current.Opacity = 1;      SB_Slide.Stop();    }    // 手指横向移动 Pivot 时,更改下横向的位移    private void ScrollViewer_ViewChanging(object sender, ScrollViewerViewChangingEventArgs e)    {      if (IsMoving && rect_current != null && pivot_sv != null)        (rect_current.RenderTransform as CompositeTransform).TranslateX = (pivot_sv.HorizontalOffset - posi_previous) / pivot_sv.ActualWidth * rect_current.ActualWidth;    }    // 手指开始触摸屏幕时    private void ScrollViewer_DirectManipulationStarted(object sender, object e)    {      IsMoving = true;      pivot_sv = sender as ScrollViewer;      posi_previous = pivot_sv.HorizontalOffset;    }    // 查找第一个 Rectangle    bool firstRectInited = false;    private void rect_Loaded(object sender, RoutedEventArgs e)    {      var r = sender as Rectangle;      r.Loaded -= rect_Loaded;      if (!firstRectInited && listview.ContainerFromIndex(0) != null)      {        var grid = (listview.ContainerFromIndex(0) as ListViewItem).ContentTemplateRoot as Grid;        rect_current = grid.FindName("rect") as Rectangle;        rect_current.Opacity = 1;        firstRectInited = true;      }    }    #endregion      }

 

 demo 工程源码