65.9K
CodeProject 正在变化。 阅读更多。
Home

使用图形层实现动画控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (28投票s)

2015年2月23日

CPOL

9分钟阅读

viewsIcon

65421

downloadIcon

6362

本文讨论了如何使用图形层创建动画控件。

摘要 目录

本文详细讨论了如何使用多个图形层来创建复杂的动态 Windows Forms 控件。

在实现复杂的图形对象时,程序员经常面临重大挑战。当包含多个图形层时,可以显著降低复杂性。此外,还可以获得显着的执行时间缩减的好处。

引言 目录

Attitude Indicator P-20 R-20 Attitude Indicator Attitude Indicator P20 R20

最近,在开发一个跨线程消息系统演示时,我需要创建一个模拟的飞机驾驶舱仪表。我实际上不需要创建仪表图形,但这对我来说是一个无法抗拒的挑战。其中一个仪表称为姿态指示器。

该仪表同时显示俯仰(机头上仰或机头下俯)和滚转(机翼上抬或机翼下压)。在现代飞机中,电子姿态和航向参考系统(AHRS)在很大程度上取代了该仪表。然而,姿态指示器仅由两个输入(俯仰和滚转)驱动。因此,通过使用姿态指示器,我可以在不模拟 AHRS 的情况下模拟飞机的姿态。

姿态指示器是一个复合控件。一个小型的符号化飞机固定在仪表内部。仪表的上半部分是蓝色,代表天空;下半部分是棕色,代表地面。顶部的滚转索引显示了滚转角度,滚转刻度上有 0°、10°、20°、30°、45° 和 60° 的标记线和三角形。中心的俯仰索引显示了俯仰的量。符号化飞机在俯仰索引上方上下移动,而滚转则由天空-地面滚转索引的顺时针或逆时针旋转表示。

在接下来的讨论中,由开发人员指定的属性将以粗体混合大小写文本显示。在软件内部使用的变量将以斜体小写文本显示,并用下划线分隔单词。

目录

符号 目录 将读者带回到目录的顶部。

视觉属性 目录

AttitudeIndicator 控件具有影响用户视觉图像的属性。开发人员通过将控件从工具箱拖到窗体上的某个位置来指定控件的左上角。该位置在控件的图形环境中成为 (0, 0)。

AttitudeIndicator 控件的高度与其宽度相同。控件的宽度可以通过 Visual Studio 设计器 [^] 的大小调整手柄来指定,或者通过设置控件的 Size [^] 属性的新值,或者通过为ControlWidth 属性指定一个值来指定。控件的宽度限制为 150 到 500 的 50 的倍数。如果控件被调整大小,所有控件组件的值将根据控件的新宽度重新计算。

CurrentPitchCurrentRoll 属性是通过俯仰和滚转值与控件通信的方式。任一值的更改都会导致显示更新。这两个属性都接受十进制值。

实现 目录

除了背景和符号化飞机之外,控件的图形组件都是动态的,并反映飞机的瞬时俯仰和滚转。这使得姿态指示器成为一个相对复杂的仪表来模拟。然而,如果将图形分解为单独的图形层,就可以降低复杂性并显着提高控件的响应能力。

对于姿态指示器,我选择了七个图形层

背景        仪表主体层。
固定        符号化飞机层。
天空地面        蓝色/棕色层。
Pitch        俯仰刻度层。
顶部底部        出现在天空地面层顶部和底部的盖子。
Roll(滚动)        滚转(坡度)刻度层。
指示器        将天空地面层、俯仰层、顶部底部层和滚转层组合在一起的图层。

除指示器层外,每个层都会被创建一次,并且仅在调整大小时才会重新创建。只有在绘制指示器层时,才会将现有的天空地面层、俯仰层、顶部底部层和滚转层组合(平移和旋转)以创建飞机姿态图像。在 OnPaint 事件处理程序中,会组合背景层、指示器层和固定层。

分层 目录

提醒读者,在图形环境中,后绘制的对象会覆盖先绘制的对象。在创建或组合图层时也是如此。因此,图层的绘制顺序是

背景
指示器
天空地面
Pitch
顶部底部
Roll(滚动)
固定

初始化 目录

姿态指示器实现为 Windows Forms 控件。所有图形的尺寸均由控件的宽度决定,该宽度可以从 150 像素到 500 像素不等,以 50 像素为增量。OnCreateControl 和 OnResize 事件处理程序调用 revise_geometry_values。该方法重新计算用于绘制控件组件的变量。它还将强制重绘所有图层。

