Silverlight实现类似Mac Dock特效

2019-01-25 15:17:23 浏览数 (1)

首先,看一下最终的效果图。我采用的开发环境是Visual Studio 2010。

其实,这里要点,主要有两个,一个是计算鼠标位置到每个图像子元素中心位置的函数,另一个是计算放大倍数,放大倍数是鼠标位置到到每个图像子元素中心位置的距离的,具体如下:

下面是计算标位置到每个图像子元素中心位置的距离二次函数

代码语言:javascript复制
/// <summary>
/// 计算鼠标位置到SilverDockItem元素的中心距离
/// </summary>
/// <param name="mouseArgs">The <see cref="MouseButtonEventArgs"/> instance containing the event data.</param>
/// <param name="target">The target.</param>
/// <returns></returns>
private double CalculateDistance(MouseEventArgs mouseArgs, SilverDockItem target)
{
	double mouseX = mouseArgs.GetPosition(target).X - (target.ActualWidth / 2);
	double mouseY = mouseArgs.GetPosition(target).Y - (target.ActualHeight / 2);
	double distance = Math.Sqrt(mouseX * mouseX   mouseY * mouseY);
	return distance;
}

下面是计算放大的倍数

代码语言:javascript复制
/// <summary>
/// 计算SilverDockItem元素的放大倍数
/// </summary>
/// <param name="maxZoom">放大倍数上限,即最大的放大倍数</param>
/// <param name="maxDistance">感知长度,超过这个距离后放大倍数保持在1</param>
/// <param name="distanceToItem">鼠标到目标Item中心的距离</param>
/// <returns></returns>
private double Zoom(double maxZoom, double maxDistance, double distanceToItem)
{
	//计算放大的倍数,这里放大倍数和鼠标到SilverDockItem元素的中心距离为二次函数关系
	double zoom = (maxZoom * (distanceToItem - maxDistance) * (distanceToItem - maxDistance)) / (maxDistance * maxDistance   1);

	//如果计算放大倍数小于1,返回1,即不进行放大也不缩小
	//也就是说鼠标到子元素中心距离大于一定值时就不进行缩放了
	if (zoom < 1)
	{
		return 1;
	}
	else
	{
		return zoom;
	}
}

具体开发过程如下:

1. 新建图像子元素SilverDockItem,代码如下:

代码语言:javascript复制
<UserControl x:Class="SilverControl.SilverDockItem"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Height="60" Width="60" SizeChanged="UserControl_SizeChanged">
    
    <Grid x:Name="LayoutRoot">
        <Rectangle d:DesignHeight="60" d:DesignWidth="60"  MouseMove="Rectangle_MouseMove" MouseLeave="Rectangle_MouseLeave">
            <Rectangle.Fill>
                <ImageBrush x:Name="ContentImage" Stretch="Fill" />
            </Rectangle.Fill>
        </Rectangle>
    </Grid>
</UserControl>
代码语言:javascript复制
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Imaging;

namespace SilverControl
{
    public partial class SilverDockItem : UserControl
    {
        public static readonly double ORIGNSIZE = 60;
        
        //宿主容器,即容纳SilverDock元素的父控件
        internal SilverDock Dock { get; set; }

        public SilverDockItem(String uri)
        {
            InitializeComponent();
            //设置图像的ImageSource依赖属性,即要显示的图像元素
            //下面两句是同样的效果
            //ContentImage.SetValue(ImageBrush.ImageSourceProperty, new BitmapImage(new Uri(uri, UriKind.Relative)));
            ContentImage.ImageSource = new BitmapImage(new Uri(uri, UriKind.Relative));
        }

        /// <summary>
        /// 计算SilverDockItem元素的放大倍数
        /// </summary>
        /// <param name="maxZoom">放大倍数上限,即最大的放大倍数</param>
        /// <param name="maxDistance">感知长度,超过这个距离后放大倍数保持在1</param>
        /// <param name="distanceToItem">鼠标到目标Item中心的距离</param>
        /// <returns></returns>
        private double Zoom(double maxZoom, double maxDistance, double distanceToItem)
        {
            //计算放大的倍数,这里放大倍数和鼠标到SilverDockItem元素的中心距离为二次函数关系
            double zoom = (maxZoom * (distanceToItem - maxDistance) * (distanceToItem - maxDistance)) / (maxDistance * maxDistance   1);

            //如果计算放大倍数小于1,返回1,即不进行放大也不缩小
            //也就是说鼠标到子元素中心距离大于一定值时就不进行缩放了
            if (zoom < 1)
            {
                return 1;
            }
            else
            {
                return zoom;
            }
        }

