アニメーションの場合は、
- Storyboard の場合は、Blend でちまちま編集して動作確認
- プログラムコードに組み込んで、動作確認
- もう一度、Blend に戻って修正。
- またまた、プログラムコードに組み込んで、動作確認
っていう繰り返しになるので、Blend でデザイン、Visual Studio でビルドしてからシミュレーターで確認、ってのが定番…になると思うのですが、どうなんでしょう?そんなに複雑な Storyboard は作らないのかな?
■Storyboard.Clone を拡張メソッドで作る
リフレクションとか使って正確に書こうとも思ったのですが、さほど複雑な構造でもないし、入れ子になるクラスは決まっているのでだらだらと150行ほど書きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | public static class StoryboardExtensions { /// <summary> /// Storyboard をコピーする /// </summary> /// <param name="src"></param> /// <returns></returns> public static Storyboard Clone( this Storyboard src) { var sb = new Storyboard(); sb.AutoReverse = src.AutoReverse; sb.BeginTime = src.BeginTime; sb.Duration = src.Duration; sb.FillBehavior = src.FillBehavior; sb.RepeatBehavior = src.RepeatBehavior; sb.SpeedRatio = src.SpeedRatio; foreach ( var tl in src.Children) { var tld = tl.Clone(); sb.Children.Add(tld); } return sb; } public static Timeline Clone( this Timeline src) { Timeline dest = null ; if (src is ColorAnimationUsingKeyFrames) dest = ((ColorAnimationUsingKeyFrames)src).Clone(); if (src is DoubleAnimationUsingKeyFrames) dest = ((DoubleAnimationUsingKeyFrames)src).Clone(); if (src is ObjectAnimationUsingKeyFrames) dest = ((ObjectAnimationUsingKeyFrames)src).Clone(); if (src is PointAnimationUsingKeyFrames) dest = ((PointAnimationUsingKeyFrames)src).Clone(); if (dest != null ) { Storyboard.SetTargetProperty(dest, Storyboard.GetTargetProperty(src)); Storyboard.SetTargetName(dest, Storyboard.GetTargetName(src)); } return dest; } public static ColorAnimationUsingKeyFrames Clone( this ColorAnimationUsingKeyFrames src) { var dest = new ColorAnimationUsingKeyFrames(); foreach ( var kf in src.KeyFrames) { dest.KeyFrames.Add(kf.Clone()); } return dest; } public static ColorKeyFrame Clone( this ColorKeyFrame src) { ColorKeyFrame dest = null ; if (src is LinearColorKeyFrame) dest = new LinearColorKeyFrame(); if (src is DiscreteColorKeyFrame) dest = new DiscreteColorKeyFrame(); if (src is EasingColorKeyFrame) dest = new EasingColorKeyFrame(); if (src is SplineColorKeyFrame) dest = new SplineColorKeyFrame(); if (dest != null ) { dest.KeyTime = src.KeyTime; dest.Value = src.Value; } return dest; } public static DoubleAnimationUsingKeyFrames Clone( this DoubleAnimationUsingKeyFrames src) { var dest = new DoubleAnimationUsingKeyFrames(); foreach ( var kf in src.KeyFrames) { dest.KeyFrames.Add(kf.Clone()); } return dest; } public static DoubleKeyFrame Clone( this DoubleKeyFrame src) { DoubleKeyFrame dest = null ; if (src is LinearDoubleKeyFrame) dest = new LinearDoubleKeyFrame(); if (src is DiscreteDoubleKeyFrame) dest = new DiscreteDoubleKeyFrame(); if (src is EasingDoubleKeyFrame) dest = new EasingDoubleKeyFrame(); if (src is SplineDoubleKeyFrame) dest = new SplineDoubleKeyFrame(); if (dest == null ) return null ; dest.KeyTime = src.KeyTime; dest.Value = src.Value; return dest; } public static ObjectAnimationUsingKeyFrames Clone( this ObjectAnimationUsingKeyFrames src) { var dest = new ObjectAnimationUsingKeyFrames(); foreach ( var kf in src.KeyFrames) { dest.KeyFrames.Add(kf.Clone()); } return dest; } public static ObjectKeyFrame Clone( this ObjectKeyFrame src) { ObjectKeyFrame dest = null ; if (src is DiscreteObjectKeyFrame) dest = new DiscreteObjectKeyFrame(); if (dest == null ) return null ; dest.KeyTime = src.KeyTime; dest.Value = src.Value; return dest; } public static PointAnimationUsingKeyFrames Clone( this PointAnimationUsingKeyFrames src) { var dest = new PointAnimationUsingKeyFrames(); foreach ( var kf in src.KeyFrames) { dest.KeyFrames.Add(kf.Clone()); } return dest; } public static PointKeyFrame Clone( this PointKeyFrame src) { PointKeyFrame dest = null ; if (src is LinearPointKeyFrame) dest = new LinearPointKeyFrame(); if (src is EasingPointKeyFrame) dest = new EasingPointKeyFrame(); if (src is DiscretePointKeyFrame) dest = new DiscretePointKeyFrame(); if (src is SplinePointKeyFrame) dest = new SplinePointKeyFrame(); if (dest == null ) return null ; dest.KeyTime = src.KeyTime; dest.Value = src.Value; return dest; } } |
■アニメーションの開始点と終了点を変更する拡張メソッドを作る
アニメーションをする対象と、開始終了点を指定するのですが、色々ややこしいので拡張メソッドを用意しておきます。本当は別クラスのほうがいいんでしょうが、面倒なので Storyboard にくっつけてしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /// <summary> /// 各種の値を変えるための拡張メソッド /// </summary> public static class StorybaordValueExtentions { /// <summary> /// ターゲット名を指定する /// </summary> /// <param name="sb"></param> /// <param name="name"></param> public static void SetTarget( this Storyboard sb, UIElement el) { foreach ( var tl in sb.Children) { Storyboard.SetTarget(sb, el); } } public static void SetTargetName( this Storyboard sb, string name) { foreach ( var tl in sb.Children) { Storyboard.SetTargetName(sb, name); } } public static void SetMovePoint( this Storyboard sb, Point start, Point end) { ((DoubleAnimationUsingKeyFrames)sb.Children[0]).KeyFrames[0].Value = start.X; ((DoubleAnimationUsingKeyFrames)sb.Children[1]).KeyFrames[0].Value = start.Y; ((DoubleAnimationUsingKeyFrames)sb.Children[0]).KeyFrames[1].Value = end.X; ((DoubleAnimationUsingKeyFrames)sb.Children[1]).KeyFrames[1].Value = end.Y; } } |
storyboard は複数のターゲットを同時に動かすこともできるので、一律で変えてしまうのは困るのですが、まあ、そのときはそのときで考えるということで。
■実際に動かしてみる
あらかじめ、コピー元の storyboard を blend で作っておきます。
1 2 3 4 5 6 7 8 9 10 11 12 | < Page.Resources > < Storyboard x:Name = "sbMove" > < DoubleAnimationUsingKeyFrames Storyboard.TargetProperty = "(UIElement.RenderTransform).(CompositeTransform.TranslateX)" Storyboard.TargetName = "pictAni" > < EasingDoubleKeyFrame x:Name = "sbStartX" KeyTime = "0" Value = "180.597" /> < EasingDoubleKeyFrame x:Name = "sbEndX" KeyTime = "0:0:1" Value = "182.09" /> </ DoubleAnimationUsingKeyFrames > < DoubleAnimationUsingKeyFrames Storyboard.TargetProperty = "(UIElement.RenderTransform).(CompositeTransform.TranslateY)" Storyboard.TargetName = "pictAni" > < EasingDoubleKeyFrame x:Name = "sbStartY" KeyTime = "0" Value = "-152.239" /> < EasingDoubleKeyFrame x:Name = "sbEndY" KeyTime = "0:0:1" Value = "171.642" /> </ DoubleAnimationUsingKeyFrames > </ Storyboard > </ Page.Resources > |
アニメーションする対象は、あらかじめ、Image.RenderTransform と CompositeTransform を書いておきます。
これは Blend が出力する Storyboard の癖なので、手作業で作れば Margin とか Canvas.Left あたりを直接変更することも可能でしょう。今は、Blend と Visual Studio の行き来を簡単にするということで、Blend のほうにあわせておきます。
1 2 3 4 5 6 7 | < Image x:Name = 'pict1' HorizontalAlignment = "Left" Height = "100" VerticalAlignment = "Top" Width = "64" Source = "/Images/FC097-2.png" Margin = "288,151,0,0" > < Image.RenderTransform > < CompositeTransform /> </ Image.RenderTransform > </ Image > |
自作した Clone と拡張メソッドを使って、pict1 を移動させます。ここでは、Clone した storyboard は使い捨てになっていますが、実際は Initialize 時にキープしておきます。
1 2 3 4 5 6 7 8 | private void SbClone2Click( object sender, RoutedEventArgs e) { Storyboard sb = this .sbMove.Clone(); // ターゲットを変える sb.SetTarget( this .pict1 ); sb.SetMovePoint( new Point(0, 0), new Point(100, 200)); sb.Begin(); } |
これで、storyboard の XAML を量産しないでよくなるので(特に開始終了点の名前付けとかが面倒なので)、花札ゲームのアニメーションに活用できそう。なので、再び花札ゲーム製作に戻るということで。