背景层 目录

Background Layer

背景层是控件中显示的最低层。背景层的宽度和高度等于控件的宽度和高度。绘制背景层包括以下步骤:

1. 绘制并填充圆角矩形
2. 绘制并填充仪表圆
3. 绘制一个内圆,所有其他组件都绘制在该内圆内部

一旦绘制完成,除非 AttitudeIndicator 控件被调整大小,否则背景层永远不会被重绘。

可能唯一值得关注的步骤是绘制圆角矩形。

    // ************************************ rounded_rectangle_path

    /// <summary>
    /// computes the GraphicsPath of a rounded rectangle
    /// </summary>
    /// <param name="x">
    /// x coordinate of the upper left corner of the rectangle
    /// </param>
    /// <param name="y">
    /// y coordinate of the upper left corner of the rectangle
    /// </param>
    /// <param name="width">
    /// width of the rectangle
    /// </param>
    /// <param name="height">
    /// height of the rectangle
    /// </param>
    /// <param name="radius">
    /// radius of the circle that defines the rounded corner
    /// </param>
    /// <returns>
    /// the GraphicsPath that defines the rounded rectangle
    /// </returns>
    GraphicsPath rounded_rectangle_path ( int x,
                                          int y,
                                          int width,
                                          int height,
                                          int radius )
        {
        int           diameter = 2 * radius;
        GraphicsPath  path = new GraphicsPath ( );

        path.StartFigure ( );
        path.AddArc ( ( x + width - diameter ), 
                      ( y + height - diameter ), 
                      diameter, 
                      diameter, 
                      0.0F, 
                      90.0F );
        path.AddArc ( x, 
                      ( y + height - diameter ), 
                      diameter, 
                      diameter, 
                      90.0F, 
                      90.0F );
        path.AddArc ( x, 
                      y, 
                      diameter, 
                      diameter, 
                      180.0F, 
                      90.0F );
        path.AddArc ( ( x + width - diameter ), 
                      y, 
                      diameter, 
                      diameter, 
                      270.0F, 
                      90.0F );
        path.CloseFigure ( );

        return ( path );
        }

    // ************************************ rounded_rectangle_path

    /// <summary>
    /// computes the GraphicsPath of a rounded rectangle
    /// </summary>
    /// <param name="rectangle">
    /// the rectangle for which rounding is desired
    /// </param>
    /// <param name="radius">
    /// radius of the circle that defines the rounded corner
    /// </param>
    /// <returns>
    /// the GraphicsPath that defines the rounded rectangle
    /// </returns>
    GraphicsPath rounded_rectangle_path ( Rectangle  rectangle,
                                          int        radius )
        {

        return ( rounded_rectangle_path ( rectangle.X,
                                          rectangle.Y,
                                          rectangle.Width,
                                          rectangle.Height,
                                          radius ) );
        }

固定层 目录

Stationary Layer

固定层包含必须出现在所有其他 AttitudeIndicator 组件之上的组件。这些组件包括符号化飞机和固定的滚转指针。

图像由十三个点组成,以各种方式绘制或填充。固定层组件的绘制顺序很重要。

一旦绘制完成,除非 AttitudeIndicator 控件被调整大小,否则固定层永远不会被重绘。

天空地面层 目录

SkyGround Layer

天空地面层包含天空和地面的视觉描绘。

天空使用线性渐变画笔从上到下绘制。地面由两个矩形绘制,颜色相同。

这两个矩形之间隔开的距离等于俯仰层的地面部分。这使得俯仰层可以随着飞机俯仰的变化而上下移动。

一旦绘制完成,除非 AttitudeIndicator 控件被调整大小,否则天空地面层永远不会被重绘。

俯仰层 目录

Pitch Layer

俯仰层描绘了仪表中心的俯仰索引。它显示了飞机俯仰的量。

俯仰层包含一个俯仰刻度和一部分地面。这两个组件适合放在天空地面层地面矩形的间隙中。

一旦绘制完成,除非 AttitudeIndicator 控件被调整大小,否则俯仰层永远不会被重绘。

顶部底部层 目录

TopBottom Layer

顶部底部层通过为天空地面层提供上下盖子来补充它。它纯粹是装饰性的。

一旦绘制完成,除非 AttitudeIndicator 控件被调整大小,否则顶部底部层永远不会被重绘。

滚转层 目录

Roll Layer

滚转层描绘了仪表顶部的滚转索引。它显示了滚转角度,滚转刻度上有标记线和三角形,代表飞机 0°、10°、20°、30°、45° 和 60° 的滚转。

