2

我正在尝试以用户控件的形式创建 3..2..1 倒计时。像这样的东西。我的想法是在彼此的顶部创建两个矩形,一个浅色和一个深色,并有一个径向圆作为深色矩形的剪裁器。径向圆将动画角度属性,因此它会转身。

我找到了一个径向圆的实现,并将矩形的 Clip 属性绑定在我的圆的 RenderedGeometry 属性上。这是结果:

问题截图

红色的笔划是我的理发器的形状。这似乎是剪裁的一种奇怪行为,但我有点理解它,但我想知道是否有办法解决我的剪裁对象似乎以一种奇怪的方式使用 RenderedGeometry 的事实。

编辑 1:我正在寻找的效果http://www.youtube.com/watch?v=9FPHTo5V2BQ

4

2 回答 2

3

下面显示的简单派生形状控件绘制倒计时矩形。您必须设置它的Fill(也许还有Stroke)、WidthHeight属性Angle,并且您可以在 0 到 360 之间设置动画Angle

public class CountdownRect : Shape
{
    static CountdownRect()
    {
        WidthProperty.OverrideMetadata(typeof(CountdownRect),
            new FrameworkPropertyMetadata((o, e) => ((CountdownRect)o).UpdateGeometry()));

        HeightProperty.OverrideMetadata(typeof(CountdownRect),
            new FrameworkPropertyMetadata((o, e) => ((CountdownRect)o).UpdateGeometry()));

        StrokeLineJoinProperty.OverrideMetadata(typeof(CountdownRect),
            new FrameworkPropertyMetadata(PenLineJoin.Round));
    }

    public static readonly DependencyProperty AngleProperty =
        DependencyProperty.Register("Angle", typeof(double), typeof(CountdownRect),
            new FrameworkPropertyMetadata((o, e) => ((CountdownRect)o).UpdateGeometry()));

    public double Angle
    {
        get { return (double)GetValue(AngleProperty); }
        set { SetValue(AngleProperty, value); }
    }

    private readonly StreamGeometry geometry = new StreamGeometry();

    protected override Geometry DefiningGeometry
    {
        get { return geometry; }
    }

    private void UpdateGeometry()
    {
        if (!double.IsNaN(Width) && !double.IsNaN(Height))
        {
            var angle = ((Angle % 360d) + 360d) % 360d;
            var margin = StrokeThickness / 2d;
            var p0 = new Point(margin, margin);
            var p1 = new Point(Width - margin, margin);
            var p2 = new Point(Width - margin, Height - margin);
            var p3 = new Point(margin, Height - margin);

            using (var context = geometry.Open())
            {
                if (angle == 0d)
                {
                    context.BeginFigure(p0, true, true);
                    context.LineTo(p1, true, false);
                    context.LineTo(p2, true, false);
                    context.LineTo(p3, true, false);
                }
                else
                {
                    var x = p2.X / 2d;
                    var y = p2.Y / 2d;
                    var a = Math.Atan2(x, y) / Math.PI * 180d;
                    var t = Math.Tan(angle * Math.PI / 180d);

                    context.BeginFigure(new Point(x, y), true, true);

                    if (angle < a)
                    {
                        context.LineTo(new Point(x + y * t, p0.Y), true, false);
                        context.LineTo(p1, true, false);
                        context.LineTo(p2, true, false);
                        context.LineTo(p3, true, false);
                        context.LineTo(p0, true, false);
                    }
                    else if (angle < 180d - a)
                    {
                        context.LineTo(new Point(p2.X, y - x / t), true, false);
                        context.LineTo(p2, true, false);
                        context.LineTo(p3, true, false);
                        context.LineTo(p0, true, false);
                    }
                    else if (angle < 180d + a)
                    {
                        context.LineTo(new Point(x - y * t, p2.Y), true, false);
                        context.LineTo(p3, true, false);
                        context.LineTo(p0, true, false);
                    }
                    else if (angle < 360d - a)
                    {
                        context.LineTo(new Point(p0.X, y + x / t), true, false);
                        context.LineTo(p0, true, false);
                    }
                    else
                    {
                        context.LineTo(new Point(x + y * t, p0.Y), true, false);
                    }

                    context.LineTo(new Point(x, p0.Y), true, false);
                }
            }
        }
    }
}
于 2013-04-05T19:06:43.530 回答
3

您可以使用剪辑ArcSegment中的 来剪辑矩形,并为其端点 ( )PathGeometry设置动画。ArcSegmentPoint

端点可以通过动画进行PointAnimationUsingPath动画处理,使用与其路径相同ArcSegment的路径。以下是基于 Charlez Petzold 在此处的出色回答的建议:绘制馅饼片

<UserControl ... >

    <UserControl.Resources>
        <Point x:Key="SweepCenter" X="100" Y="100" />
        <Size x:Key="SweepRadius" Width="130" Height="130" />

        <!-- Start sweeping at twelve o'clock.. -->
        <Point x:Key="SweepStart" X="100" Y="-30" />
        <!-- ..and keep sweeping clockwise until we're (almost) back at the start point: -->
        <Point x:Key="SweepEnd" X="99.99" Y="-30" />

        <Storyboard x:Key="Sweeper" RepeatBehavior="Forever" AutoReverse="False" >

            <PointAnimationUsingPath Storyboard.TargetName="arc"
                                     Storyboard.TargetProperty="Point"
                                     Duration="0:0:5">
                <PointAnimationUsingPath.PathGeometry>
                    <PathGeometry>
                        <PathFigure StartPoint="{StaticResource SweepStart}">
                            <ArcSegment Size="{StaticResource SweepRadius}" 
                                        Point="{StaticResource SweepEnd}"
                                        SweepDirection="Clockwise"
                                        IsLargeArc="True" />
                        </PathFigure>
                    </PathGeometry>
                </PointAnimationUsingPath.PathGeometry>
            </PointAnimationUsingPath>

            <BooleanAnimationUsingKeyFrames Storyboard.TargetName="arc"
                                            Storyboard.TargetProperty="IsLargeArc" >
                <DiscreteBooleanKeyFrame KeyTime="0:0:2.5" Value="True" />
                <DiscreteBooleanKeyFrame KeyTime="0:0:5" Value="False" />
            </BooleanAnimationUsingKeyFrames>
        </Storyboard>

    </UserControl.Resources>

    <Grid Width="200" Height="200" >
        <Rectangle Fill="Black" />
        <Rectangle Fill="Gray" >
            <Rectangle.Triggers>
                <EventTrigger RoutedEvent="Loaded">
                    <BeginStoryboard Storyboard="{StaticResource Sweeper}" />
                </EventTrigger>
            </Rectangle.Triggers>
            <Rectangle.Clip>
                <PathGeometry>
                    <PathFigure StartPoint="{StaticResource SweepCenter}" 
                                IsClosed="True" >
                        <LineSegment Point="{StaticResource SweepStart}" />
                        <ArcSegment x:Name="arc"
                                    Size="{StaticResource SweepRadius}"
                                    Point="{StaticResource SweepStart}"
                                    SweepDirection="Clockwise" />
                    </PathFigure>
                </PathGeometry>
            </Rectangle.Clip>
        </Rectangle>
    </Grid>

</UserControl>
于 2013-04-05T20:43:32.347 回答