Resumé

Silverlight 2.0 Tweener(Of T) Ultimate Version

Cristian Merighi () 4,50

Ultima - soddisfacente - versione del mio Tweener per Silverlight 2.0. Miglior design e supporto di funzioni quali Pause, Stop, Resume e Rewind. Disponibile è anche la relativa documentazione, il codice sorgente ed un applicativo d'esempio!
Questo articolo è da considerarsi obsoleto. Alcune funzionalità potrebbero non essere più disponibili e non è possibile aggiungere commenti.

Bene, questo è il terzo step nel processo di crescita della mia libraria per il Tweening in Silverlight 2.0, penso di poter dire che quella descritta nel presente articolo è una buona versione base/definitiva su cui lavorare.
Allo stato attuale:

  • Il Tweener può avere come target qualsiasi DependencyProperty di qualsiasi DependencyObject, l'unica limitazione sta nell'utilizzare dependency properties che sottendono un tipo numerico, un Color o un Point. Tale "limitazione" raggruppa pressoché tutte le casistiche di DependencyProperties che, in Silverlight, possono senstatamente ritenersi interpolabili.
  • Il Tweener ha ora una logica centralizzata nella gestione del Clock (Timer): ciò significa che c'è uno e un solo DispatcherTimer che si prende cura di gestire ogni step interpolativo per ogni oggetto chiamato in causa, e perdippiù di stoppa da solo ...se non c'è nulla da fare! (Nella precedente versione era prevista l'istanziazione di un timer per ogni PropertyType chiamata in causa). Per ottenere ciò, penso di aver reallizzato il più bel pezzo di oggetto polimorfico della mia carriera di programmatore; è stato parecchio divertente!
  • Il Tweener può gestire le azioni di Stop, Pause, Resume and Rewind. Sono certo che possano dare allo sviluppatore un notevole grip sugli oggetti trattati.
    Inoltre è possibile mettere handlers specifici "in ascolto" di particolari eventi che i vari tween scatenano (MotionChanged è con ogni probabilità il più gustoso di questi).

Un nuovo "poster" per il mio sistema di Tweening è riportato qui sotto (buttate un occhio a quello che solo due giorni fa ho postato qui per confrontarne l'avanzamento).

tweener class diagram

Ah, stavolta c'è anche la demo! Alla fine trovato modo di approntare un esempietto sempliciotto (che mi prometto di migliorare prima o poi); cliccate pure sullo screenshot sotto, lasciatemi però solo un attimo per anticiparne i contenuti:

  1. C'è un oggetto Canvas che contiene una Path Geometry la quale, a sua volta, ha il mio bel faccione da bambino come ImageBrush background. (!)
    Un moto rotatorio è applicato (...detta così ricorda tanto i testi dei problemi di Meccanica Razionale all'università) al Canvas di cui sopra, dimodoché continui a ruotare di 360° rimbalzando alternativamente avanti ed indietro, tale moto "perpetuo" è causato dal FinishedBehavior di tipo Yoyo ad esso applicato. E' possibile mettere in pausa e poi riprendere tale movimento utilizzando il pulsante in alto nell'interfaccia grafica.
  2. Il bordo (Stroke) della path geometry è un'istanza di SolidColorBrush, la cui ColorProperty può essere interpolata selezionando uno dei valori elencati nel listbox in alto a sinistra.
    Si otterrà un effetto lineare di blending dal colore attuale a quello selezionato.
  3. Infine, uno dei punti che formano la sagoma del quadrilatero può venire interpolato nelle sue componenti X e Y, agendo sugli appositi sliders. Questo per dimostrare anche le possibilità del Tweener(Of Point).
tweener class sample (silverlight 2.0)

Visto che è sempre utile spulciare un po' di codice, ecco tutto quello (XAML e C#) utilizzato nella demo

XAML:
(prego notate che una MatrixTransform è il valore iniziale della proprietà RenderTransform del Canvas cnv, questo per evidenziare come ogni informazione sullo stato iniziale dell'oggetto interpolato non venga persa anche se vi viene affiancato un tween).

<UserControl x:Class="TestSilverlightApplication.Page"

   xmlns="http://schemas.microsoft.com/client/2007"

   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   Width="600" Height="600" Loaded="UserControl_Loaded">

    <Grid x:Name="LayoutRoot" Background="#ffffffff" MouseLeftButtonUp="LayoutRoot_MouseLeftButtonUp">

 

        <Border x:Name="brd" CornerRadius="10,10,10,10" BorderThickness="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"

               >

            <Border.Background>

                <LinearGradientBrush EndPoint="0.5,0.5" StartPoint="1,0.5" SpreadMethod="Reflect">

                    <GradientStop Color="#FF3d3d3d" Offset="0"/>

                    <GradientStop Color="#ff8A9378" Offset="1"/>

                </LinearGradientBrush>

            </Border.Background>

            <Border.BorderBrush>

                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0" SpreadMethod="Reflect">

                    <GradientStop Color="#FFD8DFD7"/>

                    <GradientStop Color="#FF262B2E" Offset="1"/>

                </LinearGradientBrush>

            </Border.BorderBrush>

<Canvas x:Name="cnv" VerticalAlignment="Center" HorizontalAlignment="Center" Background="#ffc58263">

    <Canvas.RenderTransform>

    <MatrixTransform Matrix=".5,.5,-.866,.866,-5,-90" />

    </Canvas.RenderTransform><Path Stroke="#FFc0c0c0" StrokeThickness="4" x:Name="path">

            <Path.Fill>

                <ImageBrush ImageSource="images/CM.jpg" TileMode="None" Stretch="Fill" />

            </Path.Fill>

        <Path.Data>

        <PathGeometry>

            <PathFigure StartPoint="310,50">

                <LineSegment Point="0,50" />

                <LineSegment Point="0,180" />

                <LineSegment Point="150,180" x:Name="seg" />

                <LineSegment Point="310,50" />

            </PathFigure>

        </PathGeometry>

    </Path.Data>

</Path>

</Canvas>

 

 

        </Border>

<TextBlock x:Name="txt" TextWrapping="NoWrap" Visibility="Collapsed"

              Text="debug:" Width="Auto" Foreground="Red" FontSize="11" FontFamily="Courier New" />

 

        <StackPanel Orientation="Horizontal" Margin="10,10,0,0">

            <StackPanel Orientation="Vertical" Margin="0,0,10,0">

                <TextBlock Text="Border Color" Foreground="White" FontSize="11" />

        <ListBox x:Name="ddl" Width="100" SelectionChanged="ddl_SelectionChanged"

                HorizontalAlignment="Left" VerticalAlignment="Top" MinHeight="32">

            <ListBox.ItemTemplate>

                <DataTemplate>

                    <TextBlock Text="{Binding Key}" FontSize="11" />                   

                </DataTemplate>               

            </ListBox.ItemTemplate>

        </ListBox></StackPanel>

            <StackPanel Orientation="Vertical" Margin="0,0,10,0">

                <TextBlock Text="Corner Point (X, Y)" Foreground="White" FontSize="11" />

                    <Slider Minimum="1" Maximum="410" x:Name="sldX" Value="150" ValueChanged="sld_ValueChanged"></Slider>

                    <Slider Minimum="1" Maximum="200" x:Name="sldY" Value="180" ValueChanged="sld_ValueChanged"></Slider>

                </StackPanel>

            <StackPanel Orientation="Vertical" Margin="0,0,10,0">

                <TextBlock Text="Yoyo Rotation" Foreground="White" FontSize="11" />

                <Button Content="pause" Click="Button_Click" />

                </StackPanel>

        </StackPanel>

    </Grid>

</UserControl>

 

 

C#:

public partial class Page : UserControl

{

    public Page()

    {

        InitializeComponent();

        ddl.ItemsSource = this.ColorList;

    }

 

    Tween<double> tween = null;

    Tween<Color> tweenColor = null;

    Tween<Point> tweenPoint = null;

    bool resumed = false;

 

    private void UserControl_Loaded(object sender, RoutedEventArgs e)

    {

        tween = Tweener.CreateTween(cnv, TweenProperty.Angle, Bounce.EaseOut, 0, 360, TimeSpan.FromSeconds(3D));

        tween.FinishedBehavior = ClockFinishedBehavior.Yoyo;

    }

 

    void tween5_MotionFinished(object sender, EventArgs e)

    {

        resumed = true;

        Tweener<double>.Resume(tween);

    }

 

    void tween_MotionChanged(object o, TweenMotionChangedEventArgs<double> e)

    {

        txt.Text += string.Format("\n[{0:T}] value {1}", e.Elapsed, e.CurrentPosition);

        Tween<double> sender = (Tween<double>)o;

        if (!resumed && .5D * sender.Duration.TotalSeconds < e.Elapsed.TotalSeconds) Tweener<double>.Pause(sender);

    }

 

    void tween_MotionFinished(object sender, EventArgs e)

    {

        txt.Text = string.Format("\n[{0:T}] Finished!\ntrans children: {1}", DateTime.Now, ((TransformGroup)((UIElement)((ITween<double>)sender).Object).RenderTransform).Children.Count);

    }

 

    void tween_MotionStarted(object sender, EventArgs e)

    {

        txt.Text += string.Format("\n[{0:T}] Started!", DateTime.Now);

    }

 

    private void LayoutRoot_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

    {

        List<Transform> coll = ((TransformGroup)cnv.RenderTransform).Children.ToList();

        txt.Text = string.Format("\ntrans children: {0}", coll.Count);

        coll.ForEach(delegate(Transform item)

        {

            txt.Text += string.Format("\n   type: {0}", item.GetType());

        });

    }

 

    private void ddl_SelectionChanged(object sender, SelectionChangedEventArgs e)

    {

        if (tweenColor != null) Tweener<Color>.Stop(tweenColor, true);

        tweenColor = Tweener<Color>.CreateTween((SolidColorBrush)path.Stroke, SolidColorBrush.ColorProperty, Linear.EaseNone,

            ((SolidColorBrush)path.Stroke).Color, GetSelectedColor(), TimeSpan.FromSeconds(3D));

    }

 

    private Color GetSelectedColor()

    {

        int j = 0;

        foreach (KeyValuePair<string, Color> kvp in ColorList)

        {

            if (j == ddl.SelectedIndex) return kvp.Value;

            j++;

        }

        return new Color();

    }

 

    private Dictionary<string, Color> _List = null;

    private Dictionary<string, Color> ColorList

    {

        get

        {

            if (_List == null){

                _List = new Dictionary<string,Color>(5);

                _List.Add("Gray", Colors.Gray);

                _List.Add("Red", Colors.Red);

                _List.Add("Blue", Colors.Blue);

                _List.Add("Green", Colors.Green);

                _List.Add("Yellow", Colors.Yellow);

            }

            return _List;

        }

    }

 

    private void sld_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)

    {

        Point tget = new Point(sldX.Value, sldY.Value);

        if (tweenPoint != null) Tweener<Point>.Stop(tweenPoint, true);

        tweenPoint = Tweener<Point>.CreateTween(seg, LineSegment.PointProperty, Expo.EaseOut, seg.Point, tget, TimeSpan.FromSeconds(2.8D), TimeSpan.FromSeconds(1D));

    }

 

    private void Button_Click(object o, RoutedEventArgs e)

    {

        Button sender = (Button)o;

        if (tween.Status == ClockStatus.Paused)

        {

            Tweener<double>.Resume(tween);

            sender.Content = "pause";

        }

        else

        {

            Tweener<double>.Pause(tween);

            sender.Content = "play";

        }

    }

}

 

 

Ultima osservazione, ma fate bene attenzione: il codice di questa classe Tweener può facilmente essere copiato-e-incollato all'interno di un libreria WPF e funzionare perfettamente! (già fatto, mi sono solo preoccupato di rinominare i namespaces da Pacem.Silverlight.Tweening in Pacem.Media.Tweening per correttezza formale)
Pensate cosa può voler dire scatenare gli effetti di un bel motion tween non più su un povero Canvas bensì su un succulento ...ModelVisual3D!
...salivazione accelerata...

Ecco qui un po' di robetta pronta per il download:

  • Progetti Visual Studio 2008 (Tweener code + sample application).
  • DLL library e documentation CHM creata con Sandcastle Help File Builder (grande tool!).
zip file « download source code
zip file « download DLL + CHM

Take care. Bye.

Feedbacks

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    Shtong martedì 8 aprile 2008 4,00

    This library is great, however there's something I don't understand in the implementation: with did you use an array as the parameter of the tween functions instead of simply using four double parameters ? The tween fonctions have to be as performant as possible, however, this technique makes them allocate four more double values on the stack, and you have to check if the parameter count is correct, which causes a lot of overhead. Moveover, I made some tests that showed me that using the classic parameters is slightly more performant that using the params array, even without size checking. I also noticed that the agTweener library uses the same signatures, so I wanted to know if I was missing anything ?

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    CMerighi martedì 8 aprile 2008 0,00

    Hi Shtong, firstly thanks for the feedback! Then the answers... The reason why I took the array decision was to allow the user to plug his own easing func, with the flexibility of custom extra-arguments. As you correctly noticed, this intention isn't easy appliable at the moment (you should implement your own subclass of Tween&lt;T&gt; and override the HandleTick method to do that, so it makes no sense to plug a custom easing function). I think it is fairly right - at this point - to constrain the EasingFunc delegate to 4 sealed arguments of double (but I'll try also other ways). About the signatures of the easing functions, yes: I recycled 'em from those of Micheal Cameron. I contacted him and posted to him my library before to publish it onto my blog. He also asked me if he could put some of my Tweening logic in his next release, and I obviously agreed: it's all about open source stuff (and a bit of hedonism ;)). Thanks again.

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    Richard J mercoledì 18 giugno 2008 5,00

    Excellent library, I animated a Point in my application with just a couple of lines! A couple of questions: 1. I can't see any information on licencing --- perhaps you can clarify the terms of use? 2. Is there a reason to limit the properties that can be animated to dependency properties? Could CLR properties be animated too?

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    CMerighi mercoledì 18 giugno 2008 0,00

    Hi Richard, glad you appreciate it! I didn't provide licensing infos just because I didn't mind to... I'd certainly love some linking credits by the users of my tweener, but I know that fair coders will provide 'em whether selfish ones won't, no matter if I beg or roughly order them to do it... The tweener comprehends any dependency property whose type is numeric or color or point (I'm including also 3D properties from my personal 3D engine...) and also "TweenProperties" which wrap RenderTransformation parameters (Scale, Rotation...). This is because you essentially (I'd dare to say exclusively) need to tween UIElements in color, position or transformation params. These kinds of properties are all dependency properties in Silverlight and WPF and, more, you can lean on GetValue and SetValue methods to safely get and set relevant values. If you need to tween a generic property, you can always declare a dummy dependecy property for your usercontrol in Silverlight and handle its motionchanged event (in this manner you have a constantly updated tweened value that you can use for a whatever-you-want property)...

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    RichardJ venerdì 20 giugno 2008 0,00

    I did add a dependency property to my user control to animate that, and it works just fine. I now find I have a case where I really just need access to the eased progress value, to calculate some "result" values that need to be set at the same time. I would add a dependency property to the class for progress but the class is a simple class and Microsoft won't let me inherit directly from DependencyObject, as you can in WPF. I can work around this by attaching a property to a related class. However, I wonder whether it's a common enough use-case to consider extending the library? Just my thoughts...

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    CMerighi domenica 22 giugno 2008 0,00

    You could extend the FrameworkTemplate class (which is the lowest-level DependencyObject inheritable subclass available). I'll consider to extend the library anyway, maybe using by-reference variables aside of DependecyProperties... mmmhhh... we'll see... Cheers!...

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    ftoohhhhhhhhhhh martedì 9 dicembre 2008 0,00

    thank youuuuuuuuuuuuu

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    Josh Santangelo mercoledì 24 dicembre 2008 5,00

    In general, this looks pretty good, but I'm seeing a "jump" at the end of all of my tweens to their end destination. I am tweening custom DPs. Also, your example app should be updated to the release version of Silverlight 2.

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    Derek mercoledì 14 gennaio 2009 0,00

    Hey bud, your Silverlight version is out of date. Please upgrade to the latest so I can see your online demo.

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    Craig Muckleston mercoledì 25 febbraio 2009 5,00

    Excellent work! I have one question though. I am using the Bounce.EaseIn method o bring my object in from 0 to 1. I want to have my object start with a Scale of 0 and bounce in. If I set this in code first, then the tween doesn't bring it up to 1. If I leave the object as is, then the object is flashed up on screen before the tween runs (this is when the tween works perfectly). Any ideas how I can get around this?

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    CMerighi venerdì 27 febbraio 2009 0,00

    I'm not sure if I got it, but if so... you could keep your object hidden (collapsed) since the tween has started (see Motionstarted event) then show it...

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    Frank venerdì 6 marzo 2009 0,00

    When I run the demo on your webpage by clicking the Play button, the new webpage thinks Silverlight needs to be installed. Can you tell me why that occurs when I already have the lastest version?

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    Frank sabato 7 marzo 2009 0,00

    On my previous comment, "lastest version" should be "latest version". Is it possible that the issue is on your server? I have developed websites using Silverlight, and I do not have the issue that I encounter at your site. Any help would be greatly appreciated.

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    Andrew venerdì 13 marzo 2009 4,00

    Great little class, any reason why every 3rd tween it jumps and staggers?

  • Re: Silverlight 2.0 Tweener(Of T) Ultimate Version

    empty venerdì 13 marzo 2009 4,00

    You mention you can use these classes with WPF, which I've tried and failed to accomplish. Is this something you'll be willing to share?

feedback
 

Syndicate

Autore

Cristian Merighi facebook twitter google+ youtube

Ultimi articoli

Top rated

Archivio

Dove sono?

Autore

Cristian Merighi facebook twitter google+ youtube

Le mie letture

Feeds