带有形状的 WPF 剪辑
Posted
技术标签:
【中文标题】带有形状的 WPF 剪辑【英文标题】:WPF Clipping with a shape 【发布时间】:2013-04-05 16:00:12 【问题描述】:我正在尝试以用户控件的形式创建 3..2..1 倒计时。像this 这样的东西。我的想法是在彼此的顶部创建两个矩形,一个浅色和一个深色,并有一个径向圆作为深色矩形的剪裁器。径向圆会为 Angle 属性设置动画,因此它会转身。
我找到了一个径向圆的实现,并将矩形的 Clip 属性绑定到我的圆的 RenderedGeometry 属性上。结果如下:
红色的笔划是我的理发器的形状。这似乎是剪辑的一种奇怪行为,但我有点理解,但我想知道是否有办法绕过 事实上,我的剪辑对象似乎以一种奇怪的方式使用了 RenderedGeometry。
编辑1:我要找的效果http://www.youtube.com/watch?v=9FPHTo5V2BQ
【问题讨论】:
这似乎有点过于复杂了,你到底想要什么效果? 类似这样的东西:youtube.com/watch?v=9FPHTo5V2BQ。是的,我有时会做过于复杂的事情,但这是我研究过程的一部分。我还没有找到一个简单的方法。 我得稍后再看看,不幸的是他们在办公室为我们限制了 youtube :( 您能否发布 XAML 代码和任何相关的代码隐藏(而不仅仅是屏幕截图)? 【参考方案1】:如下所示的简单派生形状控件绘制倒计时矩形。您必须设置其Fill
(可能还有Stroke
)、Width
、Height
和Angle
属性,并且可以将Angle
的动画从0 设置为360。
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);
【讨论】:
【参考方案2】:您可以在剪辑PathGeometry
中使用ArcSegment
剪辑矩形,并为ArcSegment
的端点(Point
) 设置动画。
可以使用PointAnimationUsingPath
动画为端点设置动画,使用相同的ArcSegment
作为其路径。以下是基于 Charlez Petzold 的出色回答的建议:Drawing pie slices
<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>
【讨论】:
以上是关于带有形状的 WPF 剪辑的主要内容,如果未能解决你的问题,请参考以下文章