AnimationClock を使ってアニメーション動画を作る(2)

トップページへ

 AnimationClock を使用したアニメーションの紹介

  1.DoubleAnimationUsingKeyFrames を使用したアニメーション(2D)
  2.Transform を使った円のアニメーション(2D) (このページ)
  3.指定された経路上で円を運動させるアニメーション(2D)
  4.TileMode で描画された画像のアニメーション(2D)
  5.自公転する立方体のアニメーション(3D)
  6.立方体の表示にタイムラグを設けるアニメーション(3D)
  7.ClockGroup を使ったアニメーション−基本編(2D)
  8.ClockGroup を使ったアニメーション−応用編(2D)
  9.残像を形成するアニメーション(2D)

なお、これらの動画は、「ソフト関連Trial−No.109」で紹介しています。

 Transform を使った円のアニメーション動画の作成方法(2D)

1.作成するアニメーションの内容
Canvas の左上方に配置された 円 を回転させながら右下方向へ移動させ、この移動中に円のサイズを 2 倍に拡大するアニメーションです。
このプロジェクトでは、5 個の AnimationClock を使用します。
各 AnimationClock は、いずれも DoubleAnimation から生成されます。

アニメーション作成の画面は以下のようになります。


2.使用した開発環境
前例 と同じく、以下の開発環境を使用しました。
開発環境:Visual Studio 2010 Professional Edition
.NET Framework:.NET Framework 4 Client Profile
古いバージョンを使いましたが、最新の開発環境でも問題なく使用できると思います。

3.XAMLコード
XAMLコードは 前例 と同じなので、省略します。

