一、前言
上回说到需要做放视频的使用向导,这两天公司里的老司机一直帮我答疑解惑,让这个任务变得挺顺的,真心感谢他们!
这次与【1】中的不同之处在于:
(1)播放和暂停按钮集成在<MediaElement>的点击事件之中,点一下是播放,再点一下是暂停
(2)加入了微软官方改写的粒子特效
(3)加上了自己琢磨的按钮旋转效果,以及按钮弹出popup效果
(4)进度条改善美观
二、代码
前台:
1 <Window 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WPF_Nav" 7 xmlns:dxlc="http://schemas.devexpress.com/winfx/2008/xaml/layoutcontrol" xmlns:dxwui="http://schemas.devexpress.com/winfx/2008/xaml/windowsui" x:Class="WPF_Nav.MainWindow" 8 mc:Ignorable="d" 9 Title="MainWindow" Height="600" Width="800" WindowStyle="None" ResizeMode="NoResize" WindowStartupLocation ="CenterScreen" Loaded="Window_Loaded"> 10 11 <Window.Resources> 12 <LinearGradientBrush x:Key="SliderBackground" StartPoint="0,0" EndPoint="0,1"> 13 <GradientStop Offset="0.5" Color="#00b3fe"/> 14 </LinearGradientBrush> 15 <LinearGradientBrush x:Key="SliderThumb" StartPoint="0,0" EndPoint="0,1"> 16 <GradientStop Offset="0" Color="#FFD9D3E8"/> 17 </LinearGradientBrush> 18 19 <Style x:Key="Slider_RepeatButton" TargetType="RepeatButton"> 20 <Setter Property="Focusable" Value="false" /> 21 <Setter Property="Height" Value="5"/> 22 <Setter Property="BorderBrush" Value="Transparent"/> 23 <Setter Property="Template"> 24 <Setter.Value> 25 <ControlTemplate TargetType="RepeatButton"> 26 <Border Background="{StaticResource SliderBackground}" /> 27 </ControlTemplate> 28 </Setter.Value> 29 </Setter> 30 </Style> 31 32 <Style x:Key="Slider_RepeatButton1" TargetType="RepeatButton"> 33 <Setter Property="Focusable" Value="false" /> 34 <Setter Property="Height" Value="5"/> 35 <Setter Property="BorderBrush" Value="Transparent"/> 36 <Setter Property="Template"> 37 <Setter.Value> 38 <ControlTemplate TargetType="RepeatButton"> 39 <Border Background="Silver" /> 40 </ControlTemplate> 41 </Setter.Value> 42 </Setter> 43 </Style> 44 45 <Style x:Key="Slider_Thumb" TargetType="Thumb"> 46 <Setter Property="Focusable" Value="false" /> 47 <Setter Property="Template"> 48 <Setter.Value> 49 <ControlTemplate TargetType="Thumb"> 50 <Ellipse Name="e" Width="15" Height="15" Fill="White" Stroke="Gray"/> 51 </ControlTemplate> 52 </Setter.Value> 53 </Setter> 54 </Style> 55 56 <Style TargetType="Slider"> 57 <Setter Property="Focusable" Value="false" /> 58 <Setter Property="Template"> 59 <Setter.Value> 60 <ControlTemplate TargetType="Slider"> 61 <Grid> 62 <Border BorderBrush="Red" BorderThickness="0" CornerRadius="0,0,0,0"> 63 <Track Name="PART_Track"> 64 <Track.DecreaseRepeatButton> 65 <RepeatButton Style="{StaticResource Slider_RepeatButton}" Command="Slider.DecreaseLarge"/> 66 </Track.DecreaseRepeatButton> 67 <Track.IncreaseRepeatButton> 68 <RepeatButton Style="{StaticResource Slider_RepeatButton1}" Command="Slider.IncreaseLarge"/> 69 </Track.IncreaseRepeatButton> 70 <Track.Thumb> 71 <Thumb Style="{StaticResource Slider_Thumb}"/> 72 </Track.Thumb> 73 </Track> 74 </Border> 75 </Grid> 76 </ControlTemplate> 77 </Setter.Value> 78 </Setter> 79 </Style> 80 81 82 <Style x:Key="Button_Close" TargetType="Button"> 83 <Setter Property="Template"> 84 <Setter.Value> 85 <ControlTemplate> 86 <Image x:Name="IMG" Source="E:\Test\WPFTest\Sources\Close.jpg"></Image> 87 </ControlTemplate> 88 </Setter.Value> 89 </Setter> 90 </Style> 91 92 <Style x:Key="Button_Forbidden" TargetType="Button"> 93 <Setter Property="Template"> 94 <Setter.Value> 95 <ControlTemplate> 96 <Image x:Name="IMG" Source="E:\Test\WPFTest\Sources\Forbidden.jpg"></Image> 97 </ControlTemplate> 98 </Setter.Value> 99 </Setter> 100 </Style> 101 102 103 <Style x:Key="Button_Left" TargetType="Button"> 104 <Setter Property="Template"> 105 <Setter.Value> 106 <ControlTemplate> 107 <Image x:Name="IMG" Source="E:\Test\WPFTest\Sources\Left.png" Stretch="Fill"></Image> 108 </ControlTemplate> 109 </Setter.Value> 110 </Setter> 111 </Style> 112 113 114 <Style x:Key="Button_Right" TargetType="Button"> 115 <Setter Property="Template"> 116 <Setter.Value> 117 <ControlTemplate> 118 <Image x:Name="IMG" Source="E:\Test\WPFTest\Sources\Right.png" Stretch="Fill"></Image> 119 </ControlTemplate> 120 </Setter.Value> 121 </Setter> 122 </Style> 123 124 </Window.Resources> 125 126 <Grid Name="Main_Grid"> 127 <Grid.RowDefinitions> 128 <RowDefinition Height="50"></RowDefinition> 129 <RowDefinition Height="500"></RowDefinition> 130 <RowDefinition Height="50"></RowDefinition> 131 </Grid.RowDefinitions> 132 133 <Border Name="title_Border" BorderBrush="#FBD3D0CD" BorderThickness="3" Grid.Row="0"> 134 <Grid Name="Title"> 135 <Grid.ColumnDefinitions> 136 <ColumnDefinition Width="200"></ColumnDefinition> 137 <ColumnDefinition Width="400"></ColumnDefinition> 138 <ColumnDefinition Width="120"></ColumnDefinition> 139 <ColumnDefinition Width="80"></ColumnDefinition> 140 </Grid.ColumnDefinitions> 141 142 <Grid Grid.Column="1"> 143 <Canvas x:Name="ParticleHost" Width="400" > 144 <TextBlock Name="txtB_Step" Grid.Column="1" Width="200" Height="30" TextAlignment="Center" FontSize="20" FontFamily="Microsoft YaHei" Text="asss" Margin="100,7,0,0"/> 145 </Canvas> 146 </Grid> 147 148 <Grid Name="grid_Cofig" Grid.Column="3"> 149 <Button Name="btn_Forbidden" Width="30" Click="Config_Click" Margin="2,10,48,12" HorizontalAlignment="Center" Focusable="False" Style="{StaticResource Button_Forbidden}" RenderTransformOrigin="0.5,0.5" ToolTipService.ToolTip="播放设置" ToolTipService.InitialShowDelay="1" ToolTipService.Placement="Bottom"> 150 <Button.RenderTransform> 151 <RotateTransform x:Name="trans_forbidden" Angle="0"/> 152 </Button.RenderTransform> 153 <Button.Triggers> 154 <EventTrigger RoutedEvent="Button.MouseEnter"> 155 <BeginStoryboard > 156 <Storyboard> 157 <DoubleAnimation From="0" To="90" Duration="0:0:0.4" 158 Storyboard.TargetName="trans_forbidden" 159 Storyboard.TargetProperty="Angle"/> 160 </Storyboard> 161 </BeginStoryboard> 162 </EventTrigger> 163 </Button.Triggers> 164 </Button> 165 </Grid> 166 167 <Popup x:Name="Pop" PopupAnimation="Slide" Placement="Bottom" Width="93" Height="58" PlacementTarget="{Binding ElementName=grid_Cofig}" AllowsTransparency="True" StaysOpen="False" IsOpen="False" > 168 <Border Background="White" BorderBrush="#FFC4C9CD" BorderThickness="2" Width="93"> 169 <StackPanel Margin="1"> 170 <StackPanel Name="sp_play" Orientation="Horizontal" Width="83" Height="22" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="2" MouseEnter="sp_play_enter" MouseLeave="sp_play_leave" > 171 <CheckBox x:Name="ch_play" VerticalAlignment="Center" Margin="3,4" /> 172 <TextBlock x:Name="txb_play" Text="不再播放" Margin="5,0" VerticalAlignment="Center"/> 173 </StackPanel> 174 <StackPanel Name="sp_doc" Width="83" Height="22" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="2" MouseEnter="sp_doc_enter" MouseLeave="sp_doc_leave"> 175 <TextBlock Name="txb_Doc" Text="官方帮助文档" Margin="4,0,5,0" VerticalAlignment="Center"> 176 </TextBlock> 177 </StackPanel> 178 </StackPanel> 179 </Border> 180 </Popup> 181 182 <Button Name="btn_Close" Grid.Column="3" Width="30" Click="Close_Click" Margin="37,10,13,12" HorizontalAlignment="Center" Focusable="False" Style="{StaticResource Button_Close}" RenderTransformOrigin="0.5,0.5" ToolTipService.ToolTip="关闭视频" ToolTipService.InitialShowDelay="1" ToolTipService.Placement="Bottom"> 183 <Button.RenderTransform> 184 <RotateTransform x:Name="trans" Angle="0"/> 185 </Button.RenderTransform> 186 <Button.Triggers> 187 <EventTrigger RoutedEvent="Button.MouseEnter"> 188 <BeginStoryboard > 189 <Storyboard> 190 <DoubleAnimation From="0" To="90" Duration="0:0:0.4" 191 Storyboard.TargetName="trans" 192 Storyboard.TargetProperty="Angle"/> 193 </Storyboard> 194 </BeginStoryboard> 195 </EventTrigger> 196 </Button.Triggers> 197 </Button> 198 199 </Grid> 200 </Border> 201 202 203 <Grid Name="Movie" Grid.Row="1"> 204 <MediaElement Stretch="Fill" LoadedBehavior="Manual" Name="QS_Movie" MediaOpened="Element_MediaOpened" Loaded="QS_Movie_Loaded" MouseLeftButtonDown="QS_Movie_MouseLeftButtonDown" /> 205 <Button Name="btn_pre" Width="30" Height="40" HorizontalAlignment="Left" VerticalAlignment="Center" Click="Left_Click" Focusable="False" Style="{StaticResource Button_Left}"/> 206 <Button Name="btn_next" Width="30" Height="40" HorizontalAlignment="Right" VerticalAlignment="Center" Click="Right_Click" Focusable="False" Style="{StaticResource Button_Right}"/> 207 </Grid> 208 209 210 <Border Name="Progress_Border" BorderBrush="#FBD3D0CD" BorderThickness="3" Grid.Row="2"> 211 <Grid Name="Control_Progress" > 212 <Slider Grid.Column="0" Width="700" Name="timelineSlider" VerticalAlignment="Center" PreviewMouseLeftButtonDown="timelineMDown" PreviewMouseLeftButtonUp="timelineMUp" BorderThickness="0,6,0,0" /> 213 </Grid> 214 </Border> 215 216 </Grid> 217 </Window>
后台:
1 using System.Windows.Media; 2 using System.Windows.Media.Effects; 3 using System.Windows.Shapes; 4 5 namespace WPF_Nav 6 { 7 public class Particle 8 { 9 public Point3D Position { get; set; } 10 public Point3D Velocity { get; set; } 11 public double Size { get; set; } 12 public Ellipse Ellipse { get; set; } 13 public BlurEffect Blur { get; set; } 14 public Brush Brush { get; set; } 15 } 16 }
1 namespace WPF_Nav 2 { 3 public class Point3D 4 { 5 public double X { get; set; } 6 public double Y { get; set; } 7 public double Z { get; set; } 8 9 public Point3D(double X, double Y, double Z) 10 { 11 this.X = X; 12 this.Y = Y; 13 this.Z = Z; 14 } 15 } 16 }
主体:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Windows; 7 using System.Windows.Controls; 8 using System.Windows.Controls.Primitives; 9 using System.Windows.Data; 10 using System.Windows.Documents; 11 using System.Windows.Input; 12 using System.Windows.Media; 13 using System.Windows.Media.Imaging; 14 using System.Windows.Navigation; 15 using System.Windows.Shapes; 16 using System.Windows.Threading; 17 using System.Windows.Media.Effects; 18 19 namespace WPF_Nav 20 { 21 /// <summary> 22 /// MainWindow.xaml 的交互逻辑 23 /// </summary> 24 public partial class MainWindow : Window 25 { 26 DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer(); // 定义一个DT 27 bool play_flag = true; //判断播放状态 28 int play_now = 0; //判断当前视频索引 29 int play_target; 30 List<string> Play_Video = new List<string>(); 31 List<string> Title_Video = new List<string>(); 32 33 34 35 public MainWindow() 36 { 37 InitializeComponent(); 38 Play_Video = LoadMovies(); 39 Title_Video = LoadTitles(); 40 } 41 42 private List<string> LoadTitles() 43 { 44 List<string> list_title = new List<string>(); 45 list_title.Add("Step1"); 46 list_title.Add("Step2"); 47 list_title.Add("Step3"); 48 return list_title; 49 } 50 private List<string> LoadMovies() 51 { 52 List<string> Movie_Uri = new List<string>(); 53 Movie_Uri.Add(@"E:\Test\WPFTest\Sources\preview.mp4"); 54 Movie_Uri.Add(@"E:\Test\WPFTest\Sources\preview1.mp4"); 55 Movie_Uri.Add(@"E:\Test\WPFTest\Sources\preview2.mp4"); 56 return Movie_Uri; 57 } 58 59 private void Play_Click(object sender, RoutedEventArgs e) 60 { 61 QS_Movie.Play(); 62 } 63 64 private void Pause_Click(object sender, RoutedEventArgs e) 65 { 66 QS_Movie.Pause(); 67 } 68 69 private void Element_MediaOpened(object sender, EventArgs e) 70 { 71 timelineSlider.Maximum = QS_Movie.NaturalDuration.TimeSpan.TotalMilliseconds; //设置slider最大值 72 dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick); //超过计时间隔时发生 73 dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 200); //DT间隔 74 dispatcherTimer.Start(); //DT启动 75 } 76 77 private void dispatcherTimer_Tick(object sender, EventArgs e) 78 { 79 int time = (int)QS_Movie.Position.TotalSeconds; 80 timelineSlider.ToolTip = SecToTime(time); 81 timelineSlider.Value = QS_Movie.Position.TotalMilliseconds; //slider滑动值随播放内容位置变化 82 } 83 84 private string SecToTime(int sec) 85 { 86 int min = sec / 60; 87 sec = sec - min * 60; 88 int hour = min / 60; 89 min = min - hour * 60; 90 string h = hour.ToString(); 91 string mm = ((min < 10) ? "0" : "") + min.ToString(); 92 string ss = ((sec < 10) ? "0" : "") + sec.ToString(); 93 string time = h + ":" + mm + ":" + ss; 94 return time; 95 } 96 private void timelineMDown(object sender, EventArgs e) 97 { 98 dispatcherTimer.Stop(); 99 } 100 private void timelineMUp(object sender, EventArgs e) 101 { 102 QS_Movie.Position = new TimeSpan(0, 0, 0, 0, (int)timelineSlider.Value); 103 dispatcherTimer.Start(); 104 QS_Movie.Play(); 105 } 106 107 108 109 private void QS_Movie_Loaded(object sender, RoutedEventArgs e) 110 { 111 PreLoad(400,play_now); 112 113 } 114 115 private void Left_Click(object sender, RoutedEventArgs e) 116 { 117 play_target = (play_now + Play_Video.Count-1) % Play_Video.Count; 118 PreLoad(200, play_target); 119 play_now = play_target; 120 } 121 122 private void Right_Click(object sender, RoutedEventArgs e) 123 { 124 play_target = (play_now + 1) % Play_Video.Count; 125 PreLoad(200, play_target); 126 play_now = play_target; 127 } 128 129 private void PreLoad(int interval, int index) 130 { 131 QS_Movie.Source = new Uri(Play_Video[index]); 132 QS_Movie.Play(); 133 System.Threading.Thread.Sleep(interval); 134 QS_Movie.Pause(); 135 txtB_Step.Text = Title_Video[index]; 136 } 137 138 139 140 141 142 private void QS_Movie_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 143 { 144 145 if(play_flag==true) 146 { 147 QS_Movie.Play(); 148 btn_pre.Visibility = System.Windows.Visibility.Hidden; 149 btn_next.Visibility = btn_pre.Visibility = System.Windows.Visibility.Hidden; 150 } 151 if(play_flag==false) 152 { 153 QS_Movie.Pause(); 154 btn_pre.Visibility = System.Windows.Visibility.Visible; 155 btn_next.Visibility = System.Windows.Visibility.Visible; 156 157 } 158 play_flag = !play_flag; 159 } 160 161 private void Close_Click(object sender, RoutedEventArgs e) 162 { 163 this.Close(); 164 } 165 166 private void Config_Click(object sender, RoutedEventArgs e) 167 { 168 this.Pop.IsOpen = true; 169 } 170 171 private void sp_play_enter(object sender, MouseEventArgs e) 172 { 173 Brush br_bg = new SolidColorBrush(Color.FromRgb(0, 162, 232)); 174 Brush br_white = new SolidColorBrush(Color.FromRgb(255, 255, 255)); 175 txb_play.Foreground = br_white; 176 sp_play.Background = br_bg; 177 } 178 179 private void sp_play_leave(object sender, MouseEventArgs e) 180 { 181 Brush br_white = new SolidColorBrush(Color.FromRgb(255, 255, 255)); 182 Brush br_black = new SolidColorBrush(Color.FromRgb(0, 0, 0)); 183 txb_play.Foreground = br_black; 184 sp_play.Background = br_white; 185 } 186 187 private void sp_doc_enter(object sender, MouseEventArgs e) 188 { 189 Brush br_bg = new SolidColorBrush(Color.FromRgb(0, 162, 232)); 190 Brush br_white = new SolidColorBrush(Color.FromRgb(255, 255, 255)); 191 txb_Doc.Foreground = br_white; 192 sp_doc.Background = br_bg; 193 } 194 195 private void sp_doc_leave(object sender, MouseEventArgs e) 196 { 197 Brush br_white = new SolidColorBrush(Color.FromRgb(255, 255, 255)); 198 Brush br_black = new SolidColorBrush(Color.FromRgb(0, 0, 0)); 199 txb_Doc.Foreground = br_black; 200 sp_doc.Background = br_white; 201 } 202 203 204 205 206 207 208 /// <summary> 209 /// 特效部分 210 /// </summary> 211 212 List<Particle> particles = new List<Particle>(); 213 List<Particle> deadList = new List<Particle>(); 214 Random random = new Random(); 215 216 217 private void Window_Loaded(object sender, RoutedEventArgs e) 218 { 219 timer.Interval = TimeSpan.FromMilliseconds(10); 220 timer.Tick += new EventHandler(timer_Tick); 221 timer.Start(); 222 } 223 224 void timer_Tick(object sender, EventArgs e) 225 { 226 UpdateParticules(); 227 } 228 229 DispatcherTimer timer = new DispatcherTimer(); 230 231 double elapsed = 0.1; 232 private void UpdateParticules() 233 { 234 //更新粒子信息 235 deadList.Clear(); 236 foreach (Particle p in this.particles) 237 { 238 if (p.Position.Y < -p.Size || p.Position.X < -p.Size || p.Position.X > Width + p.Size) 239 { 240 deadList.Add(p); 241 } 242 else 243 { 244 //更新位置 245 p.Position.X += p.Velocity.X * elapsed; 246 p.Position.Y += p.Velocity.Y * elapsed; 247 p.Position.Z += p.Velocity.Z * elapsed; 248 TranslateTransform t = (p.Ellipse.RenderTransform as TranslateTransform); 249 t.X = p.Position.X; 250 t.Y = p.Position.Y; 251 252 //更新颜色信息 253 p.Ellipse.Fill = p.Brush; 254 p.Ellipse.Effect = p.Blur; 255 } 256 } 257 258 //创建新的粒子 259 for (int i = 0; i < 10 && this.particles.Count < 40; i++) 260 { 261 //尝试循环使用已有例子 262 if (deadList.Count - 1 >= i) 263 { 264 SpawnParticle(deadList[i].Ellipse); 265 deadList[i].Ellipse = null; 266 } 267 else 268 { 269 SpawnParticle(null); 270 } 271 } 272 273 foreach (Particle p in deadList) 274 { 275 if (p.Ellipse != null) ParticleHost.Children.Remove(p.Ellipse); 276 this.particles.Remove(p); 277 } 278 } 279 280 private void SpawnParticle(Ellipse e) 281 { 282 double x = RandomWithVariance(Width / 2, Width / 2); 283 double y = Height; 284 double z = 10 * (random.NextDouble() * 100); 285 double speed = RandomWithVariance(10, 10); 286 double size = RandomWithVariance(10,10); 287 288 Particle p = new Particle(); 289 p.Position = new Point3D(x, y, z); 290 p.Size = size; 291 292 //模糊 293 var blur = new BlurEffect(); 294 blur.RenderingBias = RenderingBias.Performance; 295 blur.Radius = RandomWithVariance(5, 5); 296 p.Blur = blur; 297 //颜色 298 var brush = (Brush)Brushes.Lime.Clone(); 299 brush.Opacity = RandomWithVariance(0.6, 0.5); 300 p.Brush = brush; 301 302 TranslateTransform t; 303 304 if (e != null) 305 { 306 e.Fill = null; 307 e.Width = e.Height = size; 308 p.Ellipse = e; 309 310 t = e.RenderTransform as TranslateTransform; 311 } 312 else 313 { 314 p.Ellipse = new Ellipse(); 315 p.Ellipse.Width = p.Ellipse.Height = size; 316 this.ParticleHost.Children.Add(p.Ellipse); 317 318 t = new TranslateTransform(); 319 p.Ellipse.RenderTransform = t; 320 p.Ellipse.RenderTransformOrigin = new Point(0.5, 0.5); 321 322 } 323 324 t.X = p.Position.X; 325 t.Y = p.Position.Y; 326 327 double velocityMultiplier = (random.NextDouble() + 0.25) * speed; 328 double vX = (1.0 - (random.NextDouble() * 2.0)) * velocityMultiplier; 329 double vY = -Math.Abs((1.0 - (random.NextDouble() * 2.0)) * velocityMultiplier); 330 331 p.Velocity = new Point3D(vX, vY, 0); 332 333 this.particles.Add(p); 334 } 335 336 private double RandomWithVariance(double midvalue, double variance) 337 { 338 double min = Math.Max(midvalue - (variance / 2), 0); 339 double max = midvalue + (variance / 2); 340 double value = min + ((max - min) * random.NextDouble()); 341 return value; 342 } 343 344 } 345 346 347 348 }
三、效果
四、小结
WPF挺好玩的!前台就能解决一些基本的动画效果,加上后台简直爽。最后,感谢单位的老司机们!