WPF路径动画(动态逆向动画)

简介: 原文:WPF路径动画(动态逆向动画)WPF 中的Path.Data 不再多介绍,M开始坐标点 C弧度坐标点 L 直线坐标点   个人写了关于Path.Data数据反向,意思就是把Path的数据逆转,但是图形是没有变化的 Xaml代码如下: ...
原文: WPF路径动画(动态逆向动画)

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>
View Code

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);
        }
    }
}
View Code

执行效果如下:

 

写这个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;

        }
View Code

 

如果大家在使用过程中还有发现算法bug,请在下方评论并把data贴出来。粘贴格式如下:

<Path x:Name="path0" Data="M 96 288 C 576 0, 0 0, 480 288" 
Stretch="None" Stroke="#FF61E70A" StrokeThickness="2" />

 

 源代码下载

目录
相关文章
|
3月前
|
C#
WPF中动画教程(DoubleAnimation的基本使用)
WPF中动画教程(DoubleAnimation的基本使用)
69 0
|
2月前
|
算法 C# Windows
不可不知的WPF动画(Animation)
【9月更文挑战第19天】在 WPF(Windows Presentation Foundation)中,动画能为应用程序增添生动性和交互性。主要类型包括线性动画和关键帧动画,可针对依赖属性和自定义属性操作。触发方式有事件触发和自动触发,支持暂停、恢复及停止控制。合理使用这些功能并注意性能优化,可创建引人入胜的用户界面。
|
3月前
|
C# UED 开发者
WPF与性能优化:掌握这些核心技巧,让你的应用从卡顿到丝滑,彻底告别延迟,实现响应速度质的飞跃——从布局到动画全面剖析与实例演示
【8月更文挑战第31天】本文通过对比优化前后的方法,详细探讨了提升WPF应用响应速度的策略。文章首先分析了常见的性能瓶颈,如复杂的XAML布局、耗时的事件处理、不当的数据绑定及繁重的动画效果。接着,通过具体示例展示了如何简化XAML结构、使用后台线程处理事件、调整数据绑定设置以及利用DirectX优化动画,从而有效提升应用性能。通过这些优化措施,WPF应用将更加流畅,用户体验也将得到显著改善。
223 1
|
3月前
|
区块链 C# 存储
链动未来:WPF与区块链的创新融合——从智能合约到去中心化应用,全方位解析开发安全可靠DApp的最佳路径
【8月更文挑战第31天】本文以问答形式详细介绍了区块链技术的特点及其在Windows Presentation Foundation(WPF)中的集成方法。通过示例代码展示了如何选择合适的区块链平台、创建智能合约,并在WPF应用中与其交互,实现安全可靠的消息存储和检索功能。希望这能为WPF开发者提供区块链技术应用的参考与灵感。
62 0
|
3月前
|
开发者 C# Windows
WPF与游戏开发:当桌面应用遇见游戏梦想——利用Windows Presentation Foundation打造属于你的2D游戏世界,从环境搭建到代码实践全面解析新兴开发路径
【8月更文挑战第31天】随着游戏开发技术的进步,WPF作为.NET Framework的一部分,凭借其图形渲染能力和灵活的UI设计,成为桌面游戏开发的新选择。本文通过技术综述和示例代码,介绍如何利用WPF进行游戏开发。首先确保安装最新版Visual Studio并创建WPF项目。接着,通过XAML设计游戏界面,并在C#中实现游戏逻辑,如玩家控制和障碍物碰撞检测。示例展示了创建基本2D游戏的过程,包括角色移动和碰撞处理。通过本文,WPF开发者可更好地理解并应用游戏开发技术,创造吸引人的桌面游戏。
183 0
|
3月前
|
C# UED 开发者
WPF动画大揭秘:掌握动画技巧,让你的界面动起来,告别枯燥与乏味!
【8月更文挑战第31天】在WPF应用开发中,动画能显著提升用户体验,使其更加生动有趣。本文将介绍WPF动画的基础知识和实现方法,包括平移、缩放、旋转等常见类型,并通过示例代码展示如何使用`DoubleAnimation`创建平移动画。此外,还将介绍动画触发器的使用,帮助开发者更好地控制动画效果,提升应用的吸引力。
174 0
|
3月前
|
存储 C# 数据格式
WPF动画教程(PointAnimationUsingPath的使用)
WPF动画教程(PointAnimationUsingPath的使用)
48 0
|
6月前
|
C#
WPF —— 动画缩放变换
`ScaleTransform`用于二维x-y坐标系中对象的缩放,可沿X或Y轴调整。在故事板中,通过RenderTransform.ScaleX和ScaleY属性控制缩放。示例代码展示了如何设置按钮的RenderTransformOrigin、Background等属性,并通过LayoutTransform应用ScaleTransform。当鼠标进入按钮时,EventTrigger启动DoubleAnimation实现X和Y轴的缩放动画。最后,展示了如何将动画集成到自定义按钮样式中。
175 0
|
C#
WPF技术之动画系列-上下运动
本例子展现动画小球上下循环运动
220 0
|
移动开发 开发框架 网络协议
WPF+ASP.NET SignalR实现动态折线图
WPF+ASP.NET SignalR实现动态折线图
137 0