二维图形的矩阵变换(三)——在WPF中的应用矩阵变换
2021-07-02 10:09
标签:中介 图片 translate ali visual对象 osi 为我 question src UIElement和RenderTransform 首先,我们来看看什么样的对象可以进行变换。在WPF中,用于呈现给用户的对象的基类为Visual类,但是Visual对象并不具有变换功能,具有变换功能的是它的子类UIElement。这个类也是非常底层的类了,几乎我们所有的常用控件都是继承自它,也就是说,基本上所有的UI对象都是可以应用变换的。 然后,我们在再来看看UIElement中变换种类。UIElement支持两种变换:RenderTransform和LayoutTransform,其中LayoutTransform是会改变其布局,从而影响相邻的空间大小和位置的,如下图所示。 由于我们常用的是RenderTransfrom,并且两种变换的使用方式非常类似,下面的文章中就主要以RenderTransfrom作为介绍对象。下面的例子就简单的演示了其用法: StackPanel Orientation="Vertical">
Button Content="A Button" Opacity="1" />
Button Content="Rotated Button">
Button.RenderTransform>
Button.RenderTransform>
Button>
Button Content="A Button" Opacity="1" />
StackPanel>
矩阵变换MatrixTransform
前面的例子中演示了旋转的变换RotateTransform的用法,其它几种基本变换也有相对的变换类:ScaleTransform 、TranslateTransform 、SkewTransform。我们也可以将多个变换放到一个变换组中实现叠加的效果。
这些基本变换用法相对比较简单,这里就不多介绍了。下面介绍本文的重点:矩阵变换MatrixTransform。它的用法和RotateTransform实际上也差不多:
Button Content="Rotated Button">
Button.RenderTransform>
MatrixTransform x:Name="myMatrixTransform">
MatrixTransform.Matrix >
Matrix OffsetX="10" OffsetY="100" />
MatrixTransform.Matrix>
MatrixTransform>
Button.RenderTransform>
Button>
从上面的代码中可以看到,由于矩阵变换要设置六个值,并且这几个值不容易读,因此在XAML中使用显得非常不直观,大多数的时候我们是在代码中进行设置的。
单单从这个例子来看,是无法看出矩阵变换的什么优越性的。那是因为我们使用的变换比较简单,在前文二维图形的矩阵变换(一)——基本概念中介绍过,任何二维变换的序列均可存储于单个的 Matrix 对象,因此它是可以非常容易实现变换叠加效果的,下面就以我之前的文章用WPF实现一个简单的图片查看器中介绍到的例子用矩阵变换来改写一下。
这个例子的主要功能是实现一个支持鼠标拖动和滚轮缩放的图片查看器,在原文中是靠平移变换和缩放变换叠加实现的,这里用矩阵变换来实现一下。首先还是来看看XAML部分
Grid>
Image Source="source.jpg" MouseWheel="Image_MouseWheel" PreviewMouseLeftButtonDown="Image_MouseLeftButtonDown"
PreviewMouseMove="Image_MouseMove">
Image.RenderTransform>
MatrixTransform x:Name="transForm" />
Image.RenderTransform>
Image>
Grid>
然后就是事件的实现了:
private
void Image_MouseWheel(object sender, MouseWheelEventArgs e)
{
var center = getPosition(sender, e);
var scale = (e.Delta > 0 ? 1.2 : 1 / 1.2);
var matrix = transForm.Matrix;
matrix.ScaleAt(scale, scale, center.X, center.Y);
transForm.Matrix = matrix;
}
Point dragStart;
private
void Image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
dragStart = getPosition(sender, e);
}
private
void Image_MouseMove(object sender, MouseEventArgs e)
{
if ((e.LeftButton != MouseButtonState.Pressed))
{
return;
}
var current = getPosition(sender, e);
var offset = current - dragStart;
var matrix = transForm.Matrix;
matrix.Translate(offset.X, offset.Y);
transForm.Matrix = matrix;
dragStart = current;
}
Point getPosition(object sender, MouseEventArgs e)
{
return e.GetPosition(sender as
UIElement) * transForm.Matrix;
}