前一阵无意中发现将iPod Nano5 横置后会出现一个动态的选歌界面(如下图示),感觉不错于是想用Silverlight来实现一下这个效果,欢迎大家拍砖。【示例代码下载】

本来是放Demo演示,可是这个程序怎么也显示不出来,只好用图片了(感兴趣的话可以下载源代码):

在制作过程中比较繁琐的部分是倒影效果,最开始的做法是将同一张专辑图片使用两次:一个作为专辑封面;一个作为倒影效果。对倒影效果图片进行RenderTransform->ScaleY 反转和OpacityMask->LinearGradientBrush 渐变处理,这样操作后出现一个问题:未选中专辑的封面和倒影之间有间隙(如下图示)。这是由于分别对封面和倒影进行PlaneProjection->RotationY 三维旋转时会使它们按各自的坐标轴进行旋转,不是以一个整体进行旋转而差生了偏差。如果将两个图片组合为一个StackPanel 或Canvas 然后再进行旋转感觉应该可以(但需要不少代码实现,效率可能会降低,感兴趣的朋友可以测试一下),最后偷懒用PS 给图片做了倒影~

下面先来看看XAML,这部分就是为Canvas 增加一些事件:
- <Canvas x:Name="LayoutRoot" MouseLeftButtonDown="LayoutRoot_MouseLeftButtonDown">
- </Canvas>
C# 内容请看注释详解:
- public partial class MainPage : UserControl
- {
- private String[] ALBUMS = { "images/ep1.png", "images/ep2.png", "images/ep3.png",
- "images/ep4.png", "images/ep5.png", "images/ep6.png",
- "images/ep7.png", "images/ep8.png", "images/ep9.png" };
- //当前位置
- private double CURRENT = 0;
- //目标位置
- private double TARGET = 0;
- //存储图片
- private List<Image> IMAGE = new List<Image>();
- //计时器
- private DispatcherTimer TIMER = new DispatcherTimer();
- public MainPage()
- {
- InitializeComponent();
- //加载图片
- addImages();
- //对于不同浏览器加载滚轮事件
- HtmlPage.Window.AttachEvent("DOMMouseScroll", LayoutRoot_MouseWheel);
- HtmlPage.Window.AttachEvent("onmousewheel", LayoutRoot_MouseWheel);
- HtmlPage.Document.AttachEvent("onmousewheel", LayoutRoot_MouseWheel);
- }
- private void addImages()
- {
- for (int i = 0; i < ALBUMS.Length; i++)
- {
- //逐一加载图片
- string url = ALBUMS[i];
- Image cover = new Image();
- cover.Source = new BitmapImage(new Uri(url, UriKind.Relative));
- LayoutRoot.Children.Add(cover);
- //调整图片显示方式
- setImage(cover, i);
- //将图片加入IMAGE,用于实现动态效果
- IMAGE.Add(cover);
- }
- }
- private void setImage(Image image, int index)
- {
- //遍历图片与当前(CURRENT)位置关系
- double offset = index - CURRENT;
- //对图片进行三维旋转
- PlaneProjection planeProjection = new PlaneProjection();
- planeProjection.RotationY = Math.Abs(offset) * 65 / (offset != 0 ? offset : 1);
- image.Projection = planeProjection;
- double left;
- double top = 50;
- double center = Width / 2 - 100;
- double scale = 1;
- if (index == CURRENT)
- {
- left = center;
- top = 40;
- scale = 1.15;
- }
- else if (index > CURRENT)
- {
- left = center + offset * 50 + 60;
- }
- else
- {
- left = center + offset * 50 - 40;
- }
- //对CURRENT图片进行缩放
- ScaleTransform scaleTransform = new ScaleTransform();
- scaleTransform.ScaleX = scale;
- scaleTransform.ScaleY = scale;
- image.RenderTransform = scaleTransform;
- //调整遍历图片位置
- image.SetValue(Canvas.LeftProperty, left);
- image.SetValue(Canvas.TopProperty, top);
- image.SetValue(Canvas.ZIndexProperty, (int)(-Math.Abs(offset)) * 100);
- }
- //点击鼠标左键正向移动图片
- private void LayoutRoot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
- {
- moveIndex(1);
- }
- /*
- * 如果将下面事件加入Canvas的MouseWheel="LayoutRoot_MouseWheel"(XAML)中,
- * 页面加载后初始不能获取滚轮事件。
- */
- //private void LayoutRoot_MouseWheel(object sender, MouseWheelEventArgs e)
- //{
- // double mouseDelta = 0;
- // mouseDelta = Math.Sign(e.Delta);
- // moveIndex((mouseDelta > 0) ? 1 : -1);
- //}
- //通过HtmlPage加载鼠标滚动事件,详情可参考资料<4>
- private void LayoutRoot_MouseWheel(object sender, HtmlEventArgs args)
- {
- double mouseDelta = 0;
- ScriptObject e = args.EventObject;
- // Mozilla and Safari
- if (e.GetProperty("detail") != null)
- {
- mouseDelta = ((double)e.GetProperty("detail"));
- }
- // IE and Opera
- else if (e.GetProperty("wheelDelta") != null)
- mouseDelta = ((double)e.GetProperty("wheelDelta"));
- mouseDelta = Math.Sign(mouseDelta);
- moveIndex((mouseDelta > 0) ? 1 : -1);
- }
- private void moveIndex(int value)
- {
- //获取目标图片位置,并确保其在图片数量范围内
- TARGET += value;
- TARGET = Math.Max(0, TARGET);
- TARGET = Math.Min(IMAGE.Count - 1, TARGET);
- }
- //计时器Tick,移动到目标图片
- private void timerTick(object sender, EventArgs e)
- {
- for (int i = 0; i < IMAGE.Count; i++)
- {
- Image image = IMAGE[i];
- setImage(image, i);
- }
- CURRENT = TARGET;
- }
- //启动计时器
- public void startShow()
- {
- TIMER = new DispatcherTimer();
- TIMER.Interval = new TimeSpan(0, 0, 0, 0, 10);
- TIMER.Tick += new EventHandler(timerTick);
- TIMER.Start();
- }
- }