平々毎々(アーカイブ)

はてなダイアリーのアーカイブです。

Silverlight3のPerspective 3Dを使ってみる

matarillo.com: Silverlight3でiTunesのCoverFlow
何をやったかは下の画像を見れば一目瞭然かな。Silverlight3を入れてる人はクリックすれば動作する。
http://matarillo.com/blogdata/PseudoCoverFlow/

アルバムジャケット表示部分は、どうやらカスタムコントロール化しておくと具合がよさそうなのでそうした。
まずはXAML

<Canvas x:Name="LayoutRoot">
  <Canvas.RenderTransform>
    <ScaleTransform x:Name="Scaler" />
  </Canvas.RenderTransform>
  <Canvas Width="500" Height="750">
    <Canvas.RenderTransform>
      <TranslateTransform X="-250" Y="-375" />
    </Canvas.RenderTransform>
    <Canvas.Projection>
      <PlaneProjection x:Name="Rotator" CenterOfRotationX="0.5" />
    </Canvas.Projection>
    <Canvas.Clip>
      <RectangleGeometry Rect="0,0 500,750" />
    </Canvas.Clip>
    <Image x:Name="Image" Width="500" Height="500" />
    <Image x:Name="Reflection" Width="500" Height="500">
      <Image.RenderTransform>
        <ScaleTransform CenterY="500" ScaleY="-1" />
      </Image.RenderTransform>
    </Image>
    <Border Canvas.Top="500" Width="502" Height="500" Canvas.Left="-1" >
      <Border.Background>
        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
          <GradientStop Color="#44000000"/>
          <GradientStop Color="#FF000000" Offset="0.5"/>
        </LinearGradientBrush>
      </Border.Background>
    </Border>
    <Border x:Name="Darkness" Width="500" Height="750"
      Background="#FF000000" Opacity="0" Canvas.Top="0" Canvas.Left="0" />
  </Canvas>
</Canvas>

(Silverlight3で導入された)PlaneProjectionを使って奥行きをつけるのだが、その際にCanvas.Clipで反射部分を半分切り落としておいた。そうすると、消失点の高さがアルバム接地面よりすこし高くなって、見た目がいい感じになった。

で、コントロールはこう。DependencyPropertyのあたりは本質じゃないんで省略した。

public class AlbumCover : Control
{
  #region  簡易コード
  public static DependencyProperty SourceProperty;
  public static DependencyProperty RotationYProperty;
  public static DependencyProperty ScaleProperty;
  public static DependencyProperty DarknessProperty;
  public ImageSource Source { get; set; }
  public double RotationY { get; set; }
  public double Scale { get; set; }
  public double Darkness { get; set; }
  #endregion 簡易コード

  public AlbumCover()
  {
    DefaultStyleKey = typeof(AlbumCover);
  }

  public override void OnApplyTemplate()
  {
    base.OnApplyTemplate();
    ApplySource(this.Source);
    ApplyRotationY(this.RotationY);
    ApplyScale(this.Scale);
    ApplyDarkness(this.Darkness);
  }

  private void ApplySource(ImageSource value)
  {
    Image image =
      this.GetTemplateChild("Image") as Image;
    Image reflection =
      this.GetTemplateChild("Reflection") as Image;
    if (image == null || reflection == null) return;
    image.Source = value;
    reflection.Source = value;
  }

  private void ApplyRotationY(double value)
  {
    PlaneProjection rotator =
      this.GetTemplateChild("Rotator") as PlaneProjection;
    if (rotator == null) return;
    rotator.RotationY = value;
  }

  private void ApplyScale(double value)
  {
    ScaleTransform scaler =
      this.GetTemplateChild("Scaler") as ScaleTransform;
    if (scaler == null) return;
    scaler.ScaleX = value;
    scaler.ScaleY = value;
  }

  private void ApplyDarkness(double value)
  {
    Border darkness =
      this.GetTemplateChild("Darkness") as Border;
    if (darkness == null) return;
    darkness.Opacity = value;
  }
}

コントロールが出来てしまえば、あとはアニメーションをつけるだけ。
コントロールのプロパティについては

回転角
中央は0度, それ以外は±53度
縮小率
中央は45%, それ以外は32%
明るさ
中央は100%, それ以外は50%

とし、あとはCanvas.Left, Canvas.Top, Canvas.ZIndexを動かす。

このStoryboardはBlend3を使ってXAMLで表現したんだけど、数が多くてちょっとうんざりした。繰り返しが多くてDRYじゃないし。C#コードで生成したほうが簡単だったかも。