一、图层绘制的若干细节
本文结合实例说明 Rosen 图层绘制的若干细节,以点带面,若全面了解需要深入阅读 MSDP 和 Graphic2D 子系统相关代码。
1.1. 绘制动画的数据来源
由 ARKUI 调用 MSDP 的 StartDrag()接口将 DragData 数据信息初始化到 DrawingInfo 全局变量。
1.2. 从设备 DPI 转换图片偏移位置
获取设备的 dpi,通过 deviceDpi 计算出缩放因子 scalingValue
int32_t deviceDpi = display->GetDpi();
将 pixelMap 的 Width 和 Height,通过 scalingValue,裁剪尺寸。
scalingValue=(1.0*deviceDpi*DEVICE_INDEPENDENT_PIXEL / BASELINE_DENSITY) / SVG_ORIGINAL_SIZE;
竖直方向增加一个 adjustSize 偏移量
adjustSize = TWELVE_SIZE * GetScaling();
SetBounds(DEFAULT_POSITION_X,adjustSize,pixelMap->GetWidth(),pixelMap->GetHeight());
SetFrame(DEFAULT_POSITION_X,adjustSize,pixelMap->GetWidth(),pixelMap->GetHeight());
1.3 图层与绘制节点
1.3.1. 绘制多图层的基本步骤
- 创建窗口节点 surfaceNode,设置背景颜色、大小、是否可见等信息;
- 创建根节点 rootNode,设置边框长宽大小、背景颜色等信息;
- 初始化画布;
- 再分别创建 nodes、filterNode、pixelMapNode、dragStyleNode、mouseIconNode 节点,设置相对应的节点参数信息。
1.3.2. nodes 节点存放节点信息
各节点 | 对应的下标常量 | 描述 |
---|---|---|
Rosen::RSCanvasNode filterNode | BACKGROUND_FILTER_INDEX | 裁剪节点 |
Rosen::RSCanvasNode pixelMapNode | PIXEL_MAP_INDEX | 阴影图节点 |
Rosen::RSCanvasNode dragStyleNode | DRAG_STYLE_INDEX | 拖拽样式节点 |
Rosen::RSCanvasNode mouseIconNode | MOUSE_ICON_INDEX | 鼠标光标节点 |
1.3.3. rootNode 节点存放节点信息
其中 multiSelectedNode 节点是根据 multiSelectedPixelMaps 数量给每个像素图片创建对应的节点
1.3.4. 拖拽多张图片的层叠顺序说明
1.4. 绘制阴影图的方法
1.4.1. SetShadow 系列方法
方法 | 功能 |
---|---|
SetShadowColor() | 设置阴影颜色 |
SetShadow0ffset() | 设置阴影偏移 |
SetShadowAlpha() | 设置阴影透明度 |
SetShadowRadius() | 设置阴影半径 |
SetShadowPath() | 设置阴影 path |
1.4.2. 阴影 path 说明
Path 本质上讲就是 SVG 的图片信息,就是如下字符串,只包含坐标尺寸部分。
<?xml version="1.0" encoding="UTF-8"?>
<svg width="300px" height="300px" viewBox="0 0 200 200">
<rect x="10" y="10" width="40" height="40" rx="4" ry="4"
style="stroke: gray; fill: lightgray;"/>
</svg>
用浏览器打开,显示如下:
1.5. 图层重叠的实现
1.5.1. 将 g_drawingInfo.multiSelectedPixelMaps 逐个图片添加到节点,然后保存到 multiSelectedNodes
保存节点前设置相应参数,例如 SetAlpha(alpha) 为设置透明度,SetRotation(degrees) 为设置旋转角度,SetForegroundColor(TRANSPARENT_COLOR_ARGB)为设置前景色
void DragDrawing::InitMultiSelectedNodes()
{
size_t multiSelectedPixelMapsSize = g_drawingInfo.multiSelectedPixelMaps.size();
for (size_t i = 0; i < multiSelectedPixelMapsSize; ++i) {
std::shared_ptr<Media::PixelMap> multiSelectedPixelMap = g_drawingInfo.multiSelectedPixelMaps[i];
std::shared_ptr<Rosen::RSCanvasNode> multiSelectedNode = Rosen::RSCanvasNode::Create();
multiSelectedNode->SetBgImageWidth(multiSelectedPixelMap->GetWidth());
multiSelectedNode->SetBgImageHeight(multiSelectedPixelMap->GetHeight());
multiSelectedNode->SetBgImagePositionX(0);
multiSelectedNode->SetBgImagePositionY(0);
multiSelectedNode->SetForegroundColor(TRANSPARENT_COLOR_ARGB);
auto rosenImage = std::make_shared<Rosen::RSImage>();
rosenImage->SetPixelMap(multiSelectedPixelMap);
rosenImage->SetImageRepeat(0);
multiSelectedNode->SetBgImage(rosenImage);
float alpha = DEFAULT_ALPHA;
float degrees = DEFAULT_ANGLE;
if (i == FIRST_PIXELMAP_INDEX) {
alpha = FIRST_PIXELMAP_ALPHA;
degrees = POSITIVE_ANGLE;
} else if (i == SECOND_PIXELMAP_INDEX) {
alpha = SECOND_PIXELMAP_ALPHA;
degrees = NEGATIVE_ANGLE;
}
multiSelectedNode->SetRotation(degrees);
multiSelectedNode->SetAlpha(alpha);
g_drawingInfo.multiSelectedNodes.emplace_back(multiSelectedNode);
}
}
1.5.2. 绘制前将 multiSelectedNodes 逐个添加到根节点
size_t multiSelectedNodesSize = g_drawingInfo.multiSelectedNodes.size();
for (size_t i = 0; i < multiSelectedNodesSize; ++i) {
g_drawingInfo.rootNode->AddChild(g_drawingInfo.multiSelectedNodes[i]);
}
1.5.2. 设置好参数,用 FlushImplicitTransaction()更新、重绘
Rosen::RSTransaction::FlushImplicitTransaction();
1.6. 图层错位的实现
后续目标会按照一定角度进行旋转,以形成错位效果。第一个图片为默认的角度,第二个图片根据设定的旋转角度 POSITIVE_ANGLE 进行旋转,第三个图片根据 NEGATIVE_ANGLE 角度进行旋转,达到层叠错位的效果。设置好属性后将节点添加到 g_drawingInfo.mutilSelectedNodes 中。
遍历 mutilSelectedPixelMapsSize,根据索引找到 FIRST_PIXELMAP_INDEX 和 SECOND_PIXELMAP_INDEX,设置不同的旋转角度和透明度。
constexpr float DEFAULT_ANGLE { 0.0f }; // 默认角度
constexpr float POSITIVE_ANGLE { 8.0f }; // 正向旋转角度
constexpr float NEGATIVE_ANGLE { -8.0f }; // 逆向旋转角度
1.7. 粘滞动画的实现
1.7.1. 使用延迟刷新技术,使得过程非一蹴而就
控制刷新的频率,避免过快的刷新导致拖尾效果不明显。不同层叠阴影缩略图的动画持续时间设置为不相同。在拖拽移动过程中,在前面的图片动画持续时间快于后面的图片,已达到拖尾的效果。定义动画的持续时长:
constexpr int32_t SHORT_DURATION { 55 };
constexpr int32_t LONG_DURATION { 90 };
遍历节点,设置第一个节点的动画持续效果为 SHORT_DURATION,其余的动画持续效果为 LONG_DURATION。
1.7.2. 使用动画缓入缓出技术,使得起止更加流畅,减少“失重”感
使用 Rosen::RSNode::Animate 对节点进行动画处理,参数 RSAnimationTimingCurve::EASE_IN_OUT 是一种动画时间曲线,用于描述动画的起始和结束阶段的速度变化。它是一种缓入缓出的曲线,意味着动画在开始和结束时速度较慢,而在中间阶段速度较快。这种时间曲线可以提供一种自然的、连续的视觉反馈,使动画看起来更加自然和流畅。在某些情况下,它还可以使动画的呈现更加平滑,从而增强用户的视觉体验。