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).
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:
- 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.
- 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.
- 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).
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!).
« download source code
« download DLL + CHM
Take care. Bye.