четверг, 15 апреля 2010 г.

Создаем шарики Ньютона при помощи SILVERLIGHT

Шарики Ньютона не редко стоят на рабочем столе в офисе или дома. Они хорошо успокаивают нервы. Они постоянно стучат и иногда падают со стола во время уборки или поиска нужной папки. Поэтому мы сделаем их реализацию на silverlight.
Начнем мы с рисования наших шариков. Сделаем 4 круга и 4 нитки на которых они будут висеть.
<UserControl x:Class="SilverlightAnimation.MainPage"
    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"
    mc:Ignorable="d" d:DesignWidth="340" d:DesignHeight="160">

    <Canvas x:Name="LayoutRoot" Height="150" Width="330">
        <Line X1="120" X2="120" Y1="20" Y2="130" Stroke="#FF444444" x:Name="Line1"/>
        <Line X1="160" X2="160" Y1="20" Y2="130" Stroke="#FF444444" />
        <Line X1="200" X2="200" Y1="20" Y2="130" Stroke="#FF444444" />
        <Line X1="240" X2="240" Y1="20" Y2="130" Stroke="#FF444444"  x:Name="Line2"/>
        <Ellipse 
        Width="40" Height="40" Canvas.Top="120" Canvas.Left="100" x:Name="ball">

           
        </Ellipse>
        <Ellipse Width="40" Height="40" Canvas.Top="120" Canvas.Left="140" x:Name="ball2" />
        <Ellipse Width="40" Height="40" Canvas.Top="120" Canvas.Left="180" x:Name="ball3" />
        <Ellipse Width="40" Height="40" Canvas.Top="120" Canvas.Left="220" x:Name="ball4" />
    </Canvas>
</UserControl>Syhi-подсветка кода
Теперь надо залить шарики цветом. Для этого сначала занесем все шарики в один массив, затем используем радиальный градиент. Сделаем это в конструкторе:
public MainPage()
        {
            InitializeComponent();
            Ellipse[] balls = new Ellipse[4];
            balls[0] = ball;
            balls[1] = ball2;
            balls[2] = ball3;
            balls[3] = ball4;

            RadialGradientBrush brush = new RadialGradientBrush(Colors.LightGray, Colors.DarkGray);
            for (int i = 0; i < balls.Length; i++)
                balls[i].Fill = brush;
        }Syhi-подсветка кода
Картинка готова:

Теперь будем её анимировать. После удара, шарик сначала должен замедляясь подниматься вверх, затем ускоряясь опускаться вниз, поэтому будем использовать ключевые кадры сглаженной анимации. Сначала код, потом его разъяснение.
<UserControl x:Class="SilverlightAnimation.MainPage"
    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"
    mc:Ignorable="d" d:DesignWidth="340" d:DesignHeight="160">

    <Canvas x:Name="LayoutRoot" Height="150" Width="330">
        <Line X1="120" X2="120" Y1="20" Y2="130" Stroke="#FF444444" x:Name="Line1"/>
        <Line X1="160" X2="160" Y1="20" Y2="130" Stroke="#FF444444" />
        <Line X1="200" X2="200" Y1="20" Y2="130" Stroke="#FF444444" />
        <Line X1="240" X2="240" Y1="20" Y2="130" Stroke="#FF444444"  x:Name="Line2"/>
        <Ellipse 
        Width="40" Height="40" Canvas.Top="120" Canvas.Left="100" x:Name="ball">

            <Ellipse.Triggers>
                <EventTrigger RoutedEvent="Ellipse.Loaded">
                    <BeginStoryboard>
                        <Storyboard x:Name="firstStoryboard" Completed="firstStoryboard_Completed">
                            <DoubleAnimationUsingKeyFrames 
                     Storyboard.TargetName="ball" 
                     Storyboard.TargetProperty="(Canvas.Left)" AutoReverse="True" >

                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="25" KeySpline="0,0.01 0.02,1" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames 
                     Storyboard.TargetName="ball" 
                     Storyboard.TargetProperty="(Canvas.Top)" AutoReverse="True" >

                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="80" KeySpline="0,0.01 0.02,1" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames 
                     Storyboard.TargetName="Line1"
                     Storyboard.TargetProperty="(X2)" AutoReverse="True" >

                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="50" KeySpline="0,0.01 0.02,1" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames 
                     Storyboard.TargetName="Line1" 
                     Storyboard.TargetProperty="(Y2)" AutoReverse="True" >

                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="90" KeySpline="0,0.01 0.02,1" />
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>

                    <BeginStoryboard>
                        <Storyboard x:Name="secondStoryboard" Completed="secondStoryboard_Completed">
                            <DoubleAnimationUsingKeyFrames
                     Storyboard.TargetName="ball4"
                     Storyboard.TargetProperty="(Canvas.Left)" AutoReverse="True">

                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="295"  KeySpline="0,0.01 0.02,1"/>
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames
                     Storyboard.TargetName="ball4" 
                     Storyboard.TargetProperty="(Canvas.Top)" AutoReverse="True" >

                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="80"  KeySpline="0,0.01 0.02,1"/>
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames 
                     Storyboard.TargetName="Line2"
                     Storyboard.TargetProperty="(X2)" AutoReverse="True" >

                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="310" KeySpline="0,0.01 0.02,1" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames 
                     Storyboard.TargetName="Line2" 
                     Storyboard.TargetProperty="(Y2)" AutoReverse="True" >

                                <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="90" KeySpline="0,0.01 0.02,1" />
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Ellipse.Triggers>
        </Ellipse>
        <Ellipse Width="40" Height="40" Canvas.Top="120" Canvas.Left="140" x:Name="ball2" />
        <Ellipse Width="40" Height="40" Canvas.Top="120" Canvas.Left="180" x:Name="ball3" />
        <Ellipse Width="40" Height="40" Canvas.Top="120" Canvas.Left="220" x:Name="ball4" />
    </Canvas>
