AnimationClock を使ってアニメーション動画を作る(8)
|
トップページへ
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」で紹介しています。
ClockGroup を使ったアニメーション−応用編(2D)
|
1.作成するアニメーション動画の内容
このアニメーション動画は以下のように構成されています。
(1) 1枚の画像を 16 分割して、16 個の画像要素(以下、ピースと略)を作成し、Canvas 上に配置する。
(2) 16 個のピースを1個づつ、1秒間隔で Canvas 上の所定位置まで回転させながら移動して、元の画像を復元させる。
ピースの回転と移動は、回転と移動の Transform を生成して、これに DoubleAnimation を適用して行います。
この Transform へのDoubleAnimation の適用は、ClockGroup を介して行います。
アニメーション作成の画面は以下のようになります。

作者の以下の公開動画、JigsawAnime シリーズには、このアニメーション手法が応用されています。
2.使用した開発環境
第1例 と同じく、以下の開発環境を使用しました。
開発環境:Visual Studio 2010 Professional Edition
.NET Framework:.NET Framework 4 Client Profile
古いバージョンを使いましたが、最新の開発環境でも問題なく使用できると思います。
3.XAMLコード
XAMLコードは、Canvas のサイズと Background の値を除いて、第1例 と同じなので、省略します。
4.C#コード
// usingによって名前空間を参照するコードの部分は省略
public partial class MainWindow : Window
{
// コンストラクター
public MainWindow()
{
InitializeComponent();
InitializeWindow();
}
private const int DST_X = 183; // 配置先のLeft(DestinationLeft)
private const int NUM = 4; // 縦横各方向のピースの数
private const int SIZE = 70; // 1個のピースの縦横サイズ
private const int INI_X = -100; // 初期配置のLeft
private const int INI_Y = 105; // 初期配置のTop
private const int TIME = 1;
private const int TOTAL = NUM * NUM; // 全ピースの数
private readonly int[] ORDER = // ピースの移動順番(10番ピースから開始)
{ 10, 0, 13, 2, 15, 5, 8, 7, 1, 14, 4, 11, 9, 3, 12, 6 };
private ClockGroup _cgRot, _cgTrX, _cgTrY;
private Piece[] _piece = new Piece[TOTAL]; // Pieceオブジェクトの配列
// [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)
{
_cgRot.Controller.Begin();
_cgTrX.Controller.Begin();
_cgTrY.Controller.Begin();
}
// [停止]ボタンクリックイベント
private void btnStop_Click(object sender, RoutedEventArgs e)
{
_cgRot.Controller.Stop();
_cgTrX.Controller.Stop();
_cgTrY.Controller.Stop();
}
// 切り取り画像のBitmapを返すメソッド
private CroppedBitmap GetCrpBmp(BitmapFrame vframe,
int left, int top, int width, int height)
{
var cbmp = new CroppedBitmap();
cbmp.BeginInit();
cbmp.Source = vframe;
cbmp.SourceRect = new Int32Rect(left, top, width, height);
cbmp.EndInit();
return cbmp;
}
// Aviファイル保存パス取得
private string GetAviFilePath()
{
// 省略
}
// ピースのアニメーションを生成するメソッド
private void CreateAnimation()
{
// ParallelTimeline生成
var ptRot = new ParallelTimeline();
var ptTrX = new ParallelTimeline();
var ptTrY = new ParallelTimeline();
// Transform(回転、移動)の配列生成
RotateTransform[] rottran = new RotateTransform[TOTAL];
TranslateTransform[] tranX = new TranslateTransform[TOTAL];
TranslateTransform[] tranY = new TranslateTransform[TOTAL];
var tsDur = new TimeSpan(0, 0, TIME); // アニメーションのTimeSpan
// EasingFunction設定
var easing = new CircleEase();
easing.EasingMode = EasingMode.EaseOut;
// ピースのアニメーションとClockGroup生成
for (int i = 0; i < TOTAL; i++)
{
var tsBegin = new TimeSpan(0, 0, i * TIME + 1);
// Transform(回転、移動)設定
rottran[i] = new RotateTransform();
rottran[i].CenterX = SIZE / 2;
rottran[i].CenterY = SIZE / 2;
tranX[i] = new TranslateTransform();
tranY[i] = new TranslateTransform();
var groupT = new TransformGroup();
groupT.Children.Add(rottran[i]);
groupT.Children.Add(tranX[i]);
groupT.Children.Add(tranY[i]);
_piece[i].RenderTransform = groupT;
// ピース回転のアニメーション
var daRot = new DoubleAnimation(360, new Duration(tsDur));
daRot.BeginTime = tsBegin;
daRot.EasingFunction = easing;
ptRot.Children.Add(daRot);
// ピース横方向移動のアニメーション
var daX = new DoubleAnimation(_piece[i].DestX, new Duration(tsDur));
daX.BeginTime = tsBegin;
daX.EasingFunction = easing;
ptTrX.Children.Add(daX);
// ピース縦方向移動のアニメーション
var daY = new DoubleAnimation(_piece[i].DestY, new Duration(tsDur));
daY.BeginTime = tsBegin;
daY.EasingFunction = easing;
ptTrY.Children.Add(daY);
}
// ClockGroup生成
_cgRot = ptRot.CreateClock(); // 回転のClockGroup
_cgTrX = ptTrX.CreateClock(); // 横方向移動のClockGroup
_cgTrY = ptTrY.CreateClock(); // 縦方向移動のClockGroup
// Transform(回転、移動)へClockGroupの子要素適用
for (int i2 = 0; i2 < TOTAL; i2++)
{
rottran[i2].ApplyAnimationClock(RotateTransform.AngleProperty,
(AnimationClock)_cgRot.Children[i2]);
tranX[i2].ApplyAnimationClock(TranslateTransform.XProperty,
(AnimationClock)_cgTrX.Children[i2]);
tranY[i2].ApplyAnimationClock(TranslateTransform.YProperty,
(AnimationClock)_cgTrY.Children[i2]);
}
_cgRot.Controller.Stop();
_cgTrX.Controller.Stop();
_cgTrY.Controller.Stop();
}
// ピースを生成するメソッド
private void CreatePieces()
{
var frame = BitmapFrame.Create(new Uri("Pic1.JPG", UriKind.Relative));
for (int i = 0; i < TOTAL; i++)
{
_piece[i] = new Piece(); // Pieceクラス(最下行で定義)のインスタンス生成
Canvas.SetLeft(_piece[i], INI_X);
Canvas.SetTop(_piece[i], INI_Y);
var x = ORDER[i] / NUM * SIZE;
var y = ORDER[i] % NUM * SIZE;
_piece[i].DestX = DST_X + x;
_piece[i].DestY = y - INI_Y;
_piece[i].Source = GetCrpBmp(frame, x, y, SIZE, SIZE);
cvs1.Children.Add(_piece[i]);
}
}
// ピースとそのアニメーションの設定
private void InitializeWindow()
{
CreatePieces();
CreateAnimation();
}
// Aviファイル作成
private void SaveAsAvi(string filepath)
{
var fps = 48; // フレームレート
var totalFrames = fps * TIME * (TOTAL + 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)
{
_cgRot.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(sec),
TimeSeekOrigin.BeginTime);
_cgTrX.Controller.SeekAlignedToLastTick(TimeSpan.FromSeconds(sec),
TimeSeekOrigin.BeginTime);
_cgTrY.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();
_cgRot.Controller.Stop();
_cgTrX.Controller.Stop();
_cgTrY.Controller.Stop();
System.IO.File.Delete(tempFile);
}
// Pieceクラスの定義−Imageクラスに DestX, DestY プロパティを追加したもの
private class Piece : Image
{
public int DestX { get; set; } // ピース着地点のX座標
public int DestY { get; set; } // ピース着地点のY座標
}
}
Piece クラス(private クラス)の定義は、コードの最後部に記載してあります。
ピース(Piece クラスのインスタンス)は、[CreatePieces]メソッドで生成されます。
ピースの各アニメーション、各ClockGroup は[CreateAnimation]メソッドで生成されます。
アニメーションの開始は、[開始]ボタンクリックイベント内の、各 ClockGroup の Begin メソッドで行います。
アニメーションの停止は、[停止]ボタンクリックイベント内の、各 ClockGroup の Stop メソッドで行います。
Aviファイル作成のためにフレーム画像を取り出す操作は、[SaveAsAvi]メソッド内の、各 ClockGroup の SeekAlignedToLastTick メソッドで行います。
[SaveAsAvi]メソッドの中間付近にある cvs1 の SaveImage 拡張メソッドについては、第1例 の記載を参照してください。
[CreateAnimation]メソッドで使用した EasingFunction は、ピースを減速して着地させるために使用しています。
ここで紹介したアニメーションを作成するプロジェクトファイルは、以下のリンクからダウンロードすることができます。
「Example08_Clockgroup2」プロジェクトファイルのダウンロード
なお、このプロジェクトでは、AVIファイル化用クラスライブラリとして、Code Project の「
A Simple C# Wrapper for the AviFile Library 」からダウンロードして入手した[AviFile.dll]を使用しています。
ダウンロードしたファイルの利用は、全て利用される方の責任で行っていただきます。
作者は十分な注意を払って本ファイルを作成していますが、もし万一、本ファイルの内容に誤りがあっても、
またその利用によって使用パソコンに問題が発生しても、作者は一切責任を負いません。
2017/09/01 ページを公開しました。
ご質問、ご意見、ご感想等の連絡は、
こちらへ
トップページへ