前言
看到过其他平台游戏中第三方sdk的一些悬浮的小助手,近来无聊,就做个玩儿,看着比较简单,做起来还是花费了不少力气。
开始
首先是页面的xaml代码,其实比较简单,就是一些Canvas里面装了些image和一个border(是作为按钮展开后的背景)。下面注释的那个canvas是为了看到悬浮窗在界面右边的时的效果,方便做动画添加的,读者不用在意
要求悬浮按钮可以拖动,拖动后靠在自动屏幕边上显示,拖动的时候超过屏幕的中轴就靠在右边,点击按钮向左边以此展开按钮,如果拖动后在屏幕中轴左边则靠左侧边上,点击屏幕向右依次展开,再次点击收回按钮。(展开按钮的时候我有添加了个选择动画,旋转了360度,)考虑到动画需要变化,以前悬浮窗的中的按钮位置也要根据拖放的位置动态变化,所以动画和布局都用代码控制。
<phone:PhoneApplicationPage.Resources> <Style x:Key="imageS" TargetType="Image"> <Setter Property="Width" Value="50"/> <Setter Property="Height" Value="50"/> <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/> </Style> <!--<Storyboard x:Name="Storyboard1"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.ScaleX)" Storyboard.TargetName="border"> <EasingDoubleKeyFrame KeyTime="0" Value="0.04"/> <EasingDoubleKeyFrame KeyTime="0:0:0.4" Value="1.003"/> </DoubleAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="border"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> <DiscreteObjectKeyFrame KeyTime="0:0:0.4"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard>--> </phone:PhoneApplicationPage.Resources> <!--LayoutRoot 是包含所有页面内容的根网格--> <Grid> <Grid.Background> <ImageBrush Stretch="Fill" ImageSource="/Assets/Image/bg1.jpg"/> </Grid.Background> <Canvas x:Name="HoverContentner" HorizontalAlignment="Left" Height="50" Margin="0,0,0,0" VerticalAlignment="Top" Width="300" > <Border Visibility="Collapsed" x:Name="border" Width="300" Height="47" HorizontalAlignment="Left" VerticalAlignment="Top" CornerRadius="12" Background="#B2D1DAC7" RenderTransformOrigin="0.0,0.0"> <Border.RenderTransform> <CompositeTransform/> </Border.RenderTransform> </Border> <Image x:Name="image0" Style="{StaticResource imageS}" Source="/Assets/Image/button1.png" Tap="image0_Tap" ManipulationDelta="image0_ManipulationDelta" ManipulationCompleted="image0_ManipulationCompleted" > <Image.RenderTransform> <CompositeTransform/> </Image.RenderTransform> </Image> <Image Visibility="Collapsed" x:Name="image1" Style="{StaticResource imageS}" Canvas.Left="60" Source="/Assets/Image/button2.png" > <Image.RenderTransform> <CompositeTransform/> </Image.RenderTransform> </Image> <Image Visibility="Collapsed" x:Name="image2" Style="{StaticResource imageS}" Canvas.Left="120" Source="/Assets/Image/button3.png"> <Image.RenderTransform> <CompositeTransform/> </Image.RenderTransform> </Image> <Image Visibility="Collapsed" x:Name="image3" Style="{StaticResource imageS}" Canvas.Left="180" Source="/Assets/Image/button4.png"> <Image.RenderTransform> <CompositeTransform/> </Image.RenderTransform> </Image> <Image Visibility="Collapsed" x:Name="image4" Style="{StaticResource imageS}" Canvas.Left="240" Source="/Assets/Image/button5.png" > <Image.RenderTransform> <CompositeTransform/> </Image.RenderTransform> </Image> </Canvas> <!--<Canvas x:Name="HoverContentnerRight" HorizontalAlignment="Left" Height="50" Margin="180,300,0,0" VerticalAlignment="Top" Width="300"> <Image Visibility="Visible" Style="{StaticResource imageS}" Canvas.Left="180" Source="/Assets/Image/button2.png" > <Image.RenderTransform> <CompositeTransform/> </Image.RenderTransform> </Image> <Image Visibility="Visible" Style="{StaticResource imageS}" Canvas.Left="120" Source="/Assets/Image/button3.png" > <Image.RenderTransform> <CompositeTransform/> </Image.RenderTransform> </Image> <Image Visibility="Visible" Style="{StaticResource imageS}" Canvas.Left="60" Source="/Assets/Image/button4.png" > <Image.RenderTransform> <CompositeTransform/> </Image.RenderTransform> </Image> <Image Visibility="Visible" Style="{StaticResource imageS}" Canvas.Left="0" Source="/Assets/Image/button5.png" > <Image.RenderTransform> <CompositeTransform/> </Image.RenderTransform> </Image> <Image x:Name="image0left" Style="{StaticResource imageS}" Canvas.Left="240" Source="/Assets/Image/button1.png" Tap="image0_Tap" ManipulationDelta="image0_ManipulationDelta" ManipulationCompleted="image0_ManipulationCompleted" > <Image.RenderTransform> <CompositeTransform/> </Image.RenderTransform> </Image> </Canvas>--> </Grid> </phone:PhoneApplicationPage>
一些定义的变量
private int trasnlateLength = 60; private bool left = true; private bool lastleft = true;//lastleft上次 private Storyboard expandstory = null; private Storyboard hidestory = null; private bool isStoryBegin = true;
首先是处理拖动的代码
private void image0_ManipulationDelta(object sender, System.Windows.Input.ManipulationDeltaEventArgs e) { if (image1.Visibility == Visibility.Visible) { } else { double oldleft = HoverContentner.Margin.Left; double oldtop = HoverContentner.Margin.Top; //if (!left && e.DeltaManipulation.Translation.X<0) //{ // oldleft += -e.DeltaManipulation.Translation.X; //} //else //{ // oldleft += e.DeltaManipulation.Translation.X; //} oldleft += e.DeltaManipulation.Translation.X; oldtop += e.DeltaManipulation.Translation.Y; // Debug.WriteLine("左边距"+oldleft + "............... 右边距"+ oldtop); HoverContentner.Margin = new Thickness(oldleft, oldtop, 0, 0); this.UpdateLayout(); } }
拖动完成后根据位置靠边显示
private void image0_ManipulationCompleted(object sender, System.Windows.Input.ManipulationCompletedEventArgs e) { if (e.TotalManipulation.Translation.X==0.0f&&e.TotalManipulation.Translation.Y==0.0f) { return; } // Debug.WriteLine(" HoverContentner.Margin.left: " + HoverContentner.Margin.Left); if (left &&HoverContentner.Margin.Left < 220) { HoverContentner.Margin = new Thickness(0, HoverContentner.Margin.Top, 0, 0); updateCanvas(true); } else if (left &&HoverContentner.Margin.Left > 220) { HoverContentner.Margin = new Thickness(185, HoverContentner.Margin.Top, 0, 0); updateCanvas(false); } else if (!left&&HoverContentner.Margin.Left > 0) { HoverContentner.Margin = new Thickness(185, HoverContentner.Margin.Top, 0, 0); updateCanvas(false); } else { HoverContentner.Margin = new Thickness(0, HoverContentner.Margin.Top, 0, 0); updateCanvas(true); } }
然后更新布局
private void updateCanvas( bool isleft) { left = isleft; if (isleft) { // trasnlateLength = 60; Canvas.SetLeft(image0, 0); Canvas.SetLeft(image1, 60); Canvas.SetLeft(image2, 120); Canvas.SetLeft(image3, 185); Canvas.SetLeft(image4, 240); border.HorizontalAlignment = HorizontalAlignment.Left; border.RenderTransformOrigin = new Point(0, 0); } else { // trasnlateLength = -60; Canvas.SetLeft(image0, 240); Canvas.SetLeft(image1, 185); Canvas.SetLeft(image2, 120); Canvas.SetLeft(image3, 60); Canvas.SetLeft(image4, 0); border.HorizontalAlignment = HorizontalAlignment.Right; border.RenderTransformOrigin = new Point(1, 1); HoverContentner.Width = 300; //image1.Visibility = Visibility.Visible; //image2.Visibility = Visibility.Visible; //image3.Visibility = Visibility.Visible; //image4.Visibility = Visibility.Visible; UpdateLayout(); } }
点击图片时开始执行动画,一个展开的和一个收回的动画,如果上次悬浮窗的位置和这次的位置不同(上次在左边,这次在右边,则更新动画)
private void image0_Tap(object sender, System.Windows.Input.GestureEventArgs e) { if (lastleft != left)//如果方向改变则刷新动画 { expandstory = CreateExpand(); hidestory = HideExpand(); } if (isStoryBegin) { expandstory.Begin(); isStoryBegin = false; } else { hidestory.Begin(); isStoryBegin = true; } }
展开的动画
private Storyboard CreateExpand() { Storyboard expand = new Storyboard(); #region 旋转动画 for (int i = 0; i < 5; i++) { //旋转动画 DoubleAnimationUsingKeyFrames rotation = new DoubleAnimationUsingKeyFrames(); Storyboard.SetTargetProperty(rotation, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.Rotation)")); //只有动画在xaml中才可以使用这个方法 // Storyboard.SetTargetName(rotation, "image" + i); Storyboard.SetTarget(rotation, this.FindName("image" + i) as Image); EasingDoubleKeyFrame startAngle = new EasingDoubleKeyFrame(); startAngle.Value = -360; startAngle.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)); rotation.KeyFrames.Add(startAngle); if (i == 2) { rotation.BeginTime = TimeSpan.FromMilliseconds(50); } else if (i == 3) { rotation.BeginTime = TimeSpan.FromMilliseconds(100); } else if (i == 4) { rotation.BeginTime = TimeSpan.FromMilliseconds(150); } EasingDoubleKeyFrame endAngle = new EasingDoubleKeyFrame(); endAngle.Value = 0; endAngle.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300)); rotation.KeyFrames.Add(endAngle); expand.Children.Add(rotation); } #endregion for (int i = 1; i < 5; i++) { #region 平移动画 //X轴平移 DoubleAnimationUsingKeyFrames xtranslate = new DoubleAnimationUsingKeyFrames(); Storyboard.SetTargetProperty(xtranslate, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.TranslateX)")); Storyboard.SetTarget(xtranslate, this.FindName("image" + i) as Image); //起始点 EasingDoubleKeyFrame xstartPoint = new EasingDoubleKeyFrame(); xstartPoint.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)); if (i == 1) { xstartPoint.Value = left ? -trasnlateLength : trasnlateLength; } else if (i == 2) { xtranslate.BeginTime = TimeSpan.FromMilliseconds(50); xstartPoint.Value = left ? -trasnlateLength * 2 : trasnlateLength * 2; } else if (i == 3) { xtranslate.BeginTime = TimeSpan.FromMilliseconds(100); xstartPoint.Value = left ? -trasnlateLength * 3 : trasnlateLength * 3; } else if (i == 4) { xtranslate.BeginTime = TimeSpan.FromMilliseconds(150); xstartPoint.Value = left ? -trasnlateLength * 4 : trasnlateLength * 4; ; } lastleft = left; xstartPoint.EasingFunction = new PowerEase() { EasingMode = 0 }; //终点 EasingDoubleKeyFrame xendPoint = new EasingDoubleKeyFrame(); xendPoint.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300)); xendPoint.Value = 0; xendPoint.EasingFunction = new PowerEase() { EasingMode = 0 }; xtranslate.KeyFrames.Add(xstartPoint); xtranslate.KeyFrames.Add(xendPoint); expand.Children.Add(xtranslate); #endregion #region 改变属性值的动画 //改变属性值的动画 ObjectAnimationUsingKeyFrames visi = new ObjectAnimationUsingKeyFrames(); Storyboard.SetTargetProperty(visi, new PropertyPath("(UIElement.Visibility)")); Storyboard.SetTarget(visi, this.FindName("image" + i) as Image); DiscreteObjectKeyFrame startObj = new DiscreteObjectKeyFrame(); startObj.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)); startObj.Value = Visibility.Visible; DiscreteObjectKeyFrame endObj = new DiscreteObjectKeyFrame(); endObj.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300)); endObj.Value = Visibility.Visible; if (i == 2) { visi.BeginTime = TimeSpan.FromMilliseconds(50); } else if (i == 3) { visi.BeginTime = TimeSpan.FromMilliseconds(100); } else if (i == 4) { visi.BeginTime = TimeSpan.FromMilliseconds(150); } visi.KeyFrames.Add(startObj); visi.KeyFrames.Add(endObj); expand.Children.Add(visi); #endregion } #region 缩放动画 //X轴平移 DoubleAnimationUsingKeyFrames xtranslateb = new DoubleAnimationUsingKeyFrames(); Storyboard.SetTargetProperty(xtranslateb, new PropertyPath("(UIElement.RenderTransform).(CompositeTransform.ScaleX)")); Storyboard.SetTarget(xtranslateb, this.FindName("border" ) as Border); //起始点 EasingDoubleKeyFrame xstartPointb = new EasingDoubleKeyFrame(); xstartPointb.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)); xstartPointb.Value = 0.04f; xstartPointb.EasingFunction = new PowerEase() { EasingMode = 0 }; //终点 EasingDoubleKeyFrame xendPointb = new EasingDoubleKeyFrame(); xendPointb.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300)); xendPointb.Value = 1; xendPointb.EasingFunction = new PowerEase() { EasingMode = 0 }; xtranslateb.KeyFrames.Add(xstartPointb); xtranslateb.KeyFrames.Add(xendPointb); expand.Children.Add(xtranslateb); #endregion #region 改变border属性值的动画 //改变属性值的动画 ObjectAnimationUsingKeyFrames visib = new ObjectAnimationUsingKeyFrames(); Storyboard.SetTargetProperty(visib, new PropertyPath("(UIElement.Visibility)")); Storyboard.SetTarget(visib, this.FindName("border") as Border); DiscreteObjectKeyFrame startObjb = new DiscreteObjectKeyFrame(); startObjb.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(0)); startObjb.Value = Visibility.Visible; DiscreteObjectKeyFrame endObjb = new DiscreteObjectKeyFrame(); endObjb.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300)); endObjb.Value = Visibility.Visible; visib.KeyFrames.Add(startObjb); visib.KeyFrames.Add(endObjb); expand.Children.Add(visib); #endregion return expand; }
还有个收缩的动画就不给大家贴出来了,留给大家一个自己动手的过程。哇咔咔,下面贴张动态图,吸引下诸位眼球。