原文:在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;

}

在WPF里面实现以鼠标位置为中心缩放移动图片的更多相关文章

  1. JS-以鼠标位置为中心的滑轮放大功能demo1

    以鼠标位置为中心的滑轮放大功能demo1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &qu ...

  2. WPF备忘录(2)WPF获取和设置鼠标位置与progressbar的使用方法

    一.WPF 中获取和设置鼠标位置 方法一:WPF方法 Point p = Mouse.GetPosition(e.Source as FrameworkElement); Point p = (e.S ...

  3. WPF获取和设置鼠标位置与progressbar的使用方法

    一.WPF 中获取和设置鼠标位置 方法一:WPF方法 Point p = Mouse.GetPosition(e.Source as FrameworkElement); Point p = (e.S ...

  4. WPF,强制捕获鼠标事件,鼠标移出控件外依然可以执行强制捕获的鼠标事件

    在WPF中,只有鼠标位置在某个控件上的时候才会触发该控件的鼠标事件.例如,有两个控件都注册了MouseDown和MouseUp事件,在控件1上按下鼠标,不要放开,移动到控件2上再放开.在这个过程中,控 ...

  5. WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里?

    原文:WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里? 在 WPF 程序中,我们有 Mouse.GetPosition(IInputElement relativeTo) 方法可以拿到鼠标 ...

  6. WPF实现无窗体鼠标跟随

    原文:WPF实现无窗体鼠标跟随 上次的弹力模拟动画实现后,我觉得可以把这个弄得更好玩一些,我们可以让小球实时跟随着鼠标,并且还可以让窗口完全消失,让小球在桌面上飞来飞去. 这只需要一些简单的修改就可以 ...

  7. jq获取鼠标位置

    jq获取鼠标位置 <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  8. C++获取鼠标位置及全局检测鼠标行为

    1.获取鼠标位置(在屏幕的位置)  CPoint m_mouse; GetCursorPos(&m_mouse); 2. 屏幕转化为客户端(控件的相对位置)& 客户端位置转化为屏幕位置 ...

  9. [ucgui] 对话框5——鼠标位置和移动窗口

    >_<" 这节主要是获取鼠标的位置和把窗口设置为可以移动.其中设置窗口可以移动用FRAMEWIN_SetMoveable(hFrameWin, 1)就行了.而获得鼠标位置则是利用 ...

随机推荐

  1. bash - Logical_OR

    转载 https://bash.cyberciti.biz/guide/Logical_OR Logical OR   ← Logical AND Home Logical Not ! → Logic ...

  2. 【t057】任务分配

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 现有n个任务,要交给A和B完成.每个任务给A或给B完成,所需的时间分别为ai和bi.问他们完成所有的任 ...

  3. Xcode6.3 怎样使用Leaks查看内存泄露

    Xcode -> Open Developer Tool -> Instruments : Leaks: 选择要检測的程序: 界面详情:

  4. js进阶正则表达式7点数字字母空格(w d s)(小写表原意,大写表反义)(特殊字符要加反斜杠:var reg22=/\W/g)

    js进阶正则表达式7点数字字母空格(w d s)(小写表原意,大写表反义)(特殊字符要加反斜杠:var reg22=/\W/g) 一.总结 1.w d s,word digital space 2.特 ...

  5. 【32.89%】【codeforces 719A】Vitya in the Countryside

    time limit per test 1 second memory limit per test 256 megabytes input standard input output standar ...

  6. [Ramda] Convert a Promise.all Result to an Object with Ramda's zip and zipObj

    In this lesson, we'll use Promise.all to get an array that contains the resolved values from multipl ...

  7. one hot 编码的实现

    one hot 编码,针对的是类别性属性(categorical),类别型属性可以为特征向量中的任一属性,比如性别(one hot 编码的意义在于,属性之间不具有数值上大小的区别,在对最后结果的影响上 ...

  8. Quartz2D常见图形的绘制:线条、多边形、圆

    UI高级 Quartz2D http://ios.itcast.cn  iOS学院 掌握 drawRect:方法的使用 常见图形的绘制:线条.多边形.圆 绘图状态的设置:文字颜色.线宽等 图形上下文状 ...

  9. 物理引擎UIDynamic

    nUIDynamic   n什么是UIDynamic pUIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架 p可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象 ü重力. ...

  10. 【erlang 网络编程学习】 分析cowboy acceptor实现

    http://www.tuicool.com/articles/vuymei 不知道为什么就看了cowboy代码,就继续看了下去了. 分析一下吧,主要写写cowboy 的acceptor pool 的 ...