动态创建对象与动画的高级技巧

在进行图形界面编程时,经常需要对多个图形对象进行精确的布局和复杂的动画控制。然而,现有的图形框架如Silverlight和WPF并没有提供直接的方法来实现这些需求。本文将展示如何使用Lambda表达式和高阶函数来动态地创建对象和动画。

首先,来定义一个简单的类,用于存储一组圆形对象。这个类将继承自集合类,并添加一个构造函数来创建指定数量的对象。

public class LambdaCollection : Collection where T : DependencyObject, new() { public LambdaCollection(int count) { while (count --> 0) Add(new T()); } }

接下来,添加一个方法来初始化集合中对象的属性。这个方法将使用Lambda表达式来设置属性值。

public class LambdaCollection : Collection where T : DependencyObject, new() { // ... public LambdaCollection WithProperty(DependencyProperty property, Func generator) { for (int i = 0; i < Count; ++i) this[i].SetValue(property, generator(i)); return this; } }

这个方法使用了流畅的接口风格,通过返回this来允许链式调用。它接受两个参数:要设置的属性和值生成器。值生成器是一个函数,它接受集合中元素的索引并返回一个值。

例如,可以创建10个大小递增的圆形对象:

var circles = new LambdaCollection(10) .WithProperty(WidthProperty, i => 1.5 * (i + 1)) .WithProperty(HeightProperty, i => 1.5 * (i + 1));

为了简化X和Y坐标的设置,可以定义一个辅助方法:

public class LambdaCollection : Collection where T : DependencyObject, new() { // ... public LambdaCollection WithXY(Func xGenerator, Func yGenerator) { for (int i = 0; i < Count; ++i) { this[i].SetValue(Canvas.LeftProperty, xGenerator(i)); this[i].SetValue(Canvas.TopProperty, yGenerator(i)); } return this; } }

现在,可以将这些方法组合起来,创建一个复杂的图形布局:

int count = 20; var circles = new LambdaCollection(count) .WithXY(i => 100.0 + (4.0 * i * Math.Sin(i / 4.0 * (Math.PI))), i => 100.0 + (4.0 * i * Math.Cos(i / 4.0 * (Math.PI)))) .WithProperty(WidthProperty, i => 1.5 * i) .WithProperty(HeightProperty, i => 1.5 * i) .WithProperty(Shape.FillProperty, i => new SolidColorBrush( Color.FromArgb(255, 0, 0, (byte)(255 - (byte)(12.5 * i))))); foreach (var circle in circles) MyCanvas.Children.Add(circle);

接下来,来看动画的实现。可以通过自定义动画类来控制动画的值:

public class LambdaDoubleAnimation : DoubleAnimation { public Func ValueGenerator { get; set; } protected override double GetCurrentValueCore(double origin, double dst, AnimationClock clock) { return ValueGenerator(base.GetCurrentValueCore(origin, dst, clock)); } }

这个类为提供了线性插值的功能,可以通过它来获取变换后的值并进行进一步的操作。

为了更好地管理动画集合,可以定义一个专门的集合类:

public class LambdaDoubleAnimationCollection : Collection { // ... public LambdaDoubleAnimationCollection(int count, Func from, Func to, Func duration, Func> valueGenerator) { for (int i = 0; i < count; ++i) { var lda = new LambdaDoubleAnimation { From = from(i), To = to(i), Duration = duration(i), ValueGenerator = valueGenerator(i) }; Add(lda); } } public void BeginApplyAnimation(UIElement[] targets, DependencyProperty property) { for (int i = 0; i < Count; ++i) targets[i].BeginAnimation(property, Items[i]); } }

这个集合类接受几个参数,包括值生成器。值生成器是一个二阶函数,它依赖于集合中元素的位置,并且其值依赖于动画过程中的插值值。

例如,可以创建一个动画,将螺旋形展开成正弦波:

var c = new LambdaDoubleAnimationCollection( circles.Count, i => Canvas.GetLeft(circles[i]), i => 10.0 * i, i => new Duration(TimeSpan.FromSeconds(2)), i => j => 100.0 / j); c.BeginApplyAnimation(circles.Cast().ToArray(), Canvas.LeftProperty);

虽然不能直接展示动画效果,但可以看到最终的布局效果。

public class LambdaDoubleAnimationCollection : Collection { // ... public void BeginApplyAnimation(UIElement[] targets, DependencyProperty property) { for (int i = 0; i < Count; ++i) { Items[i].BeginTime = new TimeSpan(0); targets[i].BeginAnimation(property, Items[i]); } } public void BeginSequentialAnimation(UIElement[] targets, DependencyProperty property) { TimeSpan acc = new TimeSpan(0); for (int i = 0; i < Items.Count; ++i) { Items[i].BeginTime = acc; acc += Items[i].Duration.TimeSpan; } for (int i = 0; i < Count; ++i) { targets[i].BeginAnimation(property, Items[i]); } } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485