一旦绘制完成,除非 AttitudeIndicator 控件被调整大小,否则滚转层永远不会被重绘。

指示器层 目录

Indicator Layer

指示器层为飞机俯仰和滚转的动态效果提供了画布。每当俯仰或滚转发生变化时,指示器层都会重绘。

在指示器层中,天空地面层、俯仰层、顶部底部层和滚转层中的每一个都被组合(平移和旋转)以创建飞机姿态图像。

在指示器层中创建此图像所采取的步骤是:

  1. 定义一个剪裁区域。这可以防止任何子层溢出控件边界(在 OnCreateControl 和 OnResize 事件处理程序中设置)。
  2. 将天空地面层图像(位图)旋转到飞机的CurrentRoll 并绘制到指示器层中。
  3. 俯仰层的 x 和 y 坐标根据飞机的CurrentPitch 计算;俯仰层图像(位图)旋转到飞机的CurrentRoll;指示器层的原点平移到前面计算出的 x 和 y 坐标;旋转后的俯仰层图像绘制到指示器层中。
  4. 顶部底部层图像(位图)旋转到飞机的CurrentRoll 并绘制到指示器层中。
  5. 滚转层图像(位图)旋转到飞机的CurrentRoll 并绘制到指示器层中。

用于旋转位图的方法是:

    // ********************************************* rotate_bitmap

    // http://stackoverflow.com/questions/5172906/
    //     c-rotating-graphics

    /// <summary>
    /// rotate a bitmap by a specified angle, in degrees
    /// </summary>
    /// <param name="bitmap">
    /// bitmap to rotate
    /// </param>
    /// <param name="angle">
    /// angle, in degrees, by which to rotate bitmap
    /// </param>
    /// <returns>
    /// the rotated bitmap
    /// </returns>
    Bitmap rotate_bitmap ( Bitmap  bitmap, 
                           float   angle )
        {
        Bitmap return_bitmap;
                                    // create empty bitmap to hold 
                                    // rotated image
        return_bitmap = new Bitmap ( bitmap.Width, 
                                     bitmap.Height );
                                    // make a graphic object from 
                                    // the empty bitmap
        using ( Graphics graphic = Graphics.FromImage ( 
                                                return_bitmap ) )
            {
                                    // translate rotation point to 
                                    // center of image
            graphic.TranslateTransform ( 
                                ( float ) bitmap.Width / 2.0F, 
                                ( float ) bitmap.Height / 2.0F );
                                    // rotate image
            graphic.RotateTransform ( angle );
                                    //move image back
            graphic.TranslateTransform ( 
                                -( float ) bitmap.Width / 2.0F, 
                                -( float ) bitmap.Height / 2.0F );
                                    // draw passed in image onto 
                                    // graphic object
            graphic.DrawImage ( bitmap, new Point ( 0, 0 ) );

            graphic.ResetTransform ( );
            }

        return ( return_bitmap );
        }

演示 目录

Test Attitude Indicator

演示程序描绘了 AttitudeIndicator 控件如何响应俯仰和滚转的变化。它还允许选择显示哪些图层。

程序启动时,显示背景层、指示器层和固定层。如果要显示构成指示器层的图层,则必须清除“指示器层”复选框。这是必需的,因为指示器层是一个复合层,由天空地面层、俯仰层、顶部底部层和滚转层组成。

结论 目录

本文详细讨论了如何使用多个图形层来创建复杂的动态 Windows Forms 控件。

参考文献 目录

飞机驾驶舱仪表 目录

对于有兴趣的读者,我包含了以下两个链接。完整的 FAA 航空手册和指南可以在 FAA 的网站 [^] 上找到。

编程 目录

开发环境 目录

AttitudeIndicator 控件在以下环境中开发:

Microsoft Windows 7 Professional Service Pack 1
Microsoft Visual Studio 2008 Professional
Microsoft .Net Framework Version 3.5 SP1
Microsoft Visual C# 2008

下载 目录

除了 AttitudeIndicator 控件(本文的主要主题)外,下载内容还包括一些额外的飞机驾驶舱控件:

  • 空速指示器
  • 高度指示器
  • 航向指示器
  • 转速表
  • 垂直速度指示器

所有这些控件都使用多个图形层来实现效果。然而,AttitudeIndicator 控件是迄今为止最复杂的,因此被选为本文的示例。

历史 目录

02/23/2015       原文
02/25/2015       修改文章格式
© . All rights reserved.