Scale说明
在WinUI3中可以通过Scale来对图像进行缩放,Scale的缩放不会去修改原图像数据,而是根据锚点和要缩放的大小(可以是变大也可以是变小)计算新的要显示的坐标系进行渲染。
Scale的概念是针对于渲染层的,它主要针对于渲染器,告诉渲染器在渲染某个控件时对要显示的坐标系进行缩放,但控件的原始坐标、宽高不会发生任何变化,仅仅是渲染层的效果。
这里有一个重要点需要说明一下,Scale是属于渲染层的概念,它不属于图像类,于Resize不同,Resize是会修改图像实际宽高并使用插值算法进行填充,Scale不会修改图像的任何数据,仅仅是在渲染时将渲染系得坐标进行修改,使其视觉上尺寸变了,但实际上的宽度、坐标、控件数据不会有任何改变,仅仅是渲染时根据设置的锚点以及缩放比例在渲染时候会进行公式计算,其公式如下:
x′=cx+(x−cx)×sx
y′=cy+(y−cy)×sy
x, y:原始坐标
cx, cy:缩放锚点坐标
sx, sy:缩放因子
假设一个控件左上角是 (0,0),宽高是100×100,我们想以中心缩放2倍:
cx = 50, cy = 50
sx = 2.0, sy = 2.0
控件右下角 (100,100) 缩放后:
x' = 50 + (100 - 50) * 2 = 50 + 50 * 2 = 150
y' = 50 + (100 - 50) * 2 = 50 + 50 * 2 = 150
计算后x/y的像素会得到新的渲染坐标,然后从要缩放的锚点开始一步一步扩散计算,其中在渲染时渲染器会根据渲染时坐标是扩大还是扩小来决定是填充像素还是删除像素,由渲染器决定,这些仅仅是发生在渲染时,所以它不会改变原本的数据。
通常我们会用来缩放Image控件,当我们对Image控件进行缩放时会发现图像跟着一起变大了,注意这里不是图像变大了,是因为控件的渲染坐标变了,而图像属于Image的Visual层数据,也就是视图数据,渲染器会渲染这部分数据到界面上,所以看起来图像像是被Resize了,实际上只是渲染大小变换的结果,真实的图像数据并没有被改变,当你尝试保存Visual的数据到本地时会发现图像大小以及数据没有任何变化。
**Tips
锚点是Scale的里的原点,也就是以这个点为坐标开始进行缩放,该点的坐标不变,以该点像素向四周扩散,根据公式锚点的像素坐标会加上像素坐标-锚点坐标(这一步是为了把新的坐标以锚点为原点进行偏移),离锚点近的变化就越小,离锚点远的变化就越大
在WinUI3上面使用Scale
想要在WinUI3上面使用它也非常简单,有两种方式,一种是静态的也就是XAML方式,第二种是代码方式,首先说第一种静态的,首先定义一张Image:
<Image Source="test.jpg" Stretch="UniformToFill">
</Image>
运行效果如下:

Scale可以在控件元素里使用RenderTransform模块,RenderTransform是一个用来对控件在 渲染阶段 进行几何变换的属性,例如旋转、缩放、平移、倾斜等。它不会影响控件的布局位置和大小,只是改变显示时的外观,也就是渲染层的模块,在使用Scale缩放时它会使用控件的RenderTransformOrigin属性来作为原点:
<Image Source="test.jpg" Stretch="UniformToFill" RenderTransformOrigin="0.0,0.0">
<Image.RenderTransform>
<ScaleTransform ScaleX="1.1" ScaleY="1.1" />
</Image.RenderTransform>
</Image>
运行效果:

