在Button动画的路径上绘制轨迹

时间:2019-01-18 16:20:03

标签: c# wpf vb.net xaml animation

我从这里获得了以下代码:https://docs.microsoft.com/en-us/dotnet/framework/wpf/graphics-multimedia/animate-an-object-along-a-path-matrix-animation-with-offset

<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Canvas Width="400" Height="400">
    <Button MinWidth="100" Content="A Button">
        <Button.RenderTransform>
            <MatrixTransform x:Name="myMatrixTransform">
                <MatrixTransform.Matrix >
                    <Matrix />
                </MatrixTransform.Matrix>
            </MatrixTransform>
        </Button.RenderTransform>
        <Button.Triggers>
            <EventTrigger RoutedEvent="Button.Click">
                <BeginStoryboard>
                    <Storyboard>
                        <MatrixAnimationUsingPath Storyboard.TargetName="myMatrixTransform" Storyboard.TargetProperty="Matrix" IsOffsetCumulative="True" Duration="0:0:5" RepeatBehavior="2x">
                            <MatrixAnimationUsingPath.PathGeometry>
                                <PathGeometry Figures="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100"/>
                            </MatrixAnimationUsingPath.PathGeometry>
                        </MatrixAnimationUsingPath>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Button.Triggers>
    </Button>
</Canvas>
</Window>

如何在Button动画的路径上绘制轨迹?

之前曾问过类似的问题,但没有答案。

Painting a trail over a Path in WPF

