我正在学习WPF中的动画,所以我决定创建一个模拟时钟。
请查看我的代码,以便我能清楚地解释我的问题:
<Window x:Class="Animation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:self="clr-namespace:Animation"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<self:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<self:SecondsToAngleConverter x:Key="secondsToAngleConverter" />
<self:HoursToAngleConverter x:Key="hoursToAngleConverter" />
</Window.Resources>
<Canvas>
<Ellipse Height="150" Width="150" Fill="Orange" />
<TextBlock Visibility="Collapsed" Text="{Binding DataContext.Second, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource secondsToAngleConverter}}" Width="160" Canvas.Left="10" Canvas.Top="190" />
<Line x:Name="secondHand" X1="75" Y1="75" X2="75" Y2="10" Stroke="Red" RenderTransformOrigin="1,1">
<Line.RenderTransform>
<RotateTransform x:Name="secondHandRotateTransform"/>
</Line.RenderTransform>
<Line.Triggers>
<EventTrigger RoutedEvent="Line.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="secondHandRotateTransform" Storyboard.TargetProperty="Angle"
To="{Binding DataContext.Second, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource secondsToAngleConverter}}"
Duration="0:0:1"/>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="secondHandRotateTransform" Storyboard.TargetProperty="Angle" Duration="0:0:1" RepeatBehavior="Forever" IsCumulative="True">
<DiscreteDoubleKeyFrame Value="6" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Line.Triggers>
</Line>
<Line X1="75" Y1="75" X2="75" Y2="10" Stroke="Black" StrokeThickness="3" RenderTransformOrigin="1,1">
<Line.RenderTransform>
<RotateTransform x:Name="minuteHandRotateTransform"/>
</Line.RenderTransform>
<Line.Triggers>
<EventTrigger RoutedEvent="Line.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="minuteHandRotateTransform" Storyboard.TargetProperty="Angle"
To="{Binding DataContext.Minute, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource secondsToAngleConverter}}"
Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="minuteHandRotateTransform" Storyboard.TargetProperty="Angle"
IsCumulative="True" By="6" RepeatBehavior="Forever"
Duration="0:1:0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Line.Triggers>
</Line>
<Line X1="75" Y1="75" X2="75" Y2="25" Stroke="Black" StrokeThickness="3" RenderTransformOrigin="1,1">
<Line.RenderTransform>
<RotateTransform x:Name="hourHandRotateTransform"/>
</Line.RenderTransform>
<Line.Triggers>
<EventTrigger RoutedEvent="Line.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="hourHandRotateTransform" Storyboard.TargetProperty="Angle"
To="{Binding DataContext.Hour, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource hoursToAngleConverter}}"
Duration="0:0:1"/>
<DoubleAnimation Storyboard.TargetName="hourHandRotateTransform" Storyboard.TargetProperty="Angle"
IsCumulative="True" By="30" RepeatBehavior="Forever"
Duration="1:0:0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Line.Triggers>
</Line>
</Canvas>
</Window>
ViewModel代码:
public class MainWindowViewModel
{
public MainWindowViewModel()
{
Hour = DateTime.Now.Hour;
Minute = DateTime.Now.Minute;
Second = DateTime.Now.Second;
}
public static int Hour { get; set; }
public static int Minute { get; set; }
public static int Second { get; set; }
}
转换器代码:
SecondsToAngleConverter.cs
:
public class SecondsToAngleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || value == DependencyProperty.UnsetValue)
{
return (6 * DateTime.Now.Second);
}
else
{
return (6 * (int)value);
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
HoursToAngleConverter.cs
:
public class HoursToAngleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || value == DependencyProperty.UnsetValue)
{
if (DateTime.Now.Hour > 12)
{
return (30 * (DateTime.Now.Hour - 12));
}
else
{
return (30 * DateTime.Now.Hour);
}
}
else
{
if ((int)value > 12)
{
return (30 * ((int)value - 12));
}
else
{
return (30 * (int)value);
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
情景:
DateTime.Now
,然后这些动画停止。问题:
程序启动时
DateTime.Now.Second
返回的值大于0,那么秒针将需要不到60秒的时间才能达到12。但与此同时,分针的动画时钟不会达到1分钟,因此分针的位置与物理时钟的位置会有所不同。类似地
DateTime.Now.Minute
返回的值大于0,那么分针将需要不到60分钟的时间才能达到12。但同时时针的动画时钟不会达到1小时,所以时针的位置会比实际时钟有所不同。答案 0 :(得分:1)
样本1 :设计模拟时钟的基本简单方法。
<Grid> <Rectangle Fill="Black" Height="5" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" > <Rectangle.RenderTransform> <RotateTransform x:Name="hrHand" /> </Rectangle.RenderTransform> <Rectangle.Triggers> <EventTrigger RoutedEvent="Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="12:0:0" To="360" RepeatBehavior="Forever" Storyboard.TargetName="hrHand" Storyboard.TargetProperty="Angle"/> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> <Rectangle Fill="Black" Height="2" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" > <Rectangle.RenderTransform> <RotateTransform x:Name="minHand" /> </Rectangle.RenderTransform> <Rectangle.Triggers> <EventTrigger RoutedEvent="Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="1:0:0" To="360" RepeatBehavior="Forever" Storyboard.TargetName="minHand" Storyboard.TargetProperty="Angle"/> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> <Rectangle Fill="Red" Height="1" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" > <Rectangle.RenderTransform> <RotateTransform x:Name="sechand"/> </Rectangle.RenderTransform> <Rectangle.Triggers> <EventTrigger RoutedEvent="Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:1:0" RepeatBehavior="Forever" To="360" Storyboard.TargetName="sechand" Storyboard.TargetProperty="Angle"/> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> </Grid>
样本2 :使用当前时间进场
xaml code :ContentControl和窗口资源中的代码仅用于设计目的。
<Window x:Class="WpfApplication8.Clock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Clock" Height="900" Width="1600">
<Window.Resources>
<Grid x:Key="DesignGrid" Height="500" Width="500" >
<Grid.Clip>
<EllipseGeometry RadiusX="500" Center="250,250" RadiusY="500"/>
</Grid.Clip>
<Ellipse Height="900" Width="900" Fill="White"/>
<Rectangle Height="2" Fill="Black"/>
<Rectangle Height="2" Fill="Black" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle="30"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Height="2" Fill="Black" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle="60"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Height="2" Fill="Black" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle="90"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Height="2" Fill="Black" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle="120"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Height="2" Fill="Black" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle="150"/>
</Rectangle.RenderTransform>
</Rectangle>
<Ellipse Height="400" Width="400" Fill="White"/>
</Grid>
</Window.Resources>
<Grid Name="gd">
<Grid.Resources>
<Storyboard x:Key="animation">
<DoubleAnimation Duration="0:1:0" RepeatBehavior="Forever" Storyboard.TargetName="hand" Storyboard.TargetProperty="Angle"/>
<DoubleAnimation Duration="1:0:0" RepeatBehavior="Forever" Storyboard.TargetName="minHand" Storyboard.TargetProperty="Angle"/>
<DoubleAnimation Duration="12:0:0" RepeatBehavior="Forever" Storyboard.TargetName="hrHand" Storyboard.TargetProperty="Angle"/>
</Storyboard>
</Grid.Resources>
<ContentControl Content="{StaticResource DesignGrid}"></ContentControl>
<Grid Margin="200,0,0,0" Height="500" Width="500" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle Fill="Black" Height="5" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" >
<Rectangle.RenderTransform>
<RotateTransform x:Name="hrHand" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Fill="Black" Height="2" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" >
<Rectangle.RenderTransform>
<RotateTransform x:Name="minHand" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Fill="Red" Height="1" Width="200" HorizontalAlignment="Center" VerticalAlignment="Center" >
<Rectangle.RenderTransform>
<RotateTransform x:Name="hand"/>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
</Grid>
c#code
InitializeComponent();
this.Loaded += Clock_Loaded;
}
void Clock_Loaded(object sender, RoutedEventArgs e)
{
var sb = gd.Resources["animation"] as Storyboard;
var sec = sb.Children[0] as DoubleAnimation;
var min = sb.Children[1] as DoubleAnimation;
var hr = sb.Children[2] as DoubleAnimation;
sec.From = DateTime.Now.Second * 6 - 90;
min.From = (DateTime.Now.Minute + ((double)DateTime.Now.Second / 60.0)) * 6 - 90;
hr.From = (DateTime.Now.Hour + ((double)DateTime.Now.Minute / 60.0)) * 30 - 90;
sec.To = sec.From + 360;
min.To = min.From + 360;
hr.To = hr.From + 360;
sb.Begin();
}
<强>解释强>
<强>结果强>
答案 1 :(得分:0)
你应该做普通模拟时钟所做的事情,并在一个完整的圆圈上使用计时器。因此,不要让你的分针每分钟进6度,你应该让它每小时360度。而不是你的时针每小时30度,它应该每12小时360度。
如果你希望你的时钟采用经典的方法,其中分针每分钟只更新一次(但你仍然希望它在正确的时间跳转,而不是在每分钟过后),你需要检查每一个转换如果DateTime.Now.Second == 0
。如果是,则返回值,否则返回Binding.DoNothing
。
答案 2 :(得分:0)
我必须再使用两个doubleAnimations和另外两个转换器。代码如下:
从上面的代码我需要在第二个故事板中添加一个双动画,如下所示:
<DoubleAnimation Storyboard.TargetName="minuteHandRotateTransform" Storyboard.TargetProperty="Angle"
By="{Binding DataContext.Second, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource synchronizeMinuteHandConverter}}"
Duration="0:0:1" />
同样适用于第三个故事板:
<DoubleAnimation Storyboard.TargetName="hourHandRotateTransform" Storyboard.TargetProperty="Angle"
By="{Binding DataContext.Minute, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Converter={StaticResource synchronizeHourHandConverter}}"
Duration="0:0:1"/>
SynchronizeMinuteHandConverter.cs:
public class SynchronizeMinuteHandConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || value == DependencyProperty.UnsetValue)
{
return (DateTime.Now.Second / 10);
}
else
{
if ((int)value > 59)
{
value = (int)value - 60;
}
return ((int)value / 10);
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
SynchronizeHourHandConverter.cs
public class SynchronizeHourHandConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || value == DependencyProperty.UnsetValue)
{
return (DateTime.Now.Minute / 2);
}
else
{
if ((int)value > 59)
{
value = (int)value - 60;
}
return ((int)value / 2);
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
答案 3 :(得分:0)
这是一种生成平滑模拟扫描秒针的简便方法。矩形的角度是动画的,其双重动画应用于它的旋转角度属性。将分钟和秒钟覆盖类似的代码 产生一个基本时钟。
<Page.Resources>
<Storyboard x:Name="myStoryboard">
<DoubleAnimationUsingKeyFrames
RepeatBehavior="Forever"
Storyboard.TargetName="ellipse1"
Storyboard.TargetProperty="(Ellipse.RenderTransform).(CompositeTransform.Rotation)">
<LinearDoubleKeyFrame Value="-90" KeyTime="0:0:0"/>
<LinearDoubleKeyFrame Value="270" KeyTime="0:0:59"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Rectangle x:Name="ellipse1"
Width="320" Height="10" Fill="Red" RenderTransformOrigin="0.5,0.5">
<Rectangle.Clip>
<RectangleGeometry Rect="110,0,320,320"/>
</Rectangle.Clip>
<Rectangle.RenderTransform>
<CompositeTransform Rotation="-90" />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle VerticalAlignment="Center" HorizontalAlignment="Center" Stroke="Black"
Width="640" Height="1"></Rectangle>
<Rectangle VerticalAlignment="Center" HorizontalAlignment="Center" Stroke="Black"
Width="1" Height="640"></Rectangle>
</Grid>