Håller på att skissa på ett upplägg för en Silverlightbaserad julkalender för att använda under den kommande säsongen och då fick jag en liten uppenbarelse som jag gärna vill dela med mig av.
Jag använder mig naturligtvis av Expression Blend 3 och Visual Studio och tänkte försöka att vara ganska konsekvent med att göra designen i Blend och utvecklingen i Visual Studio.
Mitt koncept till julkalender är en “fattigmans” deepzoom (även fast deepzoom inte kostar något), där varje lucka i julkalendern triggar en inzoomning till respektive område, samt eventuellt triggar en aktivitet som kan vara ytterligare en animering, spela upp en film, en ljudsnutt eller något annat kul.
Här är resultatet av den hör första delen:
För att göra min bild lite roligare så googlade jag på Bing (som Scott Hanselman säger) efter “julkort” och hittade bilden till höger som jag tycker passar lämpligt till temat.
Jag skapade sedan ett nytt projekt i Expression Blend och lägger till den trevliga bilden till projektet. Då har jag en projektstruktur som ser ut så här:

Som du säkert märker så har jag valt att skapa ett projekt med en separat webblösning som “hostar” min julkalender. Än så länge har jag inte lagt till någon som helst funktionalitet.
Det första jag gör sedan är att jag byter ut den grid som används som grundelement till en canvas, enklast genom att högerklicka på elementet “LayoutRoot” i fönstret “Objects and Timeline” och välja “Change Layout Type | Canvas”. Skälet till detta är för att använda absolutpositionering av mina luckor (som kommer att bestå av anpassade knappar).
Sedan drar jag in bilden på design-ytan (canvasen) och ser till så att bilden täcker hela ytan, exempelvis genom att sätta bredd och höjd på bilden till 640x480. Och så har det blivit dags för att skapa mina luckor.
Eftersom jag vet att en lucka ska hantera interaktion och också en siffra för att visa respektive luckas nummer så lämpar sig en knapp lysande för detta ändamål, men däremot så vill jag inte behålla det grundläggande utseendet på knapparna utan helt enkelt ha en yta som kan tryckas på och som då triggar en animering.
Jag drar ut en knapp och positionerar den över tomtefars ansikte, gör den 64x48 pixlar stor och sätter texten till att vara 24 (börjar med slutet). Men jag är inte nöjd med utseendet utan högerklickar istället på knappen och väljer att “Edit Template | Edit a Copy” som skapar en mall för mina
knappar, den mallen döper jag till “Luckor”. Mallen som jag sedan skapar är naturligtvis som smaken, jag tog bort det mesta av tillstånd och bakgrunder och slutar helt enkelt med bilden till vänster som resultat! Observer att jag har markerat knappen i designytan bara för att den ska “synas” när jag tog en skärmdump.
Nu vill jag skapa en inzoomning när jag trycker på knappen (eller luckan), så jag börjar med att skapa själva animationen, i Blend används så kallade “storyboards”. Det enklaste sättet som jag kom på att använda var att helt enkelt skala hela ytan med 10 i x- och y-led och samtidigt positionera om ytan så att den är centrerad över själva luckan. Jag bestämmer mig för en relativt snabb inzoomning på 0,5 sekunder och så här ser resultatet ut i Expression Blend.
Observera att jag har markerat “LayoutRoot som element att påverkas av animationen. I mitt fall så sätts följande värden på respektive parameter i animationen:
- Translation.X – 1327
- Translation.Y – 830
- ScaleX – 10
- ScaleY - 10
Sedan måste jag se till att den här animationen används när jag trycker på knappen. Och då blir det till att skriva lite kod, och eftersom jag vill använda mig av Visual Studio för utvecklingen så gör jag så här. Först så markerar jag knappen, och väljer att istället för att se “properties” att se “events” och skriver “Luckor_Click” i händelsen för Click. Då genereras den så kallade “event-handlern” som kommer att reagera på knapptryckningar. Men jag väljer att högerklicka på lösningen i fönstret “Projects” och välja “Edit in Visul Studio”. Här kan jag helt enkelt skriva följande kod för att starta animationen.
private void Luckor_Click(object sender, System.Windows.RoutedEventArgs e)
{
ZoomTillLucka.Begin();
}
Men så kommer jag på att jag gärna vill kunna zooma ut igen och därför skapar jag ytterligare en animation som den här gången zoomar ut till helskärmsläge igen. Den ser ut som den tidigare skapade animationen men har värdena ScaleX och ScaleY båda satta till 1 och Translation.X och Translation.Y satta till 0. Men hur ska jag kunna få till att hantera återzoomningen, exempelvis när jag trycker på knappen igen? Lite kod får det bli, det här är den färdiga koden för hela interaktionen:
public partial class MainPage : UserControl
{
bool _zoomed = false;
public MainPage()
{
// Required to initialize variables
InitializeComponent();
ZoomUt.Completed += new System.EventHandler(ZoomUt_Completed);
}
private void ZoomUt_Completed(object sender, System.EventArgs e)
{
_zoomed = false;
}
private void image_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
ZoomUt.Begin();
}
private void Luckor_Click(object sender, System.Windows.RoutedEventArgs e)
{
if (_zoomed)
ZoomUt.Begin();
else
{
_zoomed = true;
ZoomTillLucka.Begin();
}
}
}
Jag använder mig av en fält i klassen som kommer ihåg om jag för närvarande är inzoomad eller utzoomad (från början är jag utzoomad). Sedan ser jag till att sätta den parametern när jag zoomat ut och gör också bakgrunden (ytan utanför luckorna) som möjlig att trycka på för att zoom ut.
Sedan är det ett relativt repetitivt förfarande att skapa dessa animationer för inzoomning till respektive lucka, eller så drar jag nyttan av att det är just repetitivt och det borde jag kunna använda :)
Efter lite laborerande och kodandes så kommer jag fram till att om jag ger Transform.X och Transform.Y egenskaperna i animationen ZoomaTillLucka namn gör att jag kan nå dem i koden så får jag helt nya möjligheter. Jag lägger därför till attributen x:Name=”TranslateX” och x:Name=”TranslateY” enligt följande:

