使用秒针同步分针以及使用分针同步时针

时间:2014-08-03 22:51:19

标签: c# wpf animation

我正在学习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();
    }
}

情景:

  1. 秒,分钟&amp;小时动画1秒动画,将它们设置为正确的DateTime.Now,然后这些动画停止。
  2. 现在开始动画制作秒针,分针和时针的动画。
  3. 这些动画并不相互依赖。
  4. 问题:

    程序启动时

    1. 分针开始的动画,其持续时间为1分钟,右侧旋转6度
    2. 如果DateTime.Now.Second返回的值大于0,那么秒针将需要不到60秒的时间才能达到12。但与此同时,分针的动画时钟不会达到1分钟,因此分针的位置与物理时钟的位置会有所不同。
    3. 类似地

      1. 时针开始的动画,其持续时间为1小时,右侧旋转30度
      2. 如果DateTime.Now.Minute返回的值大于0,那么分针将需要不到60分钟的时间才能达到12。但同时时针的动画时钟不会达到1小时,所以时针的位置会比实际时钟有所不同。

4 个答案:

答案 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>
     

enter image description here


  

样本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#c​​ode

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();
    }

<强>解释 enter image description here enter image description here


<强>结果

enter image description here

答案 1 :(得分:0)

你应该做普通模拟时钟所做的事情,并在一个完整的圆圈上使用计时器。因此,不要让你的分针每分钟进6度,你应该让它每小时360度。而不是你的时针每小时30度,它应该每12小时360度。

  1. 计算您的分针经过一小时开始的度数,最多为.1度(1秒)并将其移动到那里。数学是:((分* 60)+秒)/ 10.
  2. 一旦在那里,开始一个1小时的计时器,使分针成为360度,每秒.1度。
  3. 如果您希望计时器首先进入1小时,则需要先根据未计算的度数和未来的部分时间启动单独的计时器。
  4. 如果你希望你的时钟采用经典的方法,其中分针每分钟只更新一次(但你仍然希望它在正确的时间跳转,而不是在每分钟过后),你需要检查每一个转换如果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>