Help us understand the problem. What is going on with this article?

WPFのアニメーション機能を使い針を滑らかに動かすアナログ時計を作成する

More than 1 year has passed since last update.

はじめに

WPFのDispatcherTimerクラスを使い簡易アナログ時計を作成する」でDispatcherTimerを使った簡易アナログ時計のサンプルコードを掲載しましたが、こちらは、WPFのアニメーション機能を使った簡易アナログ時計のサンプルです。

実行例

画像ではわかりませんが、秒針が滑らかに動きます。

2019-06-01 10_07_29.png

処理の概要

概要は以下のとおり。

  • 秒針、分針、時針の3つをLineオブジェクトとして表す。
  • 3つのLineオブジェクトの、RenderTransformプロパティには、RotateTransformを設定する。
  • 秒針、分針、時針に対応する Storyboard を定義する。
  • Storyboardでは、上記RotateTransformの回転角度を変化させることでそれぞれの針を動かす。
  • WindowのLoad時に、3つの針の角度を初期化し、アニメーションを開始する。

XAML

XAMLを示します。

Storyboardを3つ定義しているので、XAMLの行数が多めですが、ひとつひとつの要素はそれほど複雑ではありません。

<Window x:Class="WpfAnalogClock2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfAnalogClock2"
        mc:Ignorable="d"
        Title="Clock" Height="260" Width="260" 
        Loaded="Window_Loaded" >
    <Window.Resources>
        <Storyboard x:Key="SecondHand" x:Name="SecondHand" RepeatBehavior="Forever" >
            <DoubleAnimation
                Duration="0:1:0" From="0" To="360"
                Storyboard.TargetName="SecondLine"
                Storyboard.TargetProperty="(Line.RenderTransform).(RotateTransform.Angle)"
            />
        </Storyboard>
        <Storyboard x:Key="MinuteHand" x:Name="MinuteHand" RepeatBehavior="Forever">
            <DoubleAnimation
                Duration="1:0:0" From="0" To="360"
                Storyboard.TargetName="MinuteLine"
                Storyboard.TargetProperty="(Line.RenderTransform).(RotateTransform.Angle)"
            />
        </Storyboard>
        <Storyboard x:Key="HourHand" x:Name="HourHand" RepeatBehavior="Forever">
            <DoubleAnimation
                Duration="12:0:0" From="0" To="360"
                Storyboard.TargetName="HourLine"
                Storyboard.TargetProperty="(Line.RenderTransform).(RotateTransform.Angle)"
            />
        </Storyboard>
    </Window.Resources>
    <Canvas Width="200" Height="200">
        <Line x:Name="HourLine"  Stroke="Black" Fill="Black" 
              X1="100" Y1="100" X2="100" Y2="35"              
               StrokeThickness="3" >
            <Line.RenderTransform>
                <RotateTransform  x:Name="AngleHour" Angle="0" 
                                  CenterX="100" CenterY="100"/>
            </Line.RenderTransform>
        </Line>
        <Line x:Name="MinuteLine"  Stroke="Black" Fill="Black" 
              X1="100" Y1="100" X2="100" Y2="15"              
               StrokeThickness="2" >
            <Line.RenderTransform>
                <RotateTransform x:Name="AngleMinute"  Angle="0" 
                                 CenterX="100" CenterY="100"/>
            </Line.RenderTransform>
        </Line>
        <Line x:Name="SecondLine"  Stroke="Black" Fill="LightGray"
              X1="100" Y1="120" X2="100" Y2="10"  >
            <Line.RenderTransform>
                <RotateTransform x:Name="AngleSecond" Angle="0"  
                                 CenterX="100" CenterY="100"/>
            </Line.RenderTransform>
        </Line>
        <Ellipse Fill="Black" Width="6" Height="6" HorizontalAlignment="Center" 
                 VerticalAlignment="Center" 
                 Canvas.Top="97" Canvas.Left="97"/>
    </Canvas>
</Window>

C#のコード

C#側もいたって簡単なコードです。

現在の時刻に合せて、それぞれの針の初期の角度を設定し、これをFromの角度とし、Toはそれに360度を加えた角度とします。Durationは、XAMLで設定した値を使います。これでアニメーションをスタートさせています。

たったこっれだけで秒針が滑らかに動くアナログ時計が実現できるのはちょっとした驚きです。

using System;
using System.Windows;
using System.Windows.Media.Animation;

namespace WpfAnalogClock2
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            InitializeAngle();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            StartAnimation("HourHand", this.AngleHour.Angle);
            StartAnimation("MinuteHand", this.AngleMinute.Angle);
            StartAnimation("SecondHand", this.AngleSecond.Angle);
        }

        void InitializeAngle()
        {
            DateTime dt = DateTime.Now;
            this.AngleSecond.Angle = dt.Second * 360.0 / 60.0;
            this.AngleMinute.Angle = (dt.Minute + dt.Second / 60.0) * 360.0 / 60.0;
            this.AngleHour.Angle = (dt.Hour + dt.Minute / 60.0) * 360.0 / 12;
        }

        private void StartAnimation(string name, double angle)
        {
            var sb = this.Resources[name] as Storyboard;
            var da = sb.Children[0] as DoubleAnimation;
            da.From = angle;
            da.To = da.From + 360.0;
            sb.Begin();
        }

    }
}

ソースコードは、GitHubでも公開しています。

gushwell
株式会社ジード / Microsoft MVP for Developer Technologies 2005-2020 / 著書『実戦で役立つ C#プログラミングのイディオム/定石&パターン』『新・標準プログラマーズライブラリ なるほどなっとく C#入門』『C#プログラミング入門―オブジェクト指向のプログラミング手法を基礎から解説』
https://github.com/gushwell
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away