使用 Transforms 裁剪低分辨率图像并将其分配给我的用户控件
Clipping a low resolution image using Transforms and assigning it to my user control
我一直在开发一个 Windows 8.1 RT 应用程序,其中用户使用 Stretch=Uniform 加载图像。
图像可以尽可能小,也可以尽可能大。
剪辑发生在我的用户控件中,当我 tap/press 并按住 screen/image 时,我的用户控件出现。
当我点击并按住并在背景中的图像周围移动 finger/mouse 时,就会发生剪裁。
我面临的问题是
每当在 Emulator 上首次加载应用程序,并且第一次执行点击/单击并按住时,用户控件会出现在屏幕的左上角,并且然后它出现在我的 clicked/hold 区域上方。我需要的是它应该始终出现在我点击并按住的任何地方以及我点击并按住的任何时候。在释放 finger/mouse 时,它应该会崩溃。
我正在使用 center 变换。我希望鼠标当前所在的区域(像素)准确显示在用户控件的 center 中,如果我正在加载 480*800 甚至更小的小分辨率图像,我的鼠标区域没有进入中心。
为了更清楚,想象一下我正在点击并按住人类 eye.The 的 CORNEA 角膜应该显示在用户控件的中心,并且上方和下方的区域应该覆盖剩下的部分。
- 我不希望我的控件超出我的图像边界,如果我的鼠标位于图像的最后一个像素,则应该显示图像的某些部分和背景的某些部分。
我需要旋转控件,如图video
在下面找到完整的代码。
MainPage.XAML
<Page
x:Class="App78.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
x:Name="LayoutGrid"
Margin="0,0"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Holding="LayoutGrid_Holding"
PointerMoved="LayoutGrid_OnPointerMoved"
PointerWheelChanged="LayoutGrid_OnPointerWheelChanged"
PointerPressed="LayoutGrid_OnPointerPressed"
PointerReleased="LayoutGrid_OnPointerReleased">
<Image
x:Name="BigImage"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />
<local:Magnifier VerticalAlignment="Top" HorizontalAlignment="Left" x:Name="MagnifierTip" Visibility="Collapsed" />
</Grid>
</Page>
MAINPAGE.XAML.CS
using System;
using System.Diagnostics;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace App78
{
public sealed partial class MainPage : Page
{
private double zoomScale = 2;
private double pointerX = 0;
private double pointerY = 0;
private const double MinZoomScale = .25;
private const double MaxZoomScale = 32;
public MainPage()
{
this.InitializeComponent();
var bi = (BitmapImage)BigImage.Source;
bi.ImageOpened += bi_ImageOpened;
this.SizeChanged += MainPage_SizeChanged;
}
void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
this.UpdateImageLayout();
}
void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.UpdateImageLayout();
}
private void UpdateImageLayout()
{
var bi = (BitmapImage)BigImage.Source;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
this.BigImage.Stretch = Stretch.None;
}
else
{
this.BigImage.Stretch = Stretch.Uniform;
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
//DV: If pointer is not in contact we can ignore it
if (!e.Pointer.IsInContact) { return; }
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
private void UpdateMagnifier()
{
var bi = (BitmapImage)BigImage.Source;
double offsetX = 0;
double offsetY = 0;
double imageScale = 1;
var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
//imageScale = 1; - remains
}
else if (imageRatio < gridRatio)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
offsetY = 0;
imageScale = BigImage.ActualHeight / bi.PixelHeight;
}
else
{
offsetX = 0;
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
imageScale = BigImage.ActualWidth / bi.PixelWidth;
}
//DV: This is probably not need anymore
//MagnifierTip.MagnifierTransform.X = this.pointerX;
//MagnifierTip.MagnifierTransform.Y = this.pointerY;
MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
//DV: I haven't tested the Scaling/Zoom
MagnifierTip.ZoomTransform.ScaleX = imageScale * zoomScale;
MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;
MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
//DV: I added a GlobalGrid Transform which translates every children
MagnifierTip.MagnifierTransformGrid.X = this.pointerX - (MagnifierTip.ActualWidth / 2);
MagnifierTip.MagnifierTransformGrid.Y = this.pointerY - (MagnifierTip.ActualHeight); ;
}
private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
}
else
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
}
this.UpdateMagnifier();
}
//DV: Holding usually only works with touch https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.uielement.holding.aspx?f=255&MSPPError=-2147217396
private void LayoutGrid_Holding(object sender, HoldingRoutedEventArgs e)
{
//
}
//DV: pointer pressed supports both mouse and touch but fires immeadiatley. You'll have to figure out a delay strategy or using holding for touch and right click for mouse
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
//DV: pointer released supports both mouse and touch.
private void LayoutGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
}
}
Magnifier.XAML
<UserControl
x:Class="App78.Magnifier"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="230"
Width="170">
<Grid Height="230" Width="170">
<!-- DV: This is the global transform I added -->
<Grid.RenderTransform>
<TransformGroup>
<TranslateTransform x:Name="MagnifierTransformGrid" x:FieldModifier="public"/>
</TransformGroup>
</Grid.RenderTransform>
<Ellipse Opacity="1" Visibility="Visible" Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}" HorizontalAlignment="Center" VerticalAlignment="Top" IsHitTestVisible="False" Width="135" Height="128" StrokeThickness="3" Margin="0,17,0,0" />
<Ellipse x:Name="MagnifierEllipse" x:FieldModifier="public" Opacity="1" Visibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False" Width="150" Height="150" Stroke="White" StrokeThickness="3" Margin="11,8,0,0" >
<Ellipse.Fill>
<ImageBrush
ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
<ImageBrush.Transform>
<TransformGroup>
<TranslateTransform x:FieldModifier="public"
x:Name="CenterTransform"/>
<TranslateTransform x:FieldModifier="public"
x:Name="PositionTransform"/>
<ScaleTransform x:FieldModifier="public"
x:Name="ZoomTransform"/>
</TransformGroup>
</ImageBrush.Transform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
<Path Data="M25.533,0C15.457,0,7.262,8.199,7.262,18.271c0,9.461,13.676,19.698,17.63,32.338 c0.085,0.273,0.34,0.459,0.626,0.457c0.287-0.004,0.538-0.192,0.619-0.467c3.836-12.951,17.666-22.856,17.667-32.33 C43.803,8.199,35.607,0,25.533,0z M25.533,32.131c-7.9,0-14.328-6.429-14.328-14.328c0-7.9,6.428-14.328,14.328-14.328 c7.898,0,14.327,6.428,14.327,14.328C39.86,25.702,33.431,32.131,25.533,32.131z"
Fill="#FFF4F4F5"
Stretch="Fill"
Stroke="Black"
UseLayoutRounding="False"
Height="227"
Width="171" ></Path>
</Grid>
</UserControl>
此处 1 的修复是即使在
内也更新放大镜
LayoutGrid_OnPointerPressed
所以更新后的方法看起来像
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
这为您提供了您正在谈论的理想行为。
解决边界问题
根据 BitImage 而不是 LayoutGrid 计算上面的指针值。
if (this.pointerX < 0.0 || this.pointerY < 0.0)
return;
如果 (this.pointerX > BigImage.ActualWidth || this.pointerY > BigImage.ActualHeight)
return;
var bi = (BitmapImage)BigImage.Source;
接近边缘时的旋转就是仔细计算到达边缘时的旋转角度并应用该旋转变换。
MagnifierTip.RenderTransform = new RotateTransform{Angle=<CalculatedValue>, CenterX=<CalculatedValue>, CenterY=<CalculatedValue>};
希望这对您有所帮助。
我通过一些变化和三角学得到它 here。
MainPage.xaml
<Page
x:Class="MagnifierApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MagnifierApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
x:Name="LayoutGrid"
Margin="0,0"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
PointerMoved="LayoutGrid_OnPointerMoved"
PointerWheelChanged="LayoutGrid_OnPointerWheelChanged"
PointerPressed="LayoutGrid_OnPointerPressed"
PointerReleased="LayoutGrid_OnPointerReleased">
<Image
x:Name="BigImage"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />
<local:Magnifier
x:Name="MagnifierTip"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Visibility="Collapsed" />
</Grid>
</Page>
MainPage.xaml.cs
using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace MagnifierApp
{
public sealed partial class MainPage : Page
{
private double zoomScale = 2;
private double pointerX = 0;
private double pointerY = 0;
private const double MinZoomScale = .25;
private const double MaxZoomScale = 32;
private bool isFirst = true;
public MainPage()
{
this.InitializeComponent();
var bi = (BitmapImage)BigImage.Source;
bi.ImageOpened += bi_ImageOpened;
this.SizeChanged += MainPage_SizeChanged;
}
void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
this.UpdateImageLayout();
}
void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.UpdateImageLayout();
}
private void UpdateImageLayout()
{
var bi = (BitmapImage)BigImage.Source;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
this.BigImage.Stretch = Stretch.None;
}
else
{
this.BigImage.Stretch = Stretch.Uniform;
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
if (!e.Pointer.IsInContact)
{
return;
}
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
}
else
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
if (!e.Pointer.IsInContact) { return; }
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
private void LayoutGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
private void UpdateMagnifier()
{
var bi = (BitmapImage)BigImage.Source;
double offsetX;
double offsetY;
double imageScale;
CalculateImageScaleAndOffsets(bi, out offsetX, out offsetY, out imageScale);
MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
MagnifierTip.ZoomTransform.ScaleX = imageScale * zoomScale;
MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;
MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.MagnifierTranslateTransform.X = this.pointerX - (MagnifierTip.ActualWidth / 2);
MagnifierTip.MagnifierTranslateTransform.Y = this.pointerY - (MagnifierTip.ActualHeight);
bool tooHigh = MagnifierTip.MagnifierTranslateTransform.Y < 0;
bool tooLeft = MagnifierTip.MagnifierTranslateTransform.X < 0;
bool tooRight = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth > this.LayoutGrid.ActualWidth;
if (tooHigh || tooLeft || tooRight)
{
double angle = 0.0;
if (tooLeft && !tooHigh)
{
var dx = -MagnifierTip.MagnifierTranslateTransform.X;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = Math.Asin(dx / r);
angle = arad * 180 / Math.PI;
}
else if (tooLeft && tooHigh)
{
var dx = -MagnifierTip.MagnifierTranslateTransform.X;
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad1 = Math.Asin(dx / r);
var arad2 = Math.Acos((r - dy) / r);
var arad = Math.Max(arad1, arad2);
angle = arad * 180 / Math.PI;
}
else if (tooHigh && !tooRight)
{
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = Math.Acos((r - dy) / r);
if (MagnifierTip.MagnifierTranslateTransform.X + Math.Sin(arad) * r + MagnifierTip.ActualWidth > this.LayoutGrid.ActualWidth)
{
arad = -arad;
}
angle = arad * 180 / Math.PI;
}
else if (tooHigh)
{
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var dx = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth - this.LayoutGrid.ActualWidth;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad1 = -Math.Acos((r - dy) / r);
var arad2 = -Math.Asin(dx / r);
var arad = Math.Min(arad1, arad2);
angle = arad * 180 / Math.PI;
}
else //if (tooRight)
{
var dx = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth - this.LayoutGrid.ActualWidth;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = -Math.Asin(dx / r);
angle = arad * 180 / Math.PI;
}
MagnifierTip.RotateTransform.Angle = angle;
MagnifierTip.LensRotateTransform.Angle = -angle;
}
else
{
MagnifierTip.RotateTransform.Angle = 0;
MagnifierTip.LensRotateTransform.Angle = 0;
}
}
private void CalculateImageScaleAndOffsets(BitmapImage bi, out double offsetX, out double offsetY, out double imageScale)
{
var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
imageScale = 1;
}
else if (imageRatio < gridRatio)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
offsetY = 0;
imageScale = BigImage.ActualHeight / bi.PixelHeight;
}
else
{
offsetX = 0;
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
imageScale = BigImage.ActualWidth / bi.PixelWidth;
}
}
}
}
Magnifier.xaml
<UserControl
x:Class="MagnifierApp.Magnifier"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MagnifierApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="227"
Width="170">
<Grid
Height="227"
Width="170"
RenderTransformOrigin="0.5,1">
<Grid.RenderTransform>
<TransformGroup>
<RotateTransform
x:FieldModifier="public"
x:Name="RotateTransform"
Angle="0" />
<TranslateTransform
x:Name="MagnifierTranslateTransform"
x:FieldModifier="public" />
</TransformGroup>
</Grid.RenderTransform>
<Ellipse
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsHitTestVisible="False"
Width="152"
Height="152"
Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Margin="9,10,0,0"
RenderTransformOrigin="0.5,0.5"
StrokeThickness="0">
</Ellipse>
<Ellipse
x:Name="MagnifierEllipse"
x:FieldModifier="public"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsHitTestVisible="False"
Width="152"
Height="152"
Stroke="White"
Margin="9,10,0,0"
RenderTransformOrigin="0.5,0.5"
StrokeThickness="0">
<Ellipse.RenderTransform>
<RotateTransform
x:FieldModifier="public"
x:Name="LensRotateTransform"
Angle="0" />
</Ellipse.RenderTransform>
<Ellipse.Fill>
<ImageBrush
ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
<ImageBrush.Transform>
<TransformGroup>
<TranslateTransform
x:FieldModifier="public"
x:Name="PositionTransform" />
<ScaleTransform
x:FieldModifier="public"
x:Name="ZoomTransform" />
<TranslateTransform
x:FieldModifier="public"
x:Name="CenterTransform" />
</TransformGroup>
</ImageBrush.Transform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
<Path
Data="M85,11 C43.8548,11 10.5,44.3548 10.5,85.5 C10.5,126.645 43.8548,160 85,160 C126.145,160 159.5,126.645 159.5,85.5 C159.5,44.3548 126.145,11 85,11 z M85,0.5 C131.668,0.5 169.5,38.3319 169.5,85 C169.5,103.959 163.256,121.459 152.713,135.558 L151.895,136.625 L152,136.625 L84.2999,226 L18,136.625 L18.1054,136.625 L17.2872,135.558 C6.74375,121.459 0.5,103.959 0.5,85 C0.5,38.3319 38.3319,0.5 85,0.5 z"
Margin="0,0.5,0,0"
Stretch="Fill"
Stroke="Black"
UseLayoutRounding="False"
StrokeThickness="0"
Fill="White" />
</Grid>
</UserControl>
我一直在开发一个 Windows 8.1 RT 应用程序,其中用户使用 Stretch=Uniform 加载图像。 图像可以尽可能小,也可以尽可能大。 剪辑发生在我的用户控件中,当我 tap/press 并按住 screen/image 时,我的用户控件出现。 当我点击并按住并在背景中的图像周围移动 finger/mouse 时,就会发生剪裁。
我面临的问题是
每当在 Emulator 上首次加载应用程序,并且第一次执行点击/单击并按住时,用户控件会出现在屏幕的左上角,并且然后它出现在我的 clicked/hold 区域上方。我需要的是它应该始终出现在我点击并按住的任何地方以及我点击并按住的任何时候。在释放 finger/mouse 时,它应该会崩溃。
我正在使用 center 变换。我希望鼠标当前所在的区域(像素)准确显示在用户控件的 center 中,如果我正在加载 480*800 甚至更小的小分辨率图像,我的鼠标区域没有进入中心。 为了更清楚,想象一下我正在点击并按住人类 eye.The 的 CORNEA 角膜应该显示在用户控件的中心,并且上方和下方的区域应该覆盖剩下的部分。
- 我不希望我的控件超出我的图像边界,如果我的鼠标位于图像的最后一个像素,则应该显示图像的某些部分和背景的某些部分。
我需要旋转控件,如图video 在下面找到完整的代码。
MainPage.XAML
<Page
x:Class="App78.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
x:Name="LayoutGrid"
Margin="0,0"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Holding="LayoutGrid_Holding"
PointerMoved="LayoutGrid_OnPointerMoved"
PointerWheelChanged="LayoutGrid_OnPointerWheelChanged"
PointerPressed="LayoutGrid_OnPointerPressed"
PointerReleased="LayoutGrid_OnPointerReleased">
<Image
x:Name="BigImage"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />
<local:Magnifier VerticalAlignment="Top" HorizontalAlignment="Left" x:Name="MagnifierTip" Visibility="Collapsed" />
</Grid>
</Page>
MAINPAGE.XAML.CS
using System;
using System.Diagnostics;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace App78
{
public sealed partial class MainPage : Page
{
private double zoomScale = 2;
private double pointerX = 0;
private double pointerY = 0;
private const double MinZoomScale = .25;
private const double MaxZoomScale = 32;
public MainPage()
{
this.InitializeComponent();
var bi = (BitmapImage)BigImage.Source;
bi.ImageOpened += bi_ImageOpened;
this.SizeChanged += MainPage_SizeChanged;
}
void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
this.UpdateImageLayout();
}
void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.UpdateImageLayout();
}
private void UpdateImageLayout()
{
var bi = (BitmapImage)BigImage.Source;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
this.BigImage.Stretch = Stretch.None;
}
else
{
this.BigImage.Stretch = Stretch.Uniform;
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
//DV: If pointer is not in contact we can ignore it
if (!e.Pointer.IsInContact) { return; }
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
private void UpdateMagnifier()
{
var bi = (BitmapImage)BigImage.Source;
double offsetX = 0;
double offsetY = 0;
double imageScale = 1;
var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
//imageScale = 1; - remains
}
else if (imageRatio < gridRatio)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
offsetY = 0;
imageScale = BigImage.ActualHeight / bi.PixelHeight;
}
else
{
offsetX = 0;
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
imageScale = BigImage.ActualWidth / bi.PixelWidth;
}
//DV: This is probably not need anymore
//MagnifierTip.MagnifierTransform.X = this.pointerX;
//MagnifierTip.MagnifierTransform.Y = this.pointerY;
MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
//DV: I haven't tested the Scaling/Zoom
MagnifierTip.ZoomTransform.ScaleX = imageScale * zoomScale;
MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;
MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
//DV: I added a GlobalGrid Transform which translates every children
MagnifierTip.MagnifierTransformGrid.X = this.pointerX - (MagnifierTip.ActualWidth / 2);
MagnifierTip.MagnifierTransformGrid.Y = this.pointerY - (MagnifierTip.ActualHeight); ;
}
private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
}
else
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
}
this.UpdateMagnifier();
}
//DV: Holding usually only works with touch https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.uielement.holding.aspx?f=255&MSPPError=-2147217396
private void LayoutGrid_Holding(object sender, HoldingRoutedEventArgs e)
{
//
}
//DV: pointer pressed supports both mouse and touch but fires immeadiatley. You'll have to figure out a delay strategy or using holding for touch and right click for mouse
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
//DV: pointer released supports both mouse and touch.
private void LayoutGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
}
}
Magnifier.XAML
<UserControl
x:Class="App78.Magnifier"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App78"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="230"
Width="170">
<Grid Height="230" Width="170">
<!-- DV: This is the global transform I added -->
<Grid.RenderTransform>
<TransformGroup>
<TranslateTransform x:Name="MagnifierTransformGrid" x:FieldModifier="public"/>
</TransformGroup>
</Grid.RenderTransform>
<Ellipse Opacity="1" Visibility="Visible" Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}" HorizontalAlignment="Center" VerticalAlignment="Top" IsHitTestVisible="False" Width="135" Height="128" StrokeThickness="3" Margin="0,17,0,0" />
<Ellipse x:Name="MagnifierEllipse" x:FieldModifier="public" Opacity="1" Visibility="Visible" HorizontalAlignment="Left" VerticalAlignment="Top" IsHitTestVisible="False" Width="150" Height="150" Stroke="White" StrokeThickness="3" Margin="11,8,0,0" >
<Ellipse.Fill>
<ImageBrush
ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
<ImageBrush.Transform>
<TransformGroup>
<TranslateTransform x:FieldModifier="public"
x:Name="CenterTransform"/>
<TranslateTransform x:FieldModifier="public"
x:Name="PositionTransform"/>
<ScaleTransform x:FieldModifier="public"
x:Name="ZoomTransform"/>
</TransformGroup>
</ImageBrush.Transform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
<Path Data="M25.533,0C15.457,0,7.262,8.199,7.262,18.271c0,9.461,13.676,19.698,17.63,32.338 c0.085,0.273,0.34,0.459,0.626,0.457c0.287-0.004,0.538-0.192,0.619-0.467c3.836-12.951,17.666-22.856,17.667-32.33 C43.803,8.199,35.607,0,25.533,0z M25.533,32.131c-7.9,0-14.328-6.429-14.328-14.328c0-7.9,6.428-14.328,14.328-14.328 c7.898,0,14.327,6.428,14.327,14.328C39.86,25.702,33.431,32.131,25.533,32.131z"
Fill="#FFF4F4F5"
Stretch="Fill"
Stroke="Black"
UseLayoutRounding="False"
Height="227"
Width="171" ></Path>
</Grid>
</UserControl>
此处 1 的修复是即使在
内也更新放大镜LayoutGrid_OnPointerPressed
所以更新后的方法看起来像
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
这为您提供了您正在谈论的理想行为。
解决边界问题
根据 BitImage 而不是 LayoutGrid 计算上面的指针值。
if (this.pointerX < 0.0 || this.pointerY < 0.0) return; 如果 (this.pointerX > BigImage.ActualWidth || this.pointerY > BigImage.ActualHeight) return; var bi = (BitmapImage)BigImage.Source;
接近边缘时的旋转就是仔细计算到达边缘时的旋转角度并应用该旋转变换。
MagnifierTip.RenderTransform = new RotateTransform{Angle=<CalculatedValue>, CenterX=<CalculatedValue>, CenterY=<CalculatedValue>};
希望这对您有所帮助。
我通过一些变化和三角学得到它 here。
MainPage.xaml
<Page
x:Class="MagnifierApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MagnifierApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid
x:Name="LayoutGrid"
Margin="0,0"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
PointerMoved="LayoutGrid_OnPointerMoved"
PointerWheelChanged="LayoutGrid_OnPointerWheelChanged"
PointerPressed="LayoutGrid_OnPointerPressed"
PointerReleased="LayoutGrid_OnPointerReleased">
<Image
x:Name="BigImage"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Stretch="Uniform"
Source="http://blog.al.com/space-news/2009/04/iss015e22574.jpg" />
<local:Magnifier
x:Name="MagnifierTip"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Visibility="Collapsed" />
</Grid>
</Page>
MainPage.xaml.cs
using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
namespace MagnifierApp
{
public sealed partial class MainPage : Page
{
private double zoomScale = 2;
private double pointerX = 0;
private double pointerY = 0;
private const double MinZoomScale = .25;
private const double MaxZoomScale = 32;
private bool isFirst = true;
public MainPage()
{
this.InitializeComponent();
var bi = (BitmapImage)BigImage.Source;
bi.ImageOpened += bi_ImageOpened;
this.SizeChanged += MainPage_SizeChanged;
}
void MainPage_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
this.UpdateImageLayout();
}
void bi_ImageOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.UpdateImageLayout();
}
private void UpdateImageLayout()
{
var bi = (BitmapImage)BigImage.Source;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
this.BigImage.Stretch = Stretch.None;
}
else
{
this.BigImage.Stretch = Stretch.Uniform;
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerMoved(object sender, PointerRoutedEventArgs e)
{
if (!e.Pointer.IsInContact)
{
return;
}
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
if (e.GetCurrentPoint(this.LayoutGrid).Properties.MouseWheelDelta > 0)
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale * 1.2));
}
else
{
zoomScale = Math.Max(MinZoomScale, Math.Min(MaxZoomScale, zoomScale / 1.2));
}
this.UpdateMagnifier();
}
private void LayoutGrid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
if (!e.Pointer.IsInContact) { return; }
var point = e.GetCurrentPoint(this.LayoutGrid);
this.pointerX = point.Position.X;
this.pointerY = point.Position.Y;
this.UpdateMagnifier();
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
private void LayoutGrid_OnPointerReleased(object sender, PointerRoutedEventArgs e)
{
MagnifierTip.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
private void UpdateMagnifier()
{
var bi = (BitmapImage)BigImage.Source;
double offsetX;
double offsetY;
double imageScale;
CalculateImageScaleAndOffsets(bi, out offsetX, out offsetY, out imageScale);
MagnifierTip.PositionTransform.X = (-this.pointerX + offsetX) / imageScale;
MagnifierTip.PositionTransform.Y = (-this.pointerY + offsetY) / imageScale;
MagnifierTip.ZoomTransform.ScaleX = imageScale * zoomScale;
MagnifierTip.ZoomTransform.ScaleY = imageScale * zoomScale;
MagnifierTip.CenterTransform.X = MagnifierTip.MagnifierEllipse.ActualWidth / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.CenterTransform.Y = MagnifierTip.MagnifierEllipse.ActualHeight / 2 - MagnifierTip.MagnifierEllipse.StrokeThickness / 2;
MagnifierTip.MagnifierTranslateTransform.X = this.pointerX - (MagnifierTip.ActualWidth / 2);
MagnifierTip.MagnifierTranslateTransform.Y = this.pointerY - (MagnifierTip.ActualHeight);
bool tooHigh = MagnifierTip.MagnifierTranslateTransform.Y < 0;
bool tooLeft = MagnifierTip.MagnifierTranslateTransform.X < 0;
bool tooRight = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth > this.LayoutGrid.ActualWidth;
if (tooHigh || tooLeft || tooRight)
{
double angle = 0.0;
if (tooLeft && !tooHigh)
{
var dx = -MagnifierTip.MagnifierTranslateTransform.X;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = Math.Asin(dx / r);
angle = arad * 180 / Math.PI;
}
else if (tooLeft && tooHigh)
{
var dx = -MagnifierTip.MagnifierTranslateTransform.X;
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad1 = Math.Asin(dx / r);
var arad2 = Math.Acos((r - dy) / r);
var arad = Math.Max(arad1, arad2);
angle = arad * 180 / Math.PI;
}
else if (tooHigh && !tooRight)
{
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = Math.Acos((r - dy) / r);
if (MagnifierTip.MagnifierTranslateTransform.X + Math.Sin(arad) * r + MagnifierTip.ActualWidth > this.LayoutGrid.ActualWidth)
{
arad = -arad;
}
angle = arad * 180 / Math.PI;
}
else if (tooHigh)
{
var dy = -MagnifierTip.MagnifierTranslateTransform.Y;
var dx = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth - this.LayoutGrid.ActualWidth;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad1 = -Math.Acos((r - dy) / r);
var arad2 = -Math.Asin(dx / r);
var arad = Math.Min(arad1, arad2);
angle = arad * 180 / Math.PI;
}
else //if (tooRight)
{
var dx = MagnifierTip.MagnifierTranslateTransform.X + MagnifierTip.ActualWidth - this.LayoutGrid.ActualWidth;
var r = MagnifierTip.ActualHeight - MagnifierTip.ActualWidth / 2;
var arad = -Math.Asin(dx / r);
angle = arad * 180 / Math.PI;
}
MagnifierTip.RotateTransform.Angle = angle;
MagnifierTip.LensRotateTransform.Angle = -angle;
}
else
{
MagnifierTip.RotateTransform.Angle = 0;
MagnifierTip.LensRotateTransform.Angle = 0;
}
}
private void CalculateImageScaleAndOffsets(BitmapImage bi, out double offsetX, out double offsetY, out double imageScale)
{
var imageRatio = (double)bi.PixelWidth / bi.PixelHeight;
var gridRatio = this.LayoutGrid.ActualWidth / this.LayoutGrid.ActualHeight;
if (bi.PixelWidth < this.LayoutGrid.ActualWidth &&
bi.PixelHeight < this.LayoutGrid.ActualHeight)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - bi.PixelWidth);
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - bi.PixelHeight);
imageScale = 1;
}
else if (imageRatio < gridRatio)
{
offsetX = 0.5 * (this.LayoutGrid.ActualWidth - imageRatio * this.LayoutGrid.ActualHeight);
offsetY = 0;
imageScale = BigImage.ActualHeight / bi.PixelHeight;
}
else
{
offsetX = 0;
offsetY = 0.5 * (this.LayoutGrid.ActualHeight - this.LayoutGrid.ActualWidth / imageRatio);
imageScale = BigImage.ActualWidth / bi.PixelWidth;
}
}
}
}
Magnifier.xaml
<UserControl
x:Class="MagnifierApp.Magnifier"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MagnifierApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="227"
Width="170">
<Grid
Height="227"
Width="170"
RenderTransformOrigin="0.5,1">
<Grid.RenderTransform>
<TransformGroup>
<RotateTransform
x:FieldModifier="public"
x:Name="RotateTransform"
Angle="0" />
<TranslateTransform
x:Name="MagnifierTranslateTransform"
x:FieldModifier="public" />
</TransformGroup>
</Grid.RenderTransform>
<Ellipse
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsHitTestVisible="False"
Width="152"
Height="152"
Fill="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Margin="9,10,0,0"
RenderTransformOrigin="0.5,0.5"
StrokeThickness="0">
</Ellipse>
<Ellipse
x:Name="MagnifierEllipse"
x:FieldModifier="public"
HorizontalAlignment="Left"
VerticalAlignment="Top"
IsHitTestVisible="False"
Width="152"
Height="152"
Stroke="White"
Margin="9,10,0,0"
RenderTransformOrigin="0.5,0.5"
StrokeThickness="0">
<Ellipse.RenderTransform>
<RotateTransform
x:FieldModifier="public"
x:Name="LensRotateTransform"
Angle="0" />
</Ellipse.RenderTransform>
<Ellipse.Fill>
<ImageBrush
ImageSource="http://blog.al.com/space-news/2009/04/iss015e22574.jpg"
Stretch="None"
AlignmentX="Left"
AlignmentY="Top">
<ImageBrush.Transform>
<TransformGroup>
<TranslateTransform
x:FieldModifier="public"
x:Name="PositionTransform" />
<ScaleTransform
x:FieldModifier="public"
x:Name="ZoomTransform" />
<TranslateTransform
x:FieldModifier="public"
x:Name="CenterTransform" />
</TransformGroup>
</ImageBrush.Transform>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
<Path
Data="M85,11 C43.8548,11 10.5,44.3548 10.5,85.5 C10.5,126.645 43.8548,160 85,160 C126.145,160 159.5,126.645 159.5,85.5 C159.5,44.3548 126.145,11 85,11 z M85,0.5 C131.668,0.5 169.5,38.3319 169.5,85 C169.5,103.959 163.256,121.459 152.713,135.558 L151.895,136.625 L152,136.625 L84.2999,226 L18,136.625 L18.1054,136.625 L17.2872,135.558 C6.74375,121.459 0.5,103.959 0.5,85 C0.5,38.3319 38.3319,0.5 85,0.5 z"
Margin="0,0.5,0,0"
Stretch="Fill"
Stroke="Black"
UseLayoutRounding="False"
StrokeThickness="0"
Fill="White" />
</Grid>
</UserControl>