Spre deosebire de WPF, unde avem functionalitate 3D implementata integral, in Silverlight putem seta doar proiectii pentru controalele 2D existente care creaza iluzia de perspectiva (este vorba de o transformare non-afina). Acest lucru se face cu ajutorul proprietatii Projection a controalelor, care poate sa ia doua tipuri de valori:
-
PlaneProjection
-
MatrixTransform
1. Crearea imaginilor din interfata.
Primul pas este adaugarea programatica a imaginilor in pagina. In cazul de fata folosim rectangles care au aplicate un ImageBrush.Vom avea trei randuri de poze, iar reflexia va fi compusa din doua randuri peste care se va aplica un gradient de atenuare. Setam in proprietatea Tag valorile i si j sub forma un obiect Point(i, j), pentru a putea determina la care poza ne referim cand le accesam.
public void CreateControls()
{
for (double i = 0; i < count; i++)
{
for (double j = -2; j < 3; j++)
{
Rectangle rect = new Rectangle();
Point p = new Point(i, j);
rect.Tag = p;
ImageBrush brush = new ImageBrush();
brush.ImageSource = new BitmapImage(new Uri(@"Images/Pic" + ((i +
j) % 5 + 1) + ".jpg", UriKind.Relative));
rect.Fill = brush;
rect.SetValue(Canvas.ZIndexProperty, 3);
if (j < 0)
{
rect.SetValue(Canvas.ZIndexProperty, 0);
brush.Opacity = 0.45;
brush.ImageSource = new BitmapImage(new Uri(@"Images/Ref" +
((i - j - 1) % 5 + 1) + ".jpg", UriKind.Relative));
}
rect.MouseLeftButtonDown += new
MouseButtonEventHandler(button_MouseLeftButtonDown);
LayoutRoot.Children.Add(rect);
}
}
}
2. Realizarea transformarilor 3D.
Pana acum am creat doar obiecte 2D suprapuse, pe care le-am adaugat gridului LayoutRoot al paginii.
Parametrul a din codul urmator reprezinta unghiul curent al camerei (orientarea), in functie de care se face actualizarea ecranului. Pentru fiecare obiect adaugat in Grid, se obtin indicii i si j din proprietatea Tag care a fost setata in CreateControls(), asemanator elementelor dintr-o matrice. Plasarea imaginilor circular se face prin aplicarea unei translatii pe axa Z cu -1000 de unitati, urmata de aplicarea unei rotatii in jurul originii sistemului de coordonate. Unghiul este calculat in functie de coeficientul i si a valorii lui a.
public void UpdateControls(double a)
{
foreach (Rectangle rect in LayoutRoot.Children)
{
if (rect.Tag != null)
{
rect.Width = 240;
rect.Height = 180;
double i = ((Point)rect.Tag).X;
double j = ((Point)rect.Tag).Y;
PlaneProjection projection = new PlaneProjection();
rect.Projection = projection;
projection.LocalOffsetZ = -1000;
projection.GlobalOffsetY = -j * 195;
if (j < 0) projection.GlobalOffsetY -= 15;
projection.RotationY = a + (i - 3) * 15;
}
}
}
3. Realizarea animatiei
Pentru a realiza animatia, definim un timer in constructor. Algoritmul folosit este simplu: cand userul face click pe o poza, camera se va centra pe aceasta. Definim o variabila target (care va reprezenta unghiul destinatie) si in functie de diferenta dintre pozitia actuala si pozitia finala, se actualizeaza pozitia curenta. Daca ajungem la o valoare destul de apropiata de valoarea destinatie, oprim animatia.
timer.Interval = new TimeSpan(0, 0, 0, 0, 90);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
void timer_Tick(object sender, EventArgs e)
{
double delta = target - ang;
ang += delta * 0.11;
if (Math.Abs(delta) < 0.1) timer.Stop();
UpdateControls(ang);
}
Pentru a declansa animatia, avem nevoie de handlerul de click al pozelor.
void button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Rectangle button = (Rectangle)sender;
if (button.Tag != null)
{
double i = ((Point)button.Tag).X;
target = -(i - 3) * 15;
timer.Start();
}
}
Se poate vedea ca atunci cand facem click pe o poza, unghiul destinatie al camerei este calculat in functie de indicele i al pozei, in asa fel incat aceasta se va centra pe poza.
5. Realizarea efectului de lumina:
Presupune un gradient circular care trece de la gri la negru.
<Grid.Background>
<RadialGradientBrush GradientOrigin="0,0" Center="0.1,0.5">
<GradientStopCollection>
<GradientStop Color="#FF777777" Offset="0"></GradientStop>
<GradientStop Color="Black" Offset="1"></GradientStop>
</GradientStopCollection>
</RadialGradientBrush>
</Grid.Background>
6. Realizarea efectului de reflexie:
Presupune adaugarea unui gradient care trece din transparent in negru si are rolul de a atenua reflexia.
<Rectangle Canvas.ZIndex="1">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0, 0" EndPoint="0, 1">
<GradientStopCollection>
<GradientStop Color="#00000000" Offset="0"/>
<GradientStop Color="#F2000000" Offset="0.8"/>
<GradientStop Color="Black" Offset="1"/>
</GradientStopCollection>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
7. Adaugarea de suport pentru accelerare 3D
Acest lucru se face in fisierul html ce contine controlul Silverlight astfel:
<param name="minRuntimeVersion" value="3.0.40624.0" />
<param name="autoUpgrade" value="true" />
...
<param name="enableGPUAcceleration" value="true" />