附言: xaml或背后的代码(C#或VB)对我来说都是可以接受的。

1 个答案:

答案 0 :(得分:0)

首先,我对WPF中的这类内容不太熟悉。
不过,我试图为您的问题找到解决方案,并得出以下结果。
我估算了通过许多单个点的真实路径,以便可以使用这些点绘制路径的特定部分。
注意:这远非完美,只是一个快速解决方案。

public class PortionPath : Shape
{
    #region Geometry

    private static readonly DependencyProperty GeometryProperty =
        DependencyProperty.Register("Geometry", typeof(Geometry), typeof(PortionPath),
            new PropertyMetadata(Geometry.Empty, null, CoerceGeometry));
    private Geometry Geometry => (Geometry)GetValue(GeometryProperty);
    private static object CoerceGeometry(DependencyObject d, object baseValue)
    {
        PortionPath portionPath = (PortionPath) d;
        int firstPoint = (int)(portionPath.PointCollection.Length * portionPath.From);
        int lastPoint = (int)(portionPath.PointCollection.Length * portionPath.To);
        if (firstPoint == 0 && lastPoint == portionPath.PointCollection.Length)
        {
            return portionPath.Data;
        }
        if (firstPoint == lastPoint)
        {
            return Geometry.Empty;
        }
        PathFigure pathFigure = new PathFigure();
        var pointCollection = portionPath.PointCollection.Skip(firstPoint).Take(lastPoint - firstPoint).ToArray();
        if (pointCollection.Length > 0)
        {
            pathFigure.StartPoint = pointCollection[0];
            if (pointCollection.Length > 1)
            {
                Point[] array = new Point[pointCollection.Length - 1];
                for (int i = 1; i < pointCollection.Length; i++)
                {
                    array[i - 1] = pointCollection[i];
                }
                pathFigure.Segments.Add(new PolyLineSegment(array, true));
            }
        }
        PathGeometry polylineGeometry = new PathGeometry();
        polylineGeometry.Figures.Add(pathFigure);
        return polylineGeometry;
    }

    #endregion

    #region PointCollection

    private static readonly DependencyProperty PointCollectionProperty =
        DependencyProperty.Register("PointCollection", typeof(Point[]), typeof(PortionPath),
            new PropertyMetadata(new Point[0], PointCollectionChanged, CoercePointCollection));
    private Point[] PointCollection => (Point[]) GetValue(PointCollectionProperty);
    private static void PointCollectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        d.CoerceValue(GeometryProperty);
    }
    private static object CoercePointCollection(DependencyObject d, object baseValue)
    {
        PortionPath portionPath = (PortionPath) d;
        var path = portionPath.Data;
        var steps = portionPath.Steps;
        var g = path.GetFlattenedPathGeometry(portionPath.Tolerance, ToleranceType.Absolute);
        List<Point> pointCollection = new List<Point>();
        var step = 1.0 / steps;
        for (double i = 0; i <= 1; i += step)
        {
            g.GetPointAtFractionLength(i, out Point p, out _);
            pointCollection.Add(p);
        }
        return pointCollection.ToArray();
    }

    #endregion

    #region Tolerance

    private static readonly DependencyProperty ToleranceProperty =
        DependencyProperty.Register("Tolerance", typeof(double), typeof(PortionPath),
            new PropertyMetadata(0.000001, ToleranceChanged));
    public double Tolerance
    {
        get => (double) GetValue(ToleranceProperty);
        set => SetValue(ToleranceProperty,value);
    }
    private static void ToleranceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        d.CoerceValue(PointCollectionProperty);
    }

    #endregion

    #region Data

    public static DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(Geometry),
        typeof(PortionPath), new FrameworkPropertyMetadata(Geometry.Empty, FrameworkPropertyMetadataOptions.AffectsRender, DataChanged));
    public Geometry Data
    {
        get => (Geometry)GetValue(DataProperty);
        set => SetValue(DataProperty, value);
    }
    private static void DataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        d.CoerceValue(PointCollectionProperty);
    }

    #endregion

    #region Steps

    public static DependencyProperty StepsProperty = DependencyProperty.Register("Steps", typeof(int),
        typeof(PortionPath), new FrameworkPropertyMetadata(1000, FrameworkPropertyMetadataOptions.AffectsRender, StepsChanged));
    public int Steps
    {
        get => (int)GetValue(StepsProperty);
        set => SetValue(StepsProperty, value);
    }
    private static void StepsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        d.CoerceValue(PointCollectionProperty);
    }

    #endregion

    #region From

    public static DependencyProperty FromProperty = DependencyProperty.Register("From", typeof(double),
        typeof(PortionPath), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender, FromChanged, CoerceFrom));
    public double From
    {
        get => (double)GetValue(FromProperty);
        set => SetValue(FromProperty, value);
    }
    private static void FromChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        d.CoerceValue(ToProperty);
        d.CoerceValue(GeometryProperty);
    }
    private static object CoerceFrom(DependencyObject d, object baseValue)
    {
        switch (baseValue)
        {
            case double doubleValue:
                if (doubleValue > 1.0) return 1.0;
                if (doubleValue < 0.0) return 0.0;
                return doubleValue;
            default:
                return 1.0;
        }
    }

    #endregion

    #region To

    public static DependencyProperty ToProperty = DependencyProperty.Register("To", typeof(double),
        typeof(PortionPath), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender, ToChanged, CoerceTo));
    public double To
    {
        get => (double) GetValue(ToProperty);
        set => SetValue(ToProperty,value);
    }
    private static void ToChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        d.CoerceValue(GeometryProperty);
    }
    private static object CoerceTo(DependencyObject d, object baseValue)
    {
        PortionPath portionPath = (PortionPath) d;
        switch (baseValue)
        {
            case double doubleValue:
                if (doubleValue > 1.0) return 1.0;
                if (doubleValue < 0.0 || doubleValue < portionPath.From) return portionPath.From;
                return doubleValue;
            default:
                return 1.0;
        }
    }

    #endregion

    protected override Geometry DefiningGeometry => Geometry;
}
<Canvas Width="400" Height="400">
    <Button MinWidth="100" Content="A Button">
        <Button.RenderTransform>
            <MatrixTransform x:Name="myMatrixTransform">
                <MatrixTransform.Matrix >
                    <Matrix />
                </MatrixTransform.Matrix>
            </MatrixTransform>
        </Button.RenderTransform>
        <Button.Triggers>
            <EventTrigger RoutedEvent="Button.Click">
                <BeginStoryboard>
                    <Storyboard>
                        <MatrixAnimationUsingPath Storyboard.TargetName="myMatrixTransform" Storyboard.TargetProperty="Matrix" IsOffsetCumulative="True" Duration="0:0:5">
                            <MatrixAnimationUsingPath.PathGeometry>
                                <PathGeometry Figures="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100"/>
                            </MatrixAnimationUsingPath.PathGeometry>
                        </MatrixAnimationUsingPath>
                        <DoubleAnimation From="0" To="1" Storyboard.TargetName="PortionPath" Storyboard.TargetProperty="To" Duration="0:0:5"/>
                        <DoubleAnimation From="0" To="1" Storyboard.TargetName="PortionPath" Storyboard.TargetProperty="From" BeginTime="0:0:1" Duration="0:0:5"/>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Button.Triggers>
    </Button>
    <local:PortionPath x:Name="PortionPath" From="0" To="0" Stroke="Red" Data="M 10,100 C 35,0 135,0 160,100 180,190 285,200 310,100" />
</Canvas>

欢迎提出任何改进此解决方案的建议