        /// <summary>
        /// 计算鼠标位置到SilverDockItem元素的中心距离
        /// </summary>
        /// <param name="mouseArgs">The <see cref="MouseButtonEventArgs"/> instance containing the event data.</param>
        /// <param name="target">The target.</param>
        /// <returns></returns>
        private double CalculateDistance(MouseEventArgs mouseArgs, SilverDockItem target)
        {
            double mouseX = mouseArgs.GetPosition(target).X - (target.ActualWidth / 2);
            double mouseY = mouseArgs.GetPosition(target).Y - (target.ActualHeight / 2);
            double distance = Math.Sqrt(mouseX * mouseX   mouseY * mouseY);
            return distance;
        }

        /// <summary>
        /// Handles the MouseMove event of the Rectangle control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
        private void Rectangle_MouseMove(object sender, MouseEventArgs e)
        {
            foreach (var item in Dock.items)
            {
                double distance = CalculateDistance(e, item);
                double zoom = Zoom(Dock.MaxZoom, Dock.MaxDistance, distance);
                item.Width = zoom * Dock.Size;
                item.Height = zoom * Dock.Size;
            }
        }

        /// <summary>
        /// Handles the MouseLeave event of the Rectangle control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
        private void Rectangle_MouseLeave(object sender, MouseEventArgs e)
        {
            foreach (var item in Dock.items)
            {
                item.Width = ORIGNSIZE;
                item.Height = ORIGNSIZE;
            }
        }

        /// <summary>
        /// Handles the SizeChanged event of the UserControl control.
        /// 当每个SilverDockItem元素的大小改变时,动态修改其承载元素StackPanel的长度,可以可以确保StackPanel始终居中显示
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="SizeChangedEventArgs"/> instance containing the event data.</param>
        private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            double totalWidth = 0;
            foreach (var item in Dock.items)
            {
                totalWidth  = item.Width;
            }
            Dock.ContentPanel.Width = totalWidth;
        }
    }
}

2.新建承载的StackPanel,代码如下:

代码语言:javascript复制
<UserControl x:Class="SilverControl.SilverDock"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Height="100" Width="900" BorderBrush="Black" BorderThickness="1">
    
    <Grid x:Name="LayoutRoot">
    	<Rectangle Fill="#55000000" Stroke="Black" RadiusX="9" RadiusY="9">
    		<Rectangle.Effect>
    			<DropShadowEffect/>
    		</Rectangle.Effect>
    	</Rectangle>
        <StackPanel x:Name="ContentPanel" Orientation="Horizontal" />
    </Grid>
</UserControl>
代码语言:javascript复制
using System.Collections.Generic;
using System.Windows.Controls;

namespace SilverControl
{
    public partial class SilverDock : UserControl
    {
        internal List<SilverDockItem> items = new List<SilverDockItem>();

        public double Size { get; set; } //SilverDockItem子元素的尺寸,这里SilverDockItem长宽相等
        public double MaxDistance { get; set; } //感知长度,超过这个距离后放大倍数保持在1
        public double MaxZoom { get; set; } //放大倍数上限,即最大的放大倍数

        public SilverDock()
        {
            InitializeComponent();
        }


        /// <summary>
        /// 给SilverDock工具条动态添加子元素item
        /// </summary>
        /// <param name="item">SilverDockItem子元素</param>
        public void AddItem(SilverDockItem item)
        {
            item.Dock = this;
            item.Height = Size;
            item.Width = Size;
            items.Add(item);
            ContentPanel.Children.Add(item);
        }

        /// <summary>
        /// 从SilverDock工具条中移除SilverDockItem子元素
        /// </summary>
        /// <param name="item">SilverDockItem子元素</param>
        public void RemoveItem(SilverDockItem item)
        {
            items.Remove(item);
            ContentPanel.Children.Remove(item);
        }
    }
}

3.主页面MainPage,代码如下:

代码语言:javascript复制
<UserControl x:Class="SilverControl.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">

    </Grid>
</UserControl>
代码语言:javascript复制
using System.Windows.Controls;


namespace SilverControl
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();


            SilverDock silverDock = new SilverDock();
            silverDock.MaxZoom = 1.5;
            silverDock.MaxDistance = 600;
            silverDock.Size = 60;
            silverDock.Size = 60;
            LayoutRoot.Children.Add(silverDock);


            for (int i = 0; i <= 13; i   )
            {
                SilverDockItem item = new SilverDockItem("images/"   i   ".png");
                silverDock.AddItem(item);
            }
        }
    }
}

有需要源码的同学可以从我的网盘下载。源码下载地址如下:http://yunpan.cn/QI7KHpACMfiXa

0 人点赞