WPF 中的Path.Data 不再多介紹,M開始坐標點 C弧度坐標點 L 直線坐標點
<Path x:Name="path0" Data="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100" Height="135.32" Stretch="Uniform" Stroke="#FF61E70A" StrokeThickness="2" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Path x:Name="path0" Data="M95,50 L324.67997,50 324.67997,119.67997 234.67998,119.67997 234.67998,184.68002 344.67999,184.68002 394.68,134.67999 C394.68,134.67999 394.68,189.68005 394.68,129.68002 394.68,69.679984 434.68002,89.679985 434.68002,89.679985 L477.18005,132.18003 477.18005,164.68004 419.68006,164.6800" Height="135.32" Stretch="Uniform" Stroke="#FF61E70A" StrokeThickness="2" HorizontalAlignment="Center" VerticalAlignment="Center" />
個人寫了關於Path.Data數據反向,意思就是把Path的數據逆轉,但是圖形是沒有變化的
Xaml代碼如下:

<Window x:Class="WPFPathReverse.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPFPathReverse" mc:Ignorable="d" Title="MainWindow" Height="600" Width="500"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50"></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <Button Content="正向動畫" Width="80" Margin="5" Click="btnPositive_Click"></Button> <Button Content="反向動畫" Width="80" Margin="5" Click="btnRevPositive_Click"></Button> </StackPanel> <Canvas Grid.Row="1" > <Path x:Name="path0" Data="M1,1 L230.67997,1 230.67997,70.67997 140.67998,70.67997 140.67998,135.68002 300.68,85.67999 C300.68,85.67999 300.68,140.68005 300.68,80.68002 300.68,20.679984 340.68002,40.679985 340.68002,40.679985 L383.18005,83.18003 383.18005,115.68004 325.68006,115.68" Height="136.68" Stretch="None" Stroke="#FF61E70A" StrokeThickness="2" Width="384.18" /> </Canvas> <Canvas x:Name="canvas" Grid.Row="2"></Canvas> </Grid> </Window>
Code代碼如下

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WPFPathReverse { /// <summary> /// MainWindow.xaml 的交互邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.Loaded += MainWindow_Loaded; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { string data = this.path0.Data.ToString(); var result = ConvertReverseData(data); Path newpath = new Path(); newpath.Data = PathGeometry.CreateFromGeometry(Geometry.Parse(result)); newpath.HorizontalAlignment = HorizontalAlignment.Center; newpath.VerticalAlignment = VerticalAlignment.Center; newpath.Stretch = this.path0.Stretch; newpath.Stroke = new SolidColorBrush(Colors.Red); newpath.StrokeThickness = 2; newpath.Width = this.path0.Width; newpath.Height = this.path0.Height; canvas.Children.Add(newpath); } /// <summary> /// 反向Data數據 /// </summary> /// <param name="data"></param> /// <returns></returns> string ConvertReverseData(string data) { data = data.Replace("M", "").Replace(" ", "/"); Regex regex = new Regex("[a-z]", RegexOptions.IgnoreCase); MatchCollection mc = regex.Matches(data); //item1 從上一個位置到當前位置開始的字符 (match.Index=原始字符串中發現捕獲的子字符串的第一個字符的位置。) //item2 當前發現的匹配符號(L C Z M) List<Tuple<string, string>> tmpList = new List<Tuple<string, string>>(); int curPostion = 0; for (int i = 0; i < mc.Count; i++) { Match match = mc[i]; if (match.Index != curPostion) { string str = data.Substring(curPostion, match.Index - curPostion); tmpList.Add(new Tuple<string, string>(str, match.Value)); } curPostion = match.Index + match.Length; if (i + 1 == mc.Count)//last { tmpList.Add(new Tuple<string, string>(data.Substring(curPostion), match.Value)); } } //char[] spChar = new char[2] { 'C', 'L' }; //var tmpList = data.Split(spChar); List<string[]> spList = new List<string[]>(); for (int i = 0; i < tmpList.Count; i++) { var cList = tmpList[i].Item1.Split('/'); spList.Add(cList); } List<string> strList = new List<string>(); for (int i = spList.Count - 1; i >= 0; i--) { string[] clist = spList[i]; for (int j = clist.Length - 1; j >= 0; j--) { if (j == clist.Length - 2)//對於第二個元素增加 L或者C的標識 { var pointWord = tmpList[i - 1].Item2;//獲取標識 strList.Add(pointWord + clist[j]); } else { strList.Add(clist[j]); } } } string reverseData = "M" + string.Join(" ", strList); return reverseData; } private void btnPositive_Click(object sender, RoutedEventArgs e) { MatrixStory(0, this.path0.Data.ToString()); } private void btnRevPositive_Click(object sender, RoutedEventArgs e) { string data = this.path0.Data.ToString(); var result = ConvertReverseData(data); MatrixStory(1, result); } /// <summary> /// /// </summary> /// <param name="orientation">0正向 1反向</param> /// <param name="data">路徑數據</param> private void MatrixStory(int orientation, string data) { Border border = new Border(); border.Width = 10; border.Height = 10; border.Visibility = Visibility.Collapsed; if (orientation==0) { border.Background = new SolidColorBrush(Colors.Blue); } else { border.Background = new SolidColorBrush(Colors.Green); } this.canvas.Children.Add(border); Canvas.SetLeft(border, -border.Width / 2); Canvas.SetTop(border, -border.Height / 2); border.RenderTransformOrigin = new Point(0.5, 0.5); MatrixTransform matrix = new MatrixTransform(); TransformGroup groups = new TransformGroup(); groups.Children.Add(matrix); border.RenderTransform = groups; //NameScope.SetNameScope(this, new NameScope()); string registname = "matrix" + Guid.NewGuid().ToString().Replace("-", ""); this.RegisterName(registname, matrix); MatrixAnimationUsingPath matrixAnimation = new MatrixAnimationUsingPath(); matrixAnimation.PathGeometry = PathGeometry.CreateFromGeometry(Geometry.Parse(data)); matrixAnimation.Duration = new Duration(TimeSpan.FromSeconds(5)); matrixAnimation.DoesRotateWithTangent = true;//旋轉 //matrixAnimation.FillBehavior = FillBehavior.Stop; Storyboard story = new Storyboard(); story.Children.Add(matrixAnimation); Storyboard.SetTargetName(matrixAnimation, registname); Storyboard.SetTargetProperty(matrixAnimation, new PropertyPath(MatrixTransform.MatrixProperty)); #region 控制顯示與隱藏 ObjectAnimationUsingKeyFrames ObjectAnimation = new ObjectAnimationUsingKeyFrames(); ObjectAnimation.Duration = matrixAnimation.Duration; DiscreteObjectKeyFrame kf1 = new DiscreteObjectKeyFrame(Visibility.Visible, TimeSpan.FromMilliseconds(1)); ObjectAnimation.KeyFrames.Add(kf1); story.Children.Add(ObjectAnimation); //Storyboard.SetTargetName(border, border.Name); Storyboard.SetTargetProperty(ObjectAnimation, new PropertyPath(UIElement.VisibilityProperty)); #endregion story.FillBehavior = FillBehavior.Stop; story.Begin(border, true); } } }
執行效果如下:
寫這個Path反轉的目的是動態生成動畫的時候,可以逆向執行動畫,而不必為逆向動畫重新畫一個Path.
上面代碼中反轉Path有bug(各種不同的Path數據格式,下面是修復后的代碼)

string ConvertReverseData(string data) { data = data.Replace("M", ""); Regex regex = new Regex("[a-z]", RegexOptions.IgnoreCase); MatchCollection mc = regex.Matches(data); //item1 從上一個位置到當前位置開始的字符 (match.Index=原始字符串中發現捕獲的子字符串的第一個字符的位置。) //item2 當前發現的匹配符號(L C Z M) List<Tuple<string, string>> tmpList = new List<Tuple<string, string>>(); int curPostion = 0; for (int i = 0; i < mc.Count; i++) { Match match = mc[i]; if (match.Index != curPostion) { string str = data.Substring(curPostion, match.Index - curPostion); tmpList.Add(new Tuple<string, string>(str, match.Value)); } curPostion = match.Index + match.Length; if (i + 1 == mc.Count)//last { tmpList.Add(new Tuple<string, string>(data.Substring(curPostion), match.Value)); } } List<string[]> spList = new List<string[]>(); Regex regexnum = new Regex(@"(\-?\d+\.?\d*)", RegexOptions.IgnoreCase); for (int i = 0; i < tmpList.Count; i++) { //處理坐標數據 ex M 96 288 C 576 0, 0 0, 480 288 //ex M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100 //ex M95,50 L324.67997,50 324.67997,119.67997 234.67998,119.67997 234.67998,184.68002 //344.67999,184.68002 394.68,134.67999 C394.68,134.67999 394.68,189.68005 //394.68,129.68002 394.68,69.679984 434.68002,89.679985 434.68002,89.679985 // L477.18005,132.18003 477.18005,164.68004 419.68006,164.6800 MatchCollection childMcs = regexnum.Matches(tmpList[i].Item1); if (childMcs.Count % 2 != 0) { //分組數據有問題 continue; } int groups = childMcs.Count / 2; var strTmp = new string[groups]; for (int j = 0; j < groups; j++) { string cdatas = childMcs[j * 2] + "," + childMcs[j * 2 + 1];//重組數據 strTmp[j] = cdatas; } spList.Add(strTmp); } #region 逆向數據 List<string> strList = new List<string>(); for (int i = spList.Count - 1; i >= 0; i--) { string[] clist = spList[i]; for (int j = clist.Length - 1; j >= 0; j--) { if (j == clist.Length - 2 && i > 0)//對於第二個元素增加 L或者C的標識 { var pointWord = tmpList[i - 1].Item2;//獲取標識 strList.Add(pointWord + clist[j]); } else { strList.Add(clist[j]); //M10,50 L44.679973,69.679973 C43.627604,76.057983 43.410881,76.928271 41.082803,81.687898 if (clist.Length == 1 && i > 0)//說明只有一個元素 ex L44.679973,69.679973 { strList.Add(tmpList[i - 1].Item2); } } } } string reverseData = "M" + string.Join(" ", strList); #endregion return reverseData; }
如果大家在使用過程中還有發現算法bug,請在下方評論並把data貼出來。粘貼格式如下:
<Path x:Name="path0" Data="M 96 288 C 576 0, 0 0, 480 288" Stretch="None" Stroke="#FF61E70A" StrokeThickness="2" />