原文:在WPF里面实现以鼠标位置为中心缩放移动图片

在以前的文章使用WPF Resource以及Transform等技术实现鼠标控制图片缩放和移动的效果里面,介绍了如何在WPF里面移动和放大缩小图片,程序也支持使用滚轮的方式缩放图片。然而前面文章里介绍的缩放功能只能以图片中心为原点来实现,但是这种功能往往并不是客户想要的,我们看图片的时候,往往都喜欢以鼠标放在图片的焦点为原点进行图片的缩放。

咋看起来,实现这个功能也不是很难, ScaleTransform类里面定义了CenterX和CenterY两个属性就是用来设置缩放的原点坐标的。将这两个属性分别赋予鼠标的X, Y坐标值,就可以实现对原始图片,以鼠标位置为原点缩放图片了。但是,请注意,我说的原始图片是指没有移动之前的图片,如果图片缩放并且移动了,再次缩放的时候,就是另外一个故事了。

画个图说明一下吧,比如下图里面右下方方块是一个WPF程序里面的一个图片,大小是40 x 40,里面的黑点是预备缩放的原点,假设黑点的坐标是(10, 10),在运行程序的时候,用户首先将方块移动到左边的位置,当然原点(黑点)也移动了,假如这个时候图片移动了50个像素。

接着用户在移动后的位置上,将图片缩放,比如说放大了2倍,这个操作也会移动原点(黑点)在最终图片的位置。因为放大图片,实际上就是将原始图片的各个像素移动到新的位置(红点),这个时候,新的原点(红点)的坐标应该是(20, 20),相邻两个像素的空间使用插值的方法填充。这个时候,

ScaleTransform.ScaleX = 2;

ScaleTransform.ScaleY = 2;

这个时候,用户打算放大图片当中的另外一个区域,再放大一倍(即放大到原图的3倍),下图里是蓝点,假设坐标是(50, 50),因为无论图片缩放与否,用户只会以他在实际图片看到的内容来判断新的缩放焦点:

如果我们直接盲目地将ScaleTransform的各个属性设置为类似下面的值的话:

ScaleTransform.ScaleX = 3;

ScaleTransform.ScaleY = 3;

ScaleTransform.CenterX = 50;

ScaleTransform.CenterY = 50;

就发生问题了, 因为ScaleX = 3表示新图是原图的3倍,然而我们的原点却是在2倍图片上设置的—原图的大小只有40 x 40。解决方案当然是将蓝点的位置转换回在原始图片的位置,注意原始图片应该是下图右下方的图片,而不是左边的—用户最初已经移动了图片。

看起来转换起来有点麻烦,不过WPF提供了一个 函数TransformGroup.Inverse,可以把转换后图片上的坐标转换会在原始图片的坐标。当然啦,如果你熟悉图形学和线性代数的话,实际上,图片的缩放和移动就是将原始图片乘上一个矩阵,而TransformGroup.Inverse函数就是执行矩阵求逆操作。

下面就是关键代码:

XAML代码:

<Grid.Resources>

<TransformGroup x:Key="ImageCompareResources">

<ScaleTransform />

<TranslateTransform/>

</TransformGroup>

</Grid.Resources>

<ScrollViewer HorizontalScrollBarVisibility="Disabled"

VerticalScrollBarVisibility="Disabled" Grid.Row="0" Grid.Column="0" x:Name="MasterScrollViewer" Margin="5" Background="WhiteSmoke">

<ContentControl x:Name="TestContentControl1"

MouseLeftButtonDown="MasterImage_MouseLeftButtonDown"

MouseLeftButtonUp="MasterImage_MouseLeftButtonUp"

MouseMove="MasterImage_MouseMove"

MouseWheel="MasterImage_MouseWheel">

<Image RenderOptions.BitmapScalingMode="NearestNeighbor"

x:Name="MasterImage" Source="{Binding Path=MasterImagePath}" Stretch="Uniform"

RenderTransform="{StaticResource ImageCompareResources}"/>

</ContentControl>

</ScrollViewer>

C#代码:

private void MasterImage_MouseWheel(object sender, MouseWheelEventArgs e)

{

ContentControl image = sender as ContentControl;

if (image == null)

{

return;

}

TransformGroup group = ImageComparePanel.FindResource("ImageCompareResources") as TransformGroup;

Debug.Assert(group != null, "Can't find transform group from image compare panel resource");

Point point = e.GetPosition(image);

double scale = e.Delta * 0.001;

ZoomImage(group, point, scale);

}

private static void ZoomImage(TransformGroup group, Point point, double scale)

{

Debug.Assert(group != null, "Oops, ImageCompareResources is removed from current control's resouce");

Point pointToContent = group.Inverse.Transform(point);

ScaleTransform transform = group.Children[0] as ScaleTransform;

if (transform.ScaleX + scale < 1)

{

return;

}

transform.ScaleX += scale;

transform.ScaleY += scale;

TranslateTransform transform1 = group.Children[1] as TranslateTransform;

transform1.X = -1 * ((pointToContent.X * transform.ScaleX) - point.X);

transform1.Y = -1 * ((pointToContent.Y * transform.ScaleY) - point.Y);

}

private void MasterImage_MouseMove(object sender, MouseEventArgs e)

{

ContentControl image = sender as ContentControl;

if (image == null)

{

return;

}

if (this.isMouseLeftButtonDown && e.LeftButton == MouseButtonState.Pressed)

{

this.DoImageMove(image, e.GetPosition(image));

}

}

private void DoImageMove(ContentControl image, Point position)

{

TransformGroup group = ImageComparePanel.FindResource("ImageCompareResources") as TransformGroup;

Debug.Assert(group != null, "Can't find transform group from image compare panel resource");

TranslateTransform transform = group.Children[1] as TranslateTransform;

transform.X += position.X - this.previousMousePoint.X;

transform.Y += position.Y - this.previousMousePoint.Y;

this.previousMousePoint = position;

}

最新文章

  1. Jenkins与Hudson的关系
  2. BNUOJ 13105 nim博弈
  3. Tomcat中部署WEB项目的四种方法
  4. [App]Xamarin学习资料收集
  5. 关于部分应用无法向POJ提交代码的解决方案
  6. Hadoop 处理“Name node is in safe mode”问题(转)
  7. CSS知识点:选择符
  8. Listview源码分析(1)
  9. Java的容器类小结
  10. web基础笔记整理(一)
  11. screen乱码问题
  12. 关于php下的ajax赋值传值的调试
  13. snap7和plc的IP设置问题
  14. Centos 使用 docker
  15. 洛谷P2801 教主的魔法 分块
  16. W3CSchool CSS学习简记
  17. sql server还原到指定时间
  18. R语言学习笔记—组合数
  19. Asp.net让某一页设置成gb2312或utf-8的方法
  20. jsp实现仿QQ空间新建多个相冊名称,向相冊中加入照片

热门文章

  1. SocketChannel API用法
  2. php javascript的ajax
  3. CGI原理解析之二------WEB服务和CGI交互数据
  4. 于 Android NDK 的学习之旅-----数据传输(基本数据类型和数组传输)
  5. 【转】优先队列priority_queue 用法详解
  6. Ajax详解及使用Ajax时的返回值类型有哪些?
  7. BigDecimal 舍入模式(Rounding mode)介绍
  8. poj 2594 Treasure Exploration 二分图匹配
  9. Wpf的布局舍入属性(可以解决软件字体模糊的问题)
  10. 简洁常用权限系统的设计与实现(三):维护和利用节点的深度level,迭代实现树的构造