</UserControl>Syhi-подсветка кода
Анимацию я повесил на загрузку первого шарика. Два шарика и соответственно две нитки имеют одну и ту же зеркальную анимацию, поэтому достаточно рассмотреть одну из них.
<BeginStoryboard>
    <Storyboard x:Name="firstStoryboard" Completed="firstStoryboard_Completed">
        <DoubleAnimationUsingKeyFrames 
                     Storyboard.TargetName="ball" 
                     Storyboard.TargetProperty="(Canvas.Left)" AutoReverse="True" >

            <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="25" KeySpline="0,0.01 0.02,1" />
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames 
                     Storyboard.TargetName="ball" 
                     Storyboard.TargetProperty="(Canvas.Top)" AutoReverse="True" >

            <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="80" KeySpline="0,0.01 0.02,1" />
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames 
                     Storyboard.TargetName="Line1"
                     Storyboard.TargetProperty="(X2)" AutoReverse="True" >

            <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="50" KeySpline="0,0.01 0.02,1" />
        </DoubleAnimationUsingKeyFrames>
        <DoubleAnimationUsingKeyFrames 
                     Storyboard.TargetName="Line1" 
                     Storyboard.TargetProperty="(Y2)" AutoReverse="True" >

            <SplineDoubleKeyFrame KeyTime="0:0:0.5" Value="90" KeySpline="0,0.01 0.02,1" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</BeginStoryboard>Syhi-подсветка кода
BeginStoryboard - это своеобразный контейнер для анимаций.Storyboard - это, так сказать, сама анимация. DoubleAnimationUsingKeyFrames указывает, что мы используем анимацию с использованием ключевых кадров. Storyboard.TargetName="ball" Storyboard.TargetProperty="(Canvas.Left)" - эти свойства показывают на какой объект и на какое его свойство направленна анимация. SplineDoubleKeyFrame - указывает что мы используем сглаженную анимацию.KeyTime="0:0:0.5" - время анимации(полсекунды).KeySpline="0,0.01 0.02,1" - если говорит просто, то первые 10% пути шарик ускоряется, следующие 10% пути движется равномерно, после замедляется.
Теперь можно запустить проект. Как видим оба крайних шарика двигаются, а надо, чтобы двигались они по очереди. Для этого вешаем обработчики на action Complete каждого Storyboard. И в первом случае останавливаем первый шарик и запускаем второй, а во втором останавливаем второй шарик и запускаем первый. Ещё при запуске приложении нужно остановить второй шарик. Итого получаем полный код:
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media;

namespace SilverlightAnimation
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            secondStoryboard.Stop();
            Ellipse[] balls = new Ellipse[4];
            balls[0] = ball;
            balls[1] = ball2;
            balls[2] = ball3;
            balls[3] = ball4;

            RadialGradientBrush brush = new RadialGradientBrush(Colors.LightGray, Colors.DarkGray);
            for (int i = 0; i < balls.Length; i++)
                balls[i].Fill = brush;
        }

        private void firstStoryboard_Completed(object sender, EventArgs e)
        {
            firstStoryboard.Stop();
            secondStoryboard.Begin();
        }

        private void secondStoryboard_Completed(object sender, EventArgs e)
        {
            secondStoryboard.Stop();
            firstStoryboard.Begin();
        }
    }
}Syhi-подсветка кода

И можно успокаивать нервы:)

Комментариев нет:

Отправить комментарий