微博上有同學問我MyerSplash是如何實現那個很炫的圖片點亮,然后移動到屏幕中央的效果。慚愧啊,我又不是作者哪里會知道。硬着頭皮去GitHub拜讀了高手的代碼,自愧弗如,比我不知道高到哪里去了……這時我就面臨一個艱難的抉擇,要么就寫個幾十篇來分析一下MyerSplash(萬一分析錯了好丟人……),要么就說看不懂(貌似也很丟人……)。
幸虧我臨機一動,決定山寨一個極簡版,做一點微小的工作,順便寫一篇博客蹭點熱度,給某軟,給UWP續一秒……
首先要明確我們希望以少量的代碼來模擬MyerSplash的部分效果,較少的代碼分析起來更為易懂,所以各位同學就不要抱怨說這個是五毛錢特效……
<ListView ItemsSource="{Binding Photos,Mode=OneTime}" ItemContainerStyle="{StaticResource photoItemContainerStyle}"> <ListView.ItemsPanel> <ItemsPanelTemplate> <ItemsWrapGrid Orientation="Horizontal"></ItemsWrapGrid> </ItemsPanelTemplate> </ListView.ItemsPanel> <ListView.ItemTemplate> <DataTemplate> <Image Source="{Binding ImageUri,Mode=OneTime}" Stretch="UniformToFill" IsTapEnabled="True" Tapped="Image_Tapped"></Image> </DataTemplate> </ListView.ItemTemplate> </ListView>
上面的XAML繪制了一個根據元素數量自動換行的列表,列表中的Item元素就是單純的Image,同時Item元素響應一個Tapped事件(這個我們后面說)。
可以看到鼠標移動時,會有一個動畫,0.5秒的漸變點亮同時圖片放大的效果(生成的這個gif圖比較渣,實際效果請運行代碼)。這是通過自定義ItemContainerStyle並在其中添加VisualStateManager來實現的。
<Style x:Key="photoItemContainerStyle" TargetType="ListViewItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <Grid> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"> <Storyboard> <DoubleAnimation Duration="0:0:0.5" From="0.5" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="contentPhoto" ></DoubleAnimation> <DoubleAnimation Duration="0:0:0.5" From="1" To="1.2" Storyboard.TargetProperty="ScaleX" Storyboard.TargetName="scaleGrid" ></DoubleAnimation> <DoubleAnimation Duration="0:0:0.5" From="1" To="1.2" Storyboard.TargetProperty="ScaleY" Storyboard.TargetName="scaleGrid" ></DoubleAnimation> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Grid.Clip> <RectangleGeometry Rect="0 0 200 200"/> </Grid.Clip> <Grid Background="Black"> <ContentPresenter x:Name="contentPhoto" Width="200" Height="200" Opacity="0.5"> <ContentPresenter.RenderTransform> <ScaleTransform x:Name="scaleGrid" ScaleX="1.0" ScaleY="1.0" CenterX="100" CenterY="100"></ScaleTransform> </ContentPresenter.RenderTransform> </ContentPresenter> </Grid> </Grid> </ControlTemplate> </Setter.Value> </Setter>
在 <VisualState x:Name="PointerOver">這個狀態我們通過Storyboard播放了三個Animation來改變圖片的透明度,以及長寬。透明度從0.5變化至1時,圖片不再透明,背景黑色Grid不再可見,模擬高亮效果。同時通過Clip將放大后圖片的外緣裁剪仍保持原始大小。
貌似已經做的差不多了……至少你已經學會如何山寨照片牆了。接下來我們要做點不一樣的,換一種方式來使用Storyboard和Animation,點擊圖片放大並移動到屏幕中央的功能,我打算通過C#代碼實現(話說這部分有點坑,寫得時候搞死我了……)
浮動顯示的圖片我們采用Popup來顯示,通過給Popup添加Child,並通過storyboard和animation來改變Width,Height屬性控制圖片大小的變化。圖片的位移則是將Popup的RenderTransform屬性設置為TranslateTransform,然后改變X軸和Y軸坐標來實現。
這里需要注意Animation對象的EnableDependentAnimation屬性,這個坑爹的屬性是 Windows Runtime 8.1新增的,如果你發現自定義的Animation不能播放,又找不到任何錯誤,那就需要把這個屬性值置為true。
private void Image_Tapped(object sender, TappedRoutedEventArgs e) { popup.IsOpen = false; var tappedImage = e.OriginalSource as Image; var image = new Image { Source = tappedImage.Source }; popup.Child = image; popup.IsOpen = true; //獲取被點擊圖片相對MainPage的坐標 var position = tappedImage.TransformToVisual(this).TransformPoint(new Point()); //獲取MainPage的中心坐標 var xCenter = ActualWidth / 2 - 200 ; var yCenter = ActualHeight / 2 - 200; var storyBoard = new Storyboard(); var extendAnimation = new DoubleAnimation { Duration = new Duration(TimeSpan.FromSeconds(0.5)), From = 200, To = 400, EnableDependentAnimation = true }; Storyboard.SetTarget(extendAnimation, image); Storyboard.SetTargetProperty(extendAnimation, "Width"); Storyboard.SetTargetProperty(extendAnimation, "Height"); var xAnimation = new DoubleAnimation { Duration = new Duration(TimeSpan.FromSeconds(0.5)), From = position.X, To = xCenter, EnableDependentAnimation = true }; Storyboard.SetTarget(xAnimation, popup); Storyboard.SetTargetProperty(xAnimation, "(UIElement.RenderTransform).(TranslateTransform.X)"); var yAnimation = new DoubleAnimation { Duration = new Duration(TimeSpan.FromSeconds(0.5)), From = position.Y, To = yCenter, EnableDependentAnimation = true }; Storyboard.SetTarget(yAnimation, popup); Storyboard.SetTargetProperty(yAnimation, "(UIElement.RenderTransform).(TranslateTransform.Y)"); storyBoard.Children.Add(extendAnimation); storyBoard.Children.Add(xAnimation); storyBoard.Children.Add(yAnimation); storyBoard.Begin(); }
補充下Popup的定義,記得要設置popup.RenderTransform = TranslateTransform(),默認可是null
public sealed partial class MainPage : Page { public List<PhotoModel> Photos { get; set; } private Popup popup = new Popup(); public MainPage() { this.InitializeComponent(); Photos = CreatePhotos(); this.DataContext = this; popup.RenderTransform = new TranslateTransform(); }
總體就這么些了,大約100行左右的代碼,直接拿去賣錢肯定是不行的。作為Demo演示給吃瓜群眾,或者忽悠BOSS還是可以的。如果需要用到產品中,去GayHub下載代碼自己改吧改吧,調調UI。
有問題新浪微博@樓上那個蜀黍,免費咨詢,當然質量也是免費的程度……
GitHub:
https://github.com/manupstairs/UWPSamples/tree/master/UWPSamples/StoryboardSample