概述
Visual
类是所有显示元素的基类,提供了绘图、转换、边框计算、裁剪和命中测试。但不支持事件处理、布局、样式、数据绑定和全球化。Visual3D
是所有3D可视元素的基类。
DrawingVisual
继承自ContainerVisual
是一个轻量绘图类,不提供事件和布局,适用于创建背景和剪贴画,使用DrawingContext
绘制,最后使用AddVisualChild
方法添加进VisualTree。
Drawing对象 | 描述 |
---|---|
GeometryDrawing |
绘制形状 |
ImageDrawing |
绘制图像 |
GlyphRunDrawing |
绘制文本 |
VideoDrawing |
播放音频或视频 |
DrawingGroup |
把以上绘制合并到单个复合图层 |
坐标系
2D坐标系:左上角为原点 (0,0),右方为+x,下方为+y
3D坐标系(右手):屏幕中心为原点(0,0,0),右方为+x,下方为-y,前方为-z
画笔
画笔类型
画笔 | 说明 | 支持 TileMode (平铺) | 支持 Freeze (只读) |
---|---|---|---|
SolidColorBrush |
纯色:用颜色填充区域 | 否 | 是 |
LinearGradientBrush |
线性渐变:使用GradientStop 指定渐变的颜色和位置 |
否 | 是 |
RadialGradientBrush |
径向渐变:同上 | 否 | 是 |
ImageBrush |
图像:使用Stretch 属性指定缩放模式 |
是 | 是 |
DrawingBrush |
图形:GeometryDrawing 、ImageDrawing 、GlyphRunDrawing 、VideoDrawing 、DrawingGroup (复合其他图形),可作为DrawingImage 的源供Image使用 |
是 | 是 |
VisualBrush |
继承自Visual 的控件都可以绘制,也可以把Visual属性绑定为其他命名的控件,复制绘图 |
是 | 当Visual 属性不是null时,不可冻结 |
画笔性能从高到底
尽量使用
ImageBrush
代替VisualBrush
和DrawingBrush
,在2D中可以同通过RenderTargetBitmap
可以将控件转换成Image
,然后再使用ImageBrush
。
SolidColorBrush
> LinearGradientBrush
> ImageBrush
> DrawingBrush
(缓存) > VisualBrush
(缓存) > RadialGradientBrush
> DrawingBrush
(未缓存) > VisualBrush
(未缓存)
画笔的Transform和RelativeTransform的区别
应用流程:
确定显示的区域(Viewbox) --> 图像输出投影到1x1的转换矩形上 --> 应用RelativeTransform --> 绘制转换后的输出投影到实际大小的区域 --> 应用Transform -->最终显示
RelativeTransform
是在投影到1x1时应用转换的,所以应用转换的位置参数都是可以看作是位置的比例,比如中心位置就是0.5。Transform
是在投影到实际区域大小后应用的,所以它的转换参数都是实际的位置。
FrameworkElement的LayoutTransform和RenderTransform
LayoutTransform
是在处理布局之前转换的,所以本元素转换后会改变本身的位置大小,会引起父元素的重新布局,父元素计算布局时会以当前元素转换后的大小计算。因为会影响父元素的重新布局所以对性能有影响。RenderTransform
是在布局完成后应用的,不会影响父元素的布局
缩略图
使用BitmapImage.DecodePixelWidth
设置加载图片的缩略图大小以节省内存
提升元素的呈现性能
一个比较复杂元素的含有很多子元素时,使用BitmapCache
分配给复杂元素的CacheMode
属性。
如果元素要重复给其他元素使用则使用BitmapCacheBrush
作为资源,分配这个元素给Target属性,然后配置BitmapCacheBrush的
BitmapCache`的属性。
命中测试
默认命中测试始终返回z顺序中最顶层的对象。
自定义命中测试:
- 重写
HitTestCore
方法,当需要命中测试(例如鼠标点击时)和调用VisualTreeHelper.HitTest
会调用此方法。可以只重写当前边框区域的命中测试,然后使用base.HitTestCore
使用默认方法测试当前呈现内容。 - 在
VisualTreeHelper.HitTest
中使用回调方法HitTestFilterCallback
、HitTestResultCallback
,适当的返回结构已达到自己的目的。
3D
一个3D场景的示例
xaml<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > <DockPanel> <Viewbox> <Canvas Width="321" Height="201"> <!-- The Viewport3D provides a rendering surface for 3-D visual content. --> <Viewport3D ClipToBounds="True" Width="150" Height="150" Canvas.Left="0" Canvas.Top="10"> <!-- Defines the camera used to view the 3D object. --> <Viewport3D.Camera> <PerspectiveCamera Position="0,0,2" LookDirection="0,0,-1" FieldOfView="60" /> </Viewport3D.Camera> <!-- The ModelVisual3D children contain the 3D models --> <Viewport3D.Children> <!-- This ModelVisual3D defines the light cast in the scene. Without light, the 3D object cannot be seen. Also, the direction of the lights affect shadowing. If desired, you can create multiple lights with different colors that shine from different directions. --> <ModelVisual3D> <ModelVisual3D.Content> <DirectionalLight Color="#FFFFFF" Direction="-0.612372,-0.5,-0.612372" /> </ModelVisual3D.Content> </ModelVisual3D> <ModelVisual3D> <ModelVisual3D.Content> <GeometryModel3D> <!-- The geometry specifies the shape of the 3D plane. In this sample, a flat sheet is created. --> <GeometryModel3D.Geometry> <MeshGeometry3D TriangleIndices="0,1,2 3,4,5 " Normals="0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 0,0,1 " TextureCoordinates="0,0 1,0 1,1 1,1 0,1 0,0 " Positions="-0.5,-0.5,0.5 0.5,-0.5,0.5 0.5,0.5,0.5 0.5,0.5,0.5 -0.5,0.5,0.5 -0.5,-0.5,0.5 " /> </GeometryModel3D.Geometry> <!-- The material specifies the material applied to the 3D object. In this sample a linear gradient covers the surface of the 3D object.--> <GeometryModel3D.Material> <MaterialGroup> <DiffuseMaterial> <DiffuseMaterial.Brush> <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5"> <LinearGradientBrush.GradientStops> <GradientStop Color="Yellow" Offset="0" /> <GradientStop Color="Red" Offset="0.25" /> <GradientStop Color="Blue" Offset="0.75" /> <GradientStop Color="LimeGreen" Offset="1" /> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </DiffuseMaterial.Brush> </DiffuseMaterial> </MaterialGroup> </GeometryModel3D.Material> <!-- Apply a transform to the object. In this sample, a rotation transform is applied, rendering the 3D object rotated. --> <GeometryModel3D.Transform> <RotateTransform3D> <RotateTransform3D.Rotation> <AxisAngleRotation3D Axis="0,3,0" Angle="40" /> </RotateTransform3D.Rotation> </RotateTransform3D> </GeometryModel3D.Transform> </GeometryModel3D> </ModelVisual3D.Content> </ModelVisual3D> </Viewport3D.Children> </Viewport3D> </Canvas> </Viewbox> </DockPanel> </Page>
3D性能优化
Viewport3D.ClipToBounds
: 如果不需要裁剪以适应父元素大小,则设置为FalseViewport3D.IsHitTestVisible
: 如果不需要响应鼠标位置检测,则设置为FalseGeometryModel3D
:在需要材质或者转换的时候,尽可能少创建新实例。如何多个使用相同的材质和变换,则合并到一个GeometryModel3D
或MeshGeometry3D
中。MeshGeometry3D
:在使用动画更改顶点时从Visual Tree中移除(detach),更改后再添加(reattach)进去。- 抗锯齿:如果不需要,设置
Viewport3D.EdgeMode
=Aliased
。默认4x抗锯齿。 - 文字:如果文字不改变的化,使用
RenderTargetBitmap
渲染文字,而不是使用DrawingBrush
或者VisualBrush
。 TileBrush
画笔:如果在3D场景使用DrawingBrush
或者VisualBrush
,设置CachingHint
=Cache
,设置CacheInvalidationThresholdMinimum
和CacheInvalidationThresholdMaximum
使得画笔不会频繁重新生成。BitmapEffect
:尽量不要使用,因为不能硬件加速。ImageBrush
:当使用ImageBrush
和RenderTargetBitmap
时,图像应尽可能的小,但是会降低图像质量,自己把握。Opactity
:在3d模型中使用透明度,尽量使用在画笔或材质上(Opacity
或Color
属性),不要使用Viewport3D.Opacity
。Viewport3D
:尽量合并模型在同一个Viewport3D
对象,不要创建很多分散开的对象。- Freeezable:对于不需要改变的笔刷、材质和其他实现
Freezable
的对象,调用Freeze
方法冻结,可以节省时间和空间。 BackMaterial
:背面材质,除非必要,不要使用。- 灯光性能:
AmbientLight
环境光 >DirectionalLight
日光 >PointLight
点光 >SpotLight
聚光灯。不要使用很多灯光,日光最多支持110,点光最多支持70,聚光最多支持40个,纯黑色的灯光没有用。 MeshGeometry3D
:Positions 最多20001个Point3D
实例,TriangleIndices 最多 60003个Int32
实例- 材质性能:
EmissiveMaterial
(自发光) >DiffuseMaterial
(漫反射) >SpecularMaterial
(镜面) MaterialGroup
:尽量减少当中的材质数量。Transform3DGroup
:如果不需要动画和数据绑定,使用MatrixTransform3D
代替。ModelVisual3D
:运动的物体或共享的GeometryModel3D
模型放入ModelVisual3D
中。GeometryModel3D
表示模型,ModelVisual3D
表示场景。
动画
From/To/By 动画
From:起始值,To:到达值,By:偏移值(可以单独使用或结合From使用)。
To和By结合使用时,忽略By。
关键帧动画
Linear(线性):在指定时间内,线性速度增加或减少值。
Discrete(离散):指定时间后,立即到设定值。
Spline(曲线):指定时间内,按指定的曲线控制(
KeySpline
)设置值,贝塞尔曲线,从(0,0)到(1,1)。
以上在一个关键帧动画中可以组合使用。
关键帧:
支持百分比 (例如:
80%
)作为KeyTime
的值,表示该帧停止的时间。支持特殊值
Uniform
作为KeyTime
的值,会平分整个动画的持续时间支持特殊值
Paced
作为KeyTime
的值,根据关键帧长度分配可用时间,会保持整个动画的变化速度。
属性类型 | 对应的 From/To/By 动画类 | 支持的内插方法 |
---|---|---|
Boolean | BooleanAnimationUsingKeyFrames | 离散 |
Byte | ByteAnimationUsingKeyFrames | 离散、线性、曲线 |
Color | ColorAnimationUsingKeyFrames | 离散、线性、曲线 |
Decimal | DecimalAnimationUsingKeyFrames | 离散、线性、曲线 |
Double | DoubleAnimationUsingKeyFrames | 离散、线性、曲线 |
Int16 | Int16AnimationUsingKeyFrames | 离散、线性、曲线 |
Int32 | Int32AnimationUsingKeyFrames | 离散、线性、曲线 |
Int64 | Int64AnimationUsingKeyFrames | 离散、线性、曲线 |
Matrix | MatrixAnimationUsingKeyFrames | 离散 |
Object | ObjectAnimationUsingKeyFrames | 离散 |
Point | PointAnimationUsingKeyFrames | 离散、线性、曲线 |
Quaternion | QuaternionAnimationUsingKeyFrames | 离散、线性、曲线 |
Rect | RectAnimationUsingKeyFrames | 离散、线性、曲线 |
Rotation3D | Rotation3DAnimationUsingKeyFrames | 离散、线性、曲线 |
Single | SingleAnimationUsingKeyFrames | 离散、线性、曲线 |
String | StringAnimationUsingKeyFrames | 离散 |
Size | SizeAnimationUsingKeyFrames | 离散、线性、曲线 |
Thickness | ThicknessAnimationUsingKeyFrames | 离散、线性、曲线 |
Vector3D | Vector3DAnimationUsingKeyFrames | 离散、线性、曲线 |
Vector | VectorAnimationUsingKeyFrames | 离散、线性、曲线 |
路径动画
通过PathGeometry属性提供路径输入
属性类型 | 相应的路径动画类 | 示例 |
---|---|---|
Double | DoubleAnimationUsingPath | 沿着路径针对对象进行动画处理(双精度动画) |
Matrix | MatrixAnimationUsingPath | 沿着路径针对对象进行动画处理(矩阵动画) |
Point | PointAnimationUsingPath | 沿着路径针对对象进行动画处理(点动画) |
自定义动画
- 自定义关键帧:继承一个关键帧对象,比如
DoubleKeyFrame
。 - 自定义动画:继承
AnimationTimeline
或者*AnimationBase
(例如:DoubleAnimationBase·)。 - 使用Per-Frame Callback:注册
CompositionTarget.Rendering
事件,获取当前帧的时间,设置要处理的属性,完全绕过WPF的动画系统。
缓动函数
可以继承
EasingFunctionBase
自定义缓动函数使用:设置动画(例如
DoubleAnimation
)的EasingFunction属性
预定义的缓动:
- BackEase:先退动画运动,然后再开始在指示的路径中进行动画处理。
- BounceEase:创建弹跳效果。
- CircleEase:使用循环函数创建加速和/或减速的动画。
- CubicEase:使用公式 f (t) = t3创建加速和/或减速的动画。
- ElasticEase:创建类似于弹簧前后振荡的动画,直到其进入 rest 状态。
- ExponentialEase:使用指数公式创建加速和/或减速的动画。
- PowerEase:使用公式 f (t) = tp 创建加速和/或减速的动画,其中 p 等于 Power 属性。
- QuadraticEase:使用公式 f (t) = t2创建加速和/或减速的动画。
- QuarticEase:使用公式 f (t) = t4创建加速和/或减速的动画。
- QuinticEase:使用公式 f (t) = t5创建加速和/或减速的动画。
- SineEase:使用正弦公式创建加速和/或减速的动画。
IsAdditive
IsAdditive=true
时 动画From
的值会累积到当前属性。
比如当前属性Width=50
,动画中From=100 To=200
。
IsAdditive=true
:动画播放时Width=50+100=150
,在等于250时结束。IsAdditive=false
:动画播放时width=100
,在等于200时结束。
IsCumulative
在多次重复的动画中,累计属性值,每次加上
To-From
的值,所以下一次的From
=本次的To
。
例如在RepeatBehavior="2x"
,From="10"
,To="15"
,动画播放两次
IsCumulative=true
:第一次From="10"
,To="15"
,第二次From="15"
,To="20"
IsCumulative=false
:两次都是From="10"
,To="15"
其他
在对属性进行动画处理后,有些时候,即使动画结束也无法更改该属性的值。这是因为,动画仍然在更改这个属性的值,可以删除动画或者指定FillBehavior=Stop
时间线类型 | 时钟类型 | 时钟用途 |
---|---|---|
Animation (继承自 AnimationTimeline) | AnimationClock | 为依赖属性生成输出值。 |
MediaTimeline | MediaClock | 处理媒体文件。 |
ParallelTimeline | ClockGroup | 分组和控制其子 Clock 对象 |
Storyboard | ClockGroup | 分组和控制其子 Clock 对象 |
AnimationTimeline
表示时间线,每一个时间线都会创建一个AnimationClock
对象。属性中描述动画的输出值、持续时间、开始时间、结束时间和其他动画信息。
AnimationClock
处理这些值,维护计时相关的状态,Clock 由TimeManager管理,每次时间更新都会去更新所有在线的Clock中的时间,然后Clock在TimeLine中获取当前动画应该的值,在使用多个Clock更改同一个属性时,Clock是从上一个Clock获得的值(需要应用HandoffBehavior=Compose
)。
音视频
播放器(媒体对象)
MediaElement
:控件型,可以在XAML中使用。MeidaPlayer
:主要用于Drawing
对象,不能在XAML中使用。
播放模式
播放模式由Clock属性确定,非null则是时钟模式,默认独立模式。
- 独立模式:
Source
可指定Uri
媒体,可控制和播放(对于MediaElement
需要设置LoadedBehavior=Manual
),可修改Positon
和SpeedRatio
。 - 时钟模式:媒体对象可以视为动画目标,通过
MediaTimeline
间接的设置媒体的Source
,在TimeLine种会创建相应的Clock,由Clock控制播放,不能直接使用媒体对象的控制方法。在Storyboard
中把MediaElement
设为目标也是这么加载的。
MediaPlayer示例
csharp// Create a VideoDrawing. MediaPlayer player = new MediaPlayer(); player.Open(new Uri(@"sampleMedia\xbox.wmv", UriKind.Relative)); VideoDrawing aVideoDrawing = new VideoDrawing(); aVideoDrawing.Rect = new Rect(0, 0, 100, 100); aVideoDrawing.Player = player; // Play the video once. player.Play();
参考
SkiaSharp:https://github.com/mono/SkiaSharp
跨平台的2D绘图API。
WinUI:https://docs.microsoft.com/zh-cn/windows/apps/winui/
Windows平台整合的一个Fluent Design 系统控件库,适用于WPF/Winforms/UWP,Win2D是其2D绘图API。目前WinUI 3 Project Reunion 0.5是第一个稳定支持的版本,WinUI3的目的是所有Window10上的任何桌面应用都能以相同的方式使用它们。