4.C#コード
C#コードで、Ellipse を Canvas上に配置してから、回転、拡大、移動の変換(Transform)を設定し、各変換のアニメーションを記述します。

  // usingによって名前空間を参照するコードの部分は省略
  
  public partial class MainWindow : Window
  {
    // コンストラクター
    public MainWindow()
    {
      InitializeComponent();
      InitializeWindow();
    }

    private const double TIME = 3;     // 各変換のアニメーションの時間(片道)
    private AnimationClock _clockRot;   // 回転のAnimationClock
    private AnimationClock _clockScX;   // 横方向サイズ拡大のAnimationClock
    private AnimationClock _clockScY;   // 縦方向サイズ拡大のAnimationClock
    private AnimationClock _clockTrX;   // 横方向移動のAnimationClock
    private AnimationClock _clockTrY;   // 縦方向移動のAnimationClock

    // [Avi]ボタンクリックイベント
    private void btnAvi_Click(object sender, RoutedEventArgs e)
    {
      var filepath = GetAviFilePath();
      if (0 < filepath.Length)
      {
        SaveAsAvi(filepath);
        MessageBox.Show("OK");
      }
    }

    // [開始]ボタンクリックイベント
    private void btnStart_Click(object sender, RoutedEventArgs e)
    {
      _clockRot.Controller.Begin();
      _clockScX.Controller.Begin();
      _clockScY.Controller.Begin();
      _clockTrX.Controller.Begin();
      _clockTrY.Controller.Begin();
    }

    // [停止]ボタンクリックイベント
    private void btnStop_Click(object sender, RoutedEventArgs e)
    {
      _clockRot.Controller.Stop();
      _clockScX.Controller.Stop();
      _clockScY.Controller.Stop();
      _clockTrX.Controller.Stop();
      _clockTrY.Controller.Stop();
    }

    // Aviファイル保存パス取得
    private string GetAviFilePath()
    {
      // 省略
    }

    // Ellipseの配置とアニメーションの適用
    private void InitializeWindow()
    {
      const double RADIUS = 20;
      
      // Ellipseの生成と配置
      circle.Width = RADIUS * 2;
      circle.Height = RADIUS * 2;
      Canvas.SetLeft(circle, 0);
      Canvas.SetTop(circle, 0);
      var gscollect = new GradientStopCollection();
      gscollect.Add(new GradientStop(Colors.Blue, 0.5));
      gscollect.Add(new GradientStop(Colors.Red, 0.5));
      circle.Fill = new LinearGradientBrush(gscollect);
      cvs1.Children.Add(circle);

      // Transform関連オブジェクト生成
      var tgroup = new TransformGroup();
      circle.RenderTransform = tgroup;
      
      // 回転のアニメーション生成
      var rotTran = new RotateTransform(0, RADIUS, RADIUS);
      tgroup.Children.Add(rotTran);
      var animRot = new DoubleAnimation(0, 360, new Duration(TimeSpan.FromSeconds(TIME)));
      animRot.RepeatBehavior = RepeatBehavior.Forever;
      _clockRot = animRot.CreateClock();
      rotTran.ApplyAnimationClock(RotateTransform.AngleProperty, _clockRot);
      _clockRot.Controller.Stop();

      // サイズ拡大のアニメーション生成
      var scTran = new ScaleTransform();
      tgroup.Children.Add(scTran);
      var animSc = new DoubleAnimation(1, 2, new Duration(TimeSpan.FromSeconds(TIME)));
      animSc.AutoReverse = true;
      animSc.RepeatBehavior = RepeatBehavior.Forever;
      _clockScX = animSc.CreateClock();
      scTran.ApplyAnimationClock(ScaleTransform.ScaleXProperty, _clockScX);
      _clockScX.Controller.Stop();
      _clockScY = animSc.CreateClock();
      scTran.ApplyAnimationClock(ScaleTransform.ScaleYProperty, _clockScY);
      _clockScY.Controller.Stop();

      // 移動(X方向)のアニメーション生成
      var trTran = new TranslateTransform();
      tgroup.Children.Add(trTran);
      var animTrX = new DoubleAnimation(0, 400, new Duration(TimeSpan.FromSeconds(TIME)));
      animTrX.AutoReverse = true;
      animTrX.RepeatBehavior = RepeatBehavior.Forever;
      _clockTrX = animTrX.CreateClock();
      trTran.ApplyAnimationClock(TranslateTransform.XProperty, _clockTrX);
      _clockTrX.Controller.Stop();
      
      // 移動(Y方向)のアニメーション生成
      var animTrY = new DoubleAnimation(0, 180, new Duration(TimeSpan.FromSeconds(TIME)));
      animTrY.AutoReverse = true;
      animTrY.RepeatBehavior = RepeatBehavior.Forever;
      _clockTrY = animTrY.CreateClock();
      trTran.ApplyAnimationClock(TranslateTransform.YProperty, _clockTrY);
      _clockTrY.Controller.Stop();
    }

    // Aviファイル作成
    private void SaveAsAvi(string filepath)
    {
      var fps = 48;                // フレームレート
      var totalFrames = (int)(fps * TIME * 2);   // 全フレーム数
      var secs = Enumerable.Range(0, totalFrames).Select(t => (((double)t) / fps));
      var aviManager = new AviFile.AviManager(filepath, false);
      AviFile.VideoStream aviStream = null;
      var tempFile = "frame.png";

      foreach (var sec in secs)
      {
        _clockRot.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(sec),
                              TimeSeekOrigin.BeginTime);
        _clockScX.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(sec),
                              TimeSeekOrigin.BeginTime);
        _clockScY.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(sec),
                              TimeSeekOrigin.BeginTime);
        _clockTrX.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(sec),
                              TimeSeekOrigin.BeginTime);
        _clockTrY.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(sec),
                              TimeSeekOrigin.BeginTime);
        cvs1.UpdateLayout();
        cvs1.SaveImage(tempFile);      // cvs1の画像をファイルに保存する
        var bmp = new System.Drawing.Bitmap(tempFile);
        if (aviStream == null)
          aviStream = aviManager.AddVideoStream(true, fps, bmp);
        else
          aviStream.AddFrame(bmp);
        bmp.Dispose();
      }
      aviManager.Close();
      
      _clockRot.Controller.Stop();
      _clockScX.Controller.Stop();
      _clockScY.Controller.Stop();
      _clockTrX.Controller.Stop();
      _clockTrY.Controller.Stop();
      
      System.IO.File.Delete(tempFile);
    }
  }

円の生成と各アニメーションの生成は[InitializeWindow]メソッドで行います。
アニメーションの開始は、[開始]ボタンクリックイベント内の、各 AnimationClock の Begin メソッドで行います。
アニメーションの停止は、[停止]ボタンクリックイベント内の、各 AnimationClock の Stop メソッドで行います。
Aviファイル作成のためにフレーム画像を取り出す操作は、[SaveAsAvi]メソッド内の、各 AnimationClock の SeekAlignedToLastTick メソッドで行います。
[SaveAsAvi]メソッドの中間付近にある cvs1 の SaveImage 拡張メソッドについては、前例 の記載を参照してください。

 ダウンロード

ここで紹介したアニメーションを作成するプロジェクトファイルは、以下のリンクからダウンロードすることができます。
「Example02_Transform」プロジェクトファイルのダウンロード
なお、このプロジェクトでは、AVIファイル化用クラスライブラリとして、Code Project の「 A Simple C# Wrapper for the AviFile Library 」からダウンロードして入手した[AviFile.dll]を使用しています。

ダウンロードしたファイルの利用は、全て利用される方の責任で行っていただきます。
作者は十分な注意を払って本ファイルを作成していますが、もし万一、本ファイルの内容に誤りがあっても、 またその利用によって使用パソコンに問題が発生しても、作者は一切責任を負いません。

 公開・更新履歴

 2017/09/01 ページを公開しました。

 ご質問・ご意見・ご感想

ご質問、ご意見、ご感想等の連絡は、 こちらへ

トップページへ