可以看到图像渲染扩展到了下面的控件上面去了,我使用的是StackPanel,无视了StackPanel的布局规则,因为这是渲染坐标系变了但实际上的宽高坐标并没有变,所以没有受到StackPanel规则的限制,这样的机制就像C语言里使用指针指向const变量,然后通过指针修改该值并不违规一样的原理。
如果想通过代码实现XAML的方式也非常简单,其实代码与XAML是对应的,只不过是WinRT解释器会将XAML代码翻译成WinRT代码,我们需要使用Media功能,首先需要包含头文件以及声明对应的名字空间:
#include <winrt/Microsoft.UI.Xaml.Media.h>
using namespace Microsoft::UI::Xaml::Media;
下面是缩放代码示例:
auto scale = ScaleTransform();
scale.ScaleX(1.1);
scale.ScaleY(1.1);
TestImage().RenderTransformOrigin({ 0.0, 0.0 });
TestImage().RenderTransform(scale);
ScaleTransform是缩放比例描述类,TestImage是要变换的控件,也是使用RenderTransform功能。
在代码实现缩放上还有另外一种方式,这个方式比较特别,它的缩放是使用控件的Visual来实现的,这些类型在前几篇文章中有过介绍,这里就不过多讲解了。
下面是它的一个代码示例:
auto visual2 = ElementCompositionPreview::GetElementVisual(TestImage());
// 设置原点,x、y、z
visual2.CenterPoint({
static_cast<float>(yu2().ActualWidth()),
static_cast<float>(yu2().ActualHeight()),
0.0f
});
visual2.Scale({ 1.8f, 1.8f, 1.0f });
Visual是较为底层的实现,它的变换不会影响坐标系,仅对图像实际有效Visual进行变换:

可以看到图像中心区域被放大了,实现了类型图像放大的功能,但渲染坐标并没有发生变化。
搭配上动画效果
当图像搭配上Scale动画时会有不错的效果,当图片是山或轮廓分明的作为入场动画效果时缩放过程能突出主体,产生明显的视觉冲击力。
下面是使用visual实现的动画效果:

可以看到当加了这个效果之后图像控件更能突出主体,产生明显的视觉冲击力。
下面是完整代码的实现, 动画效果使用compositor的Vector3KeyFrameAnimation实现:
auto visual2 = ElementCompositionPreview::GetElementVisual(TestImage());
auto compositor2 = visual2.Compositor();
visual2.CenterPoint({
static_cast<float>(yu2().ActualWidth() / 2.0),
static_cast<float>(yu2().ActualHeight() / 2.0),
0.0f
});
visual2.Scale({ 1.1f, 1.1f, 1.0f });
auto scaleAnim2 = compositor2.CreateVector3KeyFrameAnimation();
scaleAnim2.InsertKeyFrame(1.0f, { 1.0f, 1.0f, 1.0f });
scaleAnim2.Duration(std::chrono::milliseconds(700));
visual2.StartAnimation(L"Scale", scaleAnim2);
上面的代码使用的是compositor的动画方法,也可以使用纯XAML实现,在XAML里可以使用Storyboard来实现动画,通过Triggers来监听事件然后执行对应的动画:
<Grid>
<Image Source="test.jpg" Stretch="UniformToFill" RenderTransformOrigin="0.0,0.0">
<Image.RenderTransform>
<ScaleTransform x:Name="imgScale" ScaleX="1.1" ScaleY="1.1" />
</Image.RenderTransform>
</Image>
<Grid.Triggers>
<EventTrigger RoutedEvent="Image.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="imgScale"
Storyboard.TargetProperty="ScaleX"
To="1.0"
Duration="0:0:1.0"
EasingFunction="{StaticResource EaseOut}" />
<DoubleAnimation Storyboard.TargetName="imgScale"
Storyboard.TargetProperty="ScaleY"
To="1.0"
Duration="0:0:1.0"
EasingFunction="{StaticResource EaseOut}" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>
上面代码使用了EasingFunction,EasingFunction是WinUI3里面设置曲线动画的属性,这样让动画不是线性的,看起来会更加自然:
<Page.Resources>
<ExponentialEase x:Key="EaseOut" EasingMode="EaseOut" Exponent="4"/>
</Page.Resources>
XAML可以说提供了足够的可扩展性,写少量的代码实现复杂的功能,更多的时候建议使用XAML来实现UI方面的功能,因为这样才符合MVVM模型。
MVVM模型用WinUI3来说,一句话:XAML负责UI,C++负责业务,通过DataBind实现数据交互。
这样代码耦合度直线降低逻辑清晰。
178

被折叠的 条评论
为什么被折叠?