Då kan jag sedan uppdatera händelsehanteraren för luckorna till följande:
private void Luckor_Click(object sender, System.Windows.RoutedEventArgs e)
{
Button lucka = sender as Button;
TranslateX.Value = -((double)lucka.GetValue(Canvas.LeftProperty)) * 10;
TranslateY.Value = -((double)lucka.GetValue(Canvas.TopProperty)) * 10;
if (_zoomed)
ZoomUt.Begin();
else
{
_zoomed = true;
ZoomTillLucka.Begin();
}
}
Det återstår en sak till och det är att se till att animationerna agerar på LayoutRoot med rätt koordinatsystem, och därför markerar jag LayoutRoot i designern och sätter RenderTransform.CenterPoint till att vara 0 i både X och Y-led.
Vad var nu detta bra för kan tänkas. Jo nu kan jag helt enkelt kopiera och klistra in den redan befintliga knappen flera gånger (23 till för att vara exakt), positionera ut dem på julkalendern och byta numret på luckan till rätt nummer. Betydligt enklare i min mening än att behöva skapa ytterligare animationer. Plus att det blir betydligt mindre kod också i XAML-filen eftersom jag bara har två animationer istället för 25 som det hade blivt om jag hade valt unika animationer för varje lucka.
Efter en gedigen klipp och klistra övning så ser min julkalender ut enligt följande:
Och när jag har zoomat in på exempelvis nummer 23 så ser det ut så här:

Som synes så blir bakgrundsbilden väldigt pixlig medans det typsnitt som jag valt för luckorna blir rent och snyggt, men det här vara egentligen bara halva vägen, nu behöver jag vara lite ytterligare kreativ och komma på vad som egentligen ska hända när luckan öppnas kanske till och med ska ha någon fejkad 3D-effekt som gör så att det ser ut som om en lucka verkligen öppnas, men det får bli en annan artikel (del 2).
Det som jag däremot hade som uppenbarelse var hur mycket snabbare det gick att skapa den här lösningen och dess animationer med lite kunskap i programmering. Från början till slut så tog det mig ungefär 30 minuter att skapa den här julkalendern, lägg till ett par timmar för att skriva själva artikeln så känns det som om det gick relativt snabbt ändå. Det går säkert att göra detta med andra verktyg och andra flöden, men jag har lärt mig en del roliga tekniker på vägen. Om jag hade gjort varje animation för sig så är jag övertygad om att tiden hade åtminstone dubblats, eventuellt till och med tredubblats.