I'm messing around with making a clock in silverlight. I am trying to set up the animation code programmatically because I want to reuse the same code for each of the 3 clock hands (and I don't think I can do this with a single storyboard in xaml).
public void Rotate(double toAngle, RotateTransform rotate)
{
Storyboard sb = new Storyboard();
DoubleAnimationUsingKeyFrames keyframes = new DoubleAnimationUsingKeyFrames();
EasingDoubleKeyFrame easingStart = new EasingDoubleKeyFrame();
easingStart.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0));
easingStart.Value = rotate.Angle;
EasingDoubleKeyFrame easingEnd = new EasingDoubleKeyFrame();
easingEnd.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.5));
easingEnd.Value = toAngle;
var ease = new ElasticEase();
ease.EasingMode = EasingMode.EaseIn;
easingEnd.EasingFunction = ease;
keyframes.KeyFrames.Add(easingStart);
keyframes.KeyFrames.Add(easingEnd);
Storyboard.SetTarget(keyframes, rotate);
Storyboard.SetTargetProperty(keyframes, new PropertyPath("(RotateTransform.Angle)"));
sb.Children.Add(keyframes);
sb.Begin();
}
I pass in both the angle I want the current hand to rotate to and the rotate transform for that hand. From this rotate transform I get the starting angle.
When I run the clock, the clock hands move (the second hand moves to the correct spot each second etc) but the animations don't appear to actually animate. They just go from start to end instantly.
Why does the animation not happen properly?
The skipping problem is likely the lag in starting a storyboard that wants to show changes in less than a second. By the time the storyboard gets itself ready to display it basically says "oh... it's after 1 second now, where should I be now". You are better off with longer storyboards.
I'm not sure how much control you are wanting over your clock, but regarding using a single storyboard, here goes...
I was not sure of the overall problem you are trying to solve, but the following example uses one simple storyboard to run the 3 hands of a clock. The only problem I had was that the maximum time interval I could specify for the hour hand (in XAML), without a parsing error, is 23:59:59.
<Storyboard x:Name="SecondHandStoryboard" RepeatBehavior="Forever">
<DoubleAnimation Duration="0:1:0" To="360" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="SecondHand" />
<DoubleAnimation Duration="1:0:0" To="360" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="MinuteHand"/>
<DoubleAnimation Duration="23:59:59" To="360" Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.Rotation)" Storyboard.TargetName="HourHand"/>
</Storyboard>
I actually wanted to add a solid "tick" animation to the second hand, but have not figured out how to get rotation "by" storyboards to work. Basically use a repeated 6 degree per-second 1-second long storyboard with steep easing-in (will keep looking into that problem).
Anyway, hope this is of help to you.
Related
I am writing a UWP WinRT app in C#. I found the very useful BitmapIcon class and use it for a grid of icons on the main page of my app. The BitmapIcon class has a Foreground brush that can be used to override the color of the original bitmap which is handy for when something controls the icon colors separate from the picture itself (like a server dictating that an icon be red to show that it's important).
I am using a storyboard animation to change the icon colors (for a flashing effect that I know is not liked these days but I am forced to do it). Here's the code:
ColorAnimationUsingKeyFrames colorAnimation = new ColorAnimationUsingKeyFrames();
colorAnimation.Duration = TimeSpan.FromSeconds( 3 );
colorAnimation.EnableDependentAnimation = true;
IconImage.Foreground = new SolidColorBrush( iconColor ?? AppSettings.appColor );
DiscreteColorKeyFrame key1 = new DiscreteColorKeyFrame();
key1.Value = finishColor;
key1.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds( 0 ) );
colorAnimation.KeyFrames.Add( key1 );
LinearColorKeyFrame key2 = new LinearColorKeyFrame();
key2.Value = startColor;
key2.KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds( 3.0 ) );
colorAnimation.KeyFrames.Add( key2 );
colorAnimation.RepeatBehavior = RepeatBehavior.Forever;
Storyboard.SetTargetProperty( colorAnimation, "(BitmapIcon.Foreground).Color" );
Storyboard.SetTarget( colorAnimation, IconImage );
// Create a storyboard to apply the animation.
//Storyboard myStoryboard = new Storyboard();
animationStoryboard.Children.Add( colorAnimation );
animationStoryboard.Duration = colorAnimation.Duration;
animationStoryboard.RepeatBehavior = colorAnimation.RepeatBehavior;
animationStoryboard.Begin();
This seems to somewhat work. I get no exceptions and the colors do change. The problem is that the changes do not show up in the window. If I resize the app window, the colors can be seen to change as I drag the edge of the window. When I stop, they stop showing a change but the change appears to stil lbe happening in the background invisibly. I can tell that the numbers are changing in the background because of the jump in colors that happen when I stop changing the size of the window for a moment.
It's been a while since I worked on C# UWP WinRT code and I think I'm missing some sort of attribute or property of the Imageicon object, it's Foreground SolidColorBrush, or the Color associated with the SolidColorBrush.
Elsewhere in my code, I use similar methods to animate an opening and closing sidebar on the window. That works and the animation is smooth, as expected.
I need to figure out why the color change seems to happen but without the UI getting updated constantly.
How can I get the UI to update when animating a color change
I could reproduce this issue, and I have discussed with team, it is a bug, I reported it to related team just now. And you could also post this with Windows feed back hub app.
If I receive any info about this, I will update below. thx!
Update
This isn’t ideal, as it involves 2 animations and 2 separate BitmapIcons, we may be able to tweak it to our liking by only using 1 Animation and having a static image behind the BitmapIcon. Please check the following code.
<Grid>
<BitmapIcon
x:Name="IconImageOne"
Foreground="Red"
UriSource="ms-appx:///Assets/StoreLogo.png">
<BitmapIcon.Resources>
<Storyboard x:Name="animationStoryboard">
<DoubleAnimation
AutoReverse="True"
RepeatBehavior="Forever"
Storyboard.TargetName="IconImageOne"
Storyboard.TargetProperty="Opacity"
From="1.0"
To="0.0"
Duration="0:0:3" />
<DoubleAnimation
AutoReverse="True"
RepeatBehavior="Forever"
Storyboard.TargetName="IconImageTwo"
Storyboard.TargetProperty="Opacity"
From="0.0"
To="1.0"
Duration="0:0:3" />
</Storyboard>
</BitmapIcon.Resources>
</BitmapIcon>
<BitmapIcon
x:Name="IconImageTwo"
Foreground="Green"
Tapped="IconImage_Tapped"
UriSource="ms-appx:///Assets/StoreLogo.png" />
</Grid>
Code behind
private void StatAnimation()
{
animationStoryboard.Begin();
}
private void IconImage_Tapped(object sender, TappedRoutedEventArgs e)
{
StatAnimation();
}
I'm posting this untested answer because I think it is a viable option and might work as well as the workaround that I used (and mentioned as a comment on my original post)...
Set up the code to animate the color in case that ever works in the future. Then also add an animation to alter the opacity of the BitmapIcon between 1.0 and 0.99999, or some other similar values. The Opacity change should cause a refresh of the view/screen while not actually changing the opacity enough for the user to see it. The color change will then be visible because of the opacity update.
I have not tested this since I had to move on after using the two-Bitmapicon workaround.
If the user starts the app there should be a annimated background. The Background of the Maingrid is that picture above. The background should be moved. How can I do this in Visual Blend? I'm aware of how to create annimations in blend but I don't know how to do this kind of annimation.
I believe you are looking for something along the lines of this:
<Canvas.Resources>
<Storyboard x:Name="myStoryboard">
<!-- The PointAnimation has a name so it can be accessed
from code. The To property is left out of the XAML
because the value of To is determined in code. -->
<PointAnimation
x:Name="clockAnimation"
Storyboard.TargetProperty="Center"
Storyboard.TargetName="clockImage"
Duration="0:0:20"/>
</Storyboard>
</Canvas.Resources>
And this C# which can be done on a timer or on initialisation:
double newX = 'Where you want the new X coordinate of the clock';
double newY = 'Where you want the new Y coordinate of the clock';
Point myPoint = new Point();
myPoint.X = newX;
myPoint.Y = newY;
clockAnimation.To = myPoint;
myStoryboard.Begin();
I hope this works for you!
What you are asking to do (as i understand it) is impossible without modifying the image you've shown fundamentally.
From the sounds of it what you're looking to do is animate the hands in this static two-dimensional image. You can no more move the hands on the clock of this static image as you could if you were to print it out onto a sheet of paper and attempt the same.
One solution would be to use an image-editing tool such as photoshop to erase the hands in question leaving you with an empty clock-face.
From there you can use it as the background as you intend, but create assets on the layer(s) above the now modified image that replicate what the old clock-hands used to look like and animate them accordingly.
(For example; something that should be as simple as making 3 separate animations that have each hand turn 360 degrees, only with the second-hand's animation taking 60seconds to complete, the minute-hand taking 1hour to complete and of course the hour hand taking 12hours to complete its spin (but you said you're competent with animation so i'll leave this part to you).)
I've spent about the entire day looking at examples of XAML storyboarding, but WinRT/Windows8 apps doesn't work the same way WPF does (At least I think), and I'm all confused.
All I want is a button that moves 100px left and 100px up when clicked. I'm having the hardest time figuring this out, and I know that once I get something I want working, I'll be able to work from there.
Also, if anyone can teach me how to use "Storyboard.TargetProperty" That'd be amazing.
<Rectangle
Name="Rectangle01"
Height="100"
Width="100"
Fill="Red">
<Rectangle.Resources>
<Storyboard x:Name="myboard">
<DoubleAnimation
Storyboard.TargetName="Rectangle01"
Storyboard.TargetProperty="Width"
From="100" To="3600" Duration="0:0:6" />
</Storyboard>
</Rectangle.Resources>
</Rectangle>
Here's an example of something I tried doing just to mess around with storyboards. This didn't throw an error until I tried to execute it, but it still doesn't work.
So yea, WRT is a little different in some ways, but many not.
What you're wanting to do is move your Button around, up and left so logically on an X and Y axis right? So in my mind, I think RenderTransform/TranslateTransform to leverage the X and Y like;
<Button Click="StartTheMove">
<Button.RenderTransform>
<TranslateTransform x:Name="MoveFoo"/>
</Button.RenderTransform>
</Button>
So now we have MoveFoo and setup our Transform, and since the only default Routed.Event I know of for WRT is Loaded we can't do that, so tie into the Click handler with StartTheMove like;
private void StartTheMove(object sender, RoutedEventArgs e)
{
MoveTheButton.Begin();
}
So now we've got our handler and we want to fire off MoveTheButton which will be your actual storyboard set as a resource somewhere in say your window.resources or whatever like;
<Storyboard x:Name="MoveTheButton">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="MoveFoo"
Storyboard.TargetProperty="Y">
<SplineDoubleKeyFrame KeyTime="0:0:1.25" Value="100" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="MoveFoo"
Storyboard.TargetProperty="X">
<SplineDoubleKeyFrame KeyTime="0:0:1.25" Value="-100" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
So once we hit our Storyboard it's then going to go and tell our Transform that we want the properties of X and Y to get some new properties declared (here's your Storyboard.TargetProperty coming in to join the fun) and we're going to set the values to those two properties to something other than what they already are and basically tell it we want it to move 100px up on the Y (up) and -100px on the X (left) of our 2d axis. Which you'll also prob want to play with the keytimes for however fast/slow you want the effect to happen.
While I didn't have time to test this and am kind of just adhoc throwing it together before I leave the office for the day, we've all been where you are and hopefully it helps. You should also be able to add the interaction behaviors to kind of leverage the same type of event triggers you're probably more used to (see tutorials) and handle your event that way to ditch your handler code behind.
However hopefully this helps, if not I'll see it again in the morning and will take another stab at it. Cheers!
I'm trying to make a simple Windows Store game in C# and XAML, one that involves hexagonal tiles moving about. This is mostly to help me learn C# and XAML as I've never worked with graphics or even UI coding before.
I've got a method that can move a single hex to the target coordinates, but looking at it now I realize that it is impossible to do multiple moves at once, which is absolutely necessary.
I feel like there's got to be something fundamentally off in my approach, multiple objects moving about a single canvas cannot be an unusual thing, can it? I'm mostly asking this in the hope that someone will point out where I went wrong.
//moves the hex hexName to coordinates x, y, over a specified duration.
public void slideHex(int x, int y, string hexName, Duration duration)
{
GameStoryboard.Stop();
Polygon hex = GameCanvas.FindName(hexName) as Polygon;
TranslateTransform slideTransform = new TranslateTransform();
slideTransform.X = hex.RenderTransformOrigin.X;
slideTransform.Y = hex.RenderTransformOrigin.Y;
hex.RenderTransform = slideTransform;
DoubleAnimation animX = new DoubleAnimation();
DoubleAnimation animY = new DoubleAnimation();
animX.Duration = duration;
animY.Duration = duration;
GameStoryboard.Duration = duration;
GameStoryboard.Children.Add(animX);
GameStoryboard.Children.Add(animY);
Storyboard.SetTarget(animX, slideTransform);
Storyboard.SetTarget(animY, slideTransform);
Storyboard.SetTargetProperty(animX, "X");
Storyboard.SetTargetProperty(animY, "Y");
animX.To = x;
animY.To = y;
GameStoryboard.Begin();
}
A storyboard can contain multiple animations, and each animation can target a different UI element. Here's an example of a storyboard which "pulses" the border colours of three different controls:
<Storyboard x:Name="pulseAnimation" AutoReverse="True">
<ColorAnimation x:Name="animateLatitudeTextBoxBorderColour" Storyboard.TargetName="textBoxLatitude" From="{StaticResource PhoneTextBoxColor}" To="Green" Storyboard.TargetProperty="(TextBox.BorderBrush).(SolidColorBrush.Color)" Duration="0:0:0.4" />
<ColorAnimation x:Name="animateLongitudeTextBoxBorderColour" Storyboard.TargetName="textBoxLongitude" From="{StaticResource PhoneTextBoxColor}" To="Green" Storyboard.TargetProperty="(TextBox.BorderBrush).(SolidColorBrush.Color)" Duration="0:0:0.4" />
<ColorAnimation x:Name="animateHyperlinkTextColour" Storyboard.TargetName="hyperlinkButtonCurrentLocation" From="{StaticResource PhoneForegroundColor}" To="Green" Storyboard.TargetProperty="(HyperlinkButton.Foreground).(SolidColorBrush.Color)" Duration="0:0:0.4" />
</Storyboard>
Your code looks fine - you're already animating multiple properties of slideTransform, and since the target of an animation is a property of the animation rather than the storyboard, there's no reason why you couldn't retarget either animX or animY to a different object altogether.
You can create multiple storyboards and execute them concurrently in order to simultaneously animate multiple objects. See the example in the following article which animates multiple items within a to-do list:
http://www.codeproject.com/Articles/428088/A-Gesture-Driven-Windows-Phone-To-Do-List#swipe
When I move a bitmap-filled path in Silverlight by updating its RenderTransform, the image updates with a stutter/jitter about every half-second.
The Path uses a BitmapCache, and Hardware Acceleration is turned on. The hardware acceleration is verified using the EnableCacheVisualization option, and by observing that the frame rate is high ~ 60 fps.
You can see what I am talking about here. It is a working silverlight app showing the stuttering behavior. (You might imagine what game I am trying to make when you see the image...)
http://glerok.com/smb/index.html
You will notice that the bitmap scrolls smoothly, but every 0.5 to 1 seconds, it stutters, as if some frames were skipped. I verified this on two different PCs.
Is this expected behavior in silverlight, or am I doing something wrong by manually updating the RenderTransform of the object? (Please don't take this as an opportunity to disparage Silverlight...)
Thanks in advance!!!
Here is the code I am using to generate the path:
RectangleGeometry rg = new RectangleGeometry();
BitmapImage bi = new BitmapImage(new Uri("world.png", UriKind.Relative));
int WorldWidth = 703;
int WorldHeight = 240;
rg.Rect = new Rect(WorldTilePosition, 0, WorldWidth * Scale, WorldHeight * Scale);
Path p = new Path { RenderTransform = new TranslateTransform { X = 0, Y = 0 } };
p.Data = rg;
p.CacheMode = new BitmapCache();
p.Fill = new ImageBrush { ImageSource = bi };
canvas.Children.Add(p);
And here is the code that update the path position:
WorldTilePosition-=10;
TranslateTransform tt = WorldPath.RenderTransform as TranslateTransform;
tt.X = -WorldTilePosition;
if (WorldTilePosition < -1000) WorldTilePosition = 1000;
I observed the stuttering/jittering in Silverlight even for an object that is animated with a storyboard. I guess this happens because there is currently no back-buffering in silverlight, making it currently unsuitable for even simple sprite-based games on PC? Note that the overall screen refresh rate is still reported to be 60 fps.
The code below generates an animated rectangle that flickers during the screen refresh. Vertical objects show better the banding during the refresh rate, at least on my laptop.
<Canvas x:Name="LayoutRoot2" Background="White">
<Rectangle x:Name="myRect" Fill="Red" Width="100" Height="500" CacheMode="BitmapCache" >
<Rectangle.RenderTransform>
<TranslateTransform x:Name="myRectTransform" X="0" />
</Rectangle.RenderTransform>
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.Loaded">
<BeginStoryboard>
<Storyboard x:Name="myStoryboard" >
<DoubleAnimation
From="0"
To="1000"
Storyboard.TargetName="myRectTransform"
Storyboard.TargetProperty="X"
Duration="0:0:1"
AutoReverse="True"
RepeatBehavior="Forever"
/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
</Canvas>
Can't notice any stuttering on my PC (Core 2 Quad 3 GHz, Win7 x64, IE9).
Especially for such a tile based game with low resolution sprites etc. I'd consider using a WriteableBitmap (a nice extended version can be found on Codeplex) and then update/display that one only. This also solves the missing backbuffer.
Also, to avoid noticeable stuttering, try to keep your game logic independent of framerate (using a timer or detecting the number of milliseconds passed since last frame). Skipped frames shouldn't be as noticeable then as well.
Also try to draw the visible part of the landscape only (not sure if clipping is enough; didn't try it).