zoukankan      html  css  js  c++  java
  • 《深入浅出WPF》学习笔记之绘图和动画

    绘图

    通过WPF绘制的图形都是矢量图,可以通过Design或Blend工具绘制原型图然后导出XAML再在Visual Studio中调整。绘图可以在任意布局控件中完成,常用的为Canvas和Grid,WPF会自动根据容器的不同计算图形坐标。WPF基本图像都继承自Shape类。

    Line,绘制直线,通过设置起始点坐标和终止点坐标确定直线的位置,可以设置直线的样式,颜色,粗细等。通过设置Stroke属性设置图形的颜色,Stroke属性为Brush类型,Brush为抽象类。WPF系统包含丰富的Brush子类,SolidColorBrush,LinearGradientBrush(线性渐变画刷),RadialGradientBrush(径向渐变画刷),ImageBrush(图像填充),DrawingBrush(使用矢量图和位图作为填充),VisualBrush,使用可视化元素填充,可以当做可视化元素的截图。

    Rectangle,绘制矩形,可以设置边框颜色,边框大小和填充,填充Fill同Stroke一样也为Brush类型。

    Ellipse,绘制椭圆,使用方法与矩形没有什么区别,当width和height相同时即为正圆。

    Path,路径是WPF绘图中最强大的工具,它即可以替代其他几种图形,也可以形成更复杂的图形。路径的重要属性是Data,通过设置Data来确定具体的图形。Data的类型为Geometry,同Brush一样,Geometry为抽象类,WPF已经准备了若干继承类,包括LineGeometry,RectangleGeometry,EllipseGeometry,PathGeometry,StreamGeometry(轻量级PathGeometry),CombinedGeometry,GeometryGroup。Geometry类与Shape类不同,Geometry不能作为单独元素使用,只能依附在其他图形上。PathGeometry可以绘制复杂的几何图形,其Figures可以容纳多个PathFigure对象,而PathFigure的Segments属性可以容纳多个PathSegment对象,通过PathSegment组合成复杂图形。同样的PathSegment为抽象类,继承类包括LineSegment,ArcSegment,BezierSegment,QuadraticBezierSegment,PolyBezierSegment,PolyQuadraticBezierSegment。所有这些Segment都没有起点,起点就是上一个Segment的终点,第一个Segment的起点为PathFigure的StartPoint。Path的示例代码:

            <Path Stroke="Blue">
                <Path.Data>
                    <PathGeometry>
                        <PathFigure StartPoint="0,0">
                            <LineSegment Point="0,10"></LineSegment>
                            <LineSegment Point="10,50"></LineSegment>
                            <LineSegment Point="20,40"></LineSegment>
                            <ArcSegment Point="40,80" Size="25,50"></ArcSegment>
                            <BezierSegment Point1="20,20" Point2="30,30" Point3="40,40"></BezierSegment>
                        </PathFigure>
                    </PathGeometry>
                </Path.Data>
            </Path>

    GeometryGroup也是Geometry的一个继承类,它最大的特点是可以将一组Geometry组合到一起。

    路径标记语法

    Path如此强大,可以使我们随心所欲的绘制图形,但其缺点也不容忽视,就是其标签语法国语繁琐,如果图形过于复杂则会生成大量标签。通过使用路径标记语法可以极大的简化Path的描述,路径标记语法实际上就是各种线段的简记法,比如<LineSegment Point="10,50"></LineSegment>可以简记为L 10,50。使用路径标记语法一般分三步:移动至起点,绘图,闭合图形。路径标记语法不区分大小写,使用两个double表示一个点,中间用逗号或空格分隔,为避免混淆,常使用逗号分隔。

    可以通过设置UIElement的Clip属性对控件进行裁剪,Clip类型即为Geometry。

    图形的效果与滤镜

    UIElement中BitmapEffect和Effect的两个属性用来给元素添加效果,BitmapEffect使用CPU计算资源,现在已经被遗弃。Effect属性使用GPU资源。BitmapEffect简单易用,它的类型为BitmapEffect类,是一个抽象类,WPF为我们实现了若干子类,通过设置这些子类的属性来调整效果。示例代码:

            <Button Width="120" Height="30">
                <Button.BitmapEffect>
                    <DropShadowBitmapEffect ShadowDepth="5" Opacity="0.7" Direction="-45"></DropShadowBitmapEffect>
                </Button.BitmapEffect>
            </Button>

    Effect属性的类型为Effect,同样为抽象类,WPF为我们实现了3个子类,BlurEffect,用来实现模糊效果。DropShadowEffect,用来实现投影效果。ShaderEffect,着色器效果,抽象类,它是留给滤镜插件开发人员的抽象接口。

    元素的变形

    控制变形的属性有两个RenderTransform和LayoutTransform,他们的数据类型都是Transform抽象类。Transform抽象类的派生类有如下一些,MatrixTransform,RotateTransform,ScaleTransform,SkewTransform,TranslateTransform,TransformGroup。呈现变形(RenderTransform)只改变元素的呈现位置,不会对布局系统造成影响,以减少资源占用,当使用动画时一定要使用呈现变形。注意控件呈现在哪里就在哪里提供交互。示例代码:

            <Button Width="120" Height="30" Click="Button_Click">
                <Button.RenderTransform>
                    <TranslateTransform X="50" Y="50"></TranslateTransform>
                </Button.RenderTransform>
            </Button>

    布局变形(LayoutTransform)会影响窗体的布局,导致布局重新计算,这会影响程序性能,因此布局变形只用在静态变形上,不会用于动画。

    动画

    可以用来制作动画的属性一定是依赖属性,动画的基类为Timeline。简单动画由一个元素就可以完成,简单动画使用AnimationTimeline实现。复杂动画由多个元素和多个动画协同完成,复杂动画使用StoryBoard实现。

    通过AnimationTimeline可以针对一个UI元素实现简单动画,AnimationTimeline为抽象类,WPF动画系统实现了常用数据类型的子类,如DoubleAnimationBase、ByteAnimationBase等。这种数据类型并以Base做后缀的类为抽象基类,每种数据类型的抽象基类又派生出3种具体动画,分别为简单动画、关键帧动画、沿路径运动的动画。注意并不是所有的数据类型都能派生出这3种类型的动画。DoubleAnimationBase派生出DoubleAnimation(简单动画),DoubleAnimationUsingKeyFrames(关键帧动画),DoubleAnimationUsingPath(沿路径运动动画)。简单动画由变化时间Duration、变化起点From、变化幅度By、变化终点To四要素构成,如果同时设置To和By,By将被忽略。示例代码:

            <Button x:Name="button" Width="120" Height="30" Click="Button_Click">
                <Button.BitmapEffect>
                    <DropShadowBitmapEffect ShadowDepth="5" Opacity="0.7" Direction="-45"></DropShadowBitmapEffect>
                </Button.BitmapEffect>
                <Button.RenderTransform>
                    <TranslateTransform X="0" Y="0"></TranslateTransform>
                </Button.RenderTransform>
            </Button>
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                DoubleAnimation dax = new DoubleAnimation();
                DoubleAnimation day = new DoubleAnimation();
                dax.From = 0;
                day.From = 0;
    
                dax.By = 100;
                day.By = 100;
    
                dax.Duration = new Duration(TimeSpan.FromMilliseconds(500));
                day.Duration = new Duration(TimeSpan.FromMilliseconds(500));
    
                button.RenderTransform.BeginAnimation(TranslateTransform.XProperty, dax);
                button.RenderTransform.BeginAnimation(TranslateTransform.YProperty, day);
            }

    通过设置动画的高级控制属性可以是动画效果更逼真,如设置加速度、减速度、循环模式,渐进式缓冲等。

    关键帧动画

     通过定义关键帧控制动画的执行效果,每个帧都包含帧所在的时间和值,关键帧动画至少包含一个帧作为动画的结束位置。关键帧动画包含KeyFrames属性用来包含关键帧,可以向其中添加不同类型的关键帧,最简单的关键帧为LinearDoubleKeyFrame。示例代码:

            <Button x:Name="button" Width="120" Click="button_Click" HorizontalAlignment="Left">
                <Button.RenderTransform>
                    <TranslateTransform X="0" Y="0"></TranslateTransform>
                </Button.RenderTransform>
                Button</Button>
    
    
            private void button_Click(object sender, RoutedEventArgs e)
            {
                DoubleAnimationUsingKeyFrames dakf = new DoubleAnimationUsingKeyFrames();
                dakf.Duration = new Duration(TimeSpan.FromSeconds(1));
                
    
                LinearDoubleKeyFrame kf1 = new LinearDoubleKeyFrame();
                kf1.KeyTime = KeyTime.FromPercent(0.2);
                kf1.Value = 50;
    
                LinearDoubleKeyFrame kf2 = new LinearDoubleKeyFrame();
                kf2.KeyTime = KeyTime.FromPercent(0.4);
                kf2.Value = 100;
    
                LinearDoubleKeyFrame kf3 = new LinearDoubleKeyFrame();
                kf3.KeyTime = KeyTime.FromPercent(0.8);
                kf3.Value = 150;
    
                dakf.KeyFrames.Add(kf1);
                dakf.KeyFrames.Add(kf2);
                dakf.KeyFrames.Add(kf3);
    
    
                button.RenderTransform.BeginAnimation(TranslateTransform.XProperty, dakf);
            }

    路径动画

    控制动画沿指定路径移动,路径动画的Source属性用来控制关注路径的X轴的变化还是Y轴的变化还是曲线切线方向变化。示例代码:

        <Grid x:Name="layoutRoot">
            <Grid.Resources>
                <PathGeometry x:Key="path" Figures="M 0,150 C300,-100 300,400 600,120"></PathGeometry>
            </Grid.Resources>
            <Button x:Name="button" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20" Click="button_Click">
                <Button.RenderTransform>
                    <TranslateTransform X="0" Y="0"></TranslateTransform>
                </Button.RenderTransform>
                Button</Button>
        </Grid>
    
    
            private void button_Click(object sender, RoutedEventArgs e)
            {
                PathGeometry path = layoutRoot.FindResource("path") as PathGeometry;
                DoubleAnimationUsingPath daupx = new DoubleAnimationUsingPath();
                DoubleAnimationUsingPath daupy = new DoubleAnimationUsingPath();
    
                daupx.Duration = new Duration(TimeSpan.FromSeconds(1));
                daupx.PathGeometry = path;
                daupx.Source = PathAnimationSource.X;
    
    
                daupy.Duration = new Duration(TimeSpan.FromSeconds(1));
                daupy.PathGeometry = path;
                daupy.Source = PathAnimationSource.Y;
    
    
                button.RenderTransform.BeginAnimation(TranslateTransform.XProperty, daupx);
                button.RenderTransform.BeginAnimation(TranslateTransform.YProperty, daupy);
            }

    场景(Storyboard)

    场景就是并行执行的一组动画,XAML代码示例:

        <Grid>
            <Button VerticalAlignment="Top" HorizontalAlignment="Left">
                <Button.RenderTransform>
                    <TranslateTransform x:Name="tt" X="0" Y="0"></TranslateTransform>
                </Button.RenderTransform>
                <Button.Triggers>
                    <EventTrigger RoutedEvent="Button.Loaded">
                        <BeginStoryboard>
                            <Storyboard Duration="0:0:1">
                                <DoubleAnimation Duration="0:0:1" To="200" Storyboard.TargetName="tt" Storyboard.TargetProperty="X"></DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </Button.Triggers>
                Button</Button>
        </Grid>

    C#代码示例:

        <Grid>
            <Button VerticalAlignment="Top" HorizontalAlignment="Left" Click="Button_Click">
                <Button.RenderTransform>
                    <TranslateTransform x:Name="tt" X="0" Y="0"></TranslateTransform>
                </Button.RenderTransform>
    
                Button</Button>
        </Grid>
    
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                DoubleAnimation da = new DoubleAnimation();
                da.To = 200;
                da.Duration = new Duration(TimeSpan.FromSeconds(1));
    
                Storyboard sb = new Storyboard();
                sb.Duration = new Duration(TimeSpan.FromSeconds(1));
                Storyboard.SetTargetName(da, "tt");
                Storyboard.SetTargetProperty(da, new PropertyPath(TranslateTransform.XProperty));
    
                sb.Children.Add(da);
                sb.RepeatBehavior = RepeatBehavior.Forever;
                sb.Begin(this);
            }
  • 相关阅读:
    Python 类中方法的内部变量,命名加'self.'变成 self.xxx 和不加直接 xxx 的区别
    用foreach遍历 datagridView 指定列所有的内容
    treeView1.SelectedNode.Level
    YES NO 上一个 下一个
    正则 单词全字匹配查找 reg 边界查找 精确匹配 只匹配字符 不含连续的字符
    抓取2个字符串中间的字符串
    sqlite 60000行 插入到数据库只用不到2秒
    将多行文本以单行的格式保存起来 读和写 ini
    将秒转换成时间格式
    richtextbox Ctrl+V只粘贴纯文本格式
  • 原文地址:https://www.cnblogs.com/jefflee/p/5836662.html
Copyright © 2011-2022 走看看