GaiaX开源解读 | 给Stretch(Rust编写的Flexbox布局引擎)新增特性,我掉了好多头发

本文主要是介绍GaiaX开源解读 | 给Stretch(Rust编写的Flexbox布局引擎)新增特性,我掉了好多头发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

GaiaX(盖亚),是在阿里文娱内广泛使用的Native动态化方案,其核心优势是性能、稳定和易用。本系列文章《GaiaX开源解读》,带大家看看过去三年GaiaX的发展过程。

GaiaX的布局方案 - Flexbox

阿里文娱业务作为一个内容分发、消费综合体,不仅覆盖Phone端,在Pad、OTT、Mac、车机、IOT带屏设备上都为广大用户提供在线视频媒体服务。
GaiaX作为文娱分发场景的一个重要端渲染解决方案,在进行布局技术方案设计时,必须充分考虑多端、多屏的响应式布局诉求。众所周知,浏览器场景很好的解决了屏幕窗口多尺寸的动态布局问题,其采用的布局方案即为Flexbox。
在这里插入图片描述

移动端原生Frame布局是Native方案的最优解,因为双端可最大限度的发挥OS的渲染特性来保证渲染性能。为了验证Flexbox的实测性能,我们针对单层及多层嵌套设计了测试用例,通过数据表现(耗时单位为ms)来进一步证明Flexbox方案是否可以作为GaiaX的布局方案。
在这里插入图片描述

实验结果的产出,完全打消了我们对Flexbox在性能上的顾虑,再加之其符合W3C规范、社区丰富、学习复杂度低等优势,最终团队选择Flexbox作为GaiaX的布局技术方案。

Flexbox高性能解析方案 - Stretch

为什么选择Stretch作为布局基础

在确定了布局方案后,接下来要做的事就是确定解析的技术选型。 社区支持Flexbox布局解析的技术方案,最为大家耳熟能详的是facebook推出的yoga,这也是业内的主流技术选型。
相对于yoga来说,Strectch具有以下的优势:

● 包大小体积
● 支持多线程的局部计算
● 支持跨平台的能力(iOS、Andriod、JS等)
● 基于rust语言实现,具备高性能,低内存占用等特性

Stretch的这些的优势是基于Rust语言特性所带来的:
更安全的内存管理:
与 C/C++ 语言相比,使用 Rust 语言来进行程序设计可以有助于从源头预防出现诸如空指针,缓存溢出和内存泄漏的内存问题。
更好的运行性能:
与 Java/C# 等语言相比,Rust 语言的内存管理不是依靠垃圾回收器机制( GC )来实现的。这个设计提高了程序运行的性能。
原生支持多线程开发:
Rust 语言的所有权机制和内存安全的特性为没有数据竞争的并发提供了语言层面上的原生支持。

网上已经有了很多关于Rust性能分析对比的文章,以下就是性能对比结果,性能上可以跟C++性能不相上下,某些场景下效率甚至优于C++。

Rust vs C++:
在这里插入图片描述

Stretch入门

由于Stretch是采用Flexbox布局的方式,在开始介绍Stretch核心布局计算逻辑之前,还需要简单了解一下Flexbox的基础概念
● 采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item)。
● 容器默认存在两根轴:
○ 水平的主轴(main axis):主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;
○ 垂直的交叉轴(cross axis):交叉轴的开始位置叫做cross start,结束位置叫做cross end。
● 项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。
Flexbox解析布局逻辑流程,实际上就是处理Flex容器和Flex项目的尺寸、排列方向、对齐方式、比例关系、绝对布局项目的代码逻辑。

Stretch实现原理

Flexbox布局解析主链路

在这里插入图片描述

核心算法

在这里插入图片描述

Stretch重点剖析

在Stretch整个布局算法中,有9个主要的环节,接下来我们重点介绍三个重要的环节,
● 确定Flex项目的基准值(flex_basis)
● Flex项目的主轴尺寸
● Flex项目的交叉轴尺寸

确定Flex项目的基准值(flex_basis)

在该阶段,会生成Flex项目集合用作逻辑处理,随后会遍历该集合,给每个Flex项目的flex_basis赋值,Flex项目有了初始值之后会便于后续主轴和交叉轴的处理。
每个Flex项目的flex_basis的值,主要受到以下几个方面影响:
● 如果flex_basis已经有值,则直接使用。
● 如果align-items被设置stretch,那么flex项目的高度会默认被拉伸到最大元素的高度。
● 如果有设置aspect-ratio值,那么需要根据宽度计算出高度赋给flex_basis,或者根据高度计算出宽度赋给flex_basis。

确定Flex项目的主轴尺寸

当有了flex_basis之后,需要进一步确定flex项目主轴尺寸 - target_main_size。
首先要确认当前flex容器中已经使用的空间,以及当前Flex容器中剩余的空间,然后根据Flex项目的Flex因子来收缩和扩展Flex项目的主轴尺寸。
所谓Flex因子就是flex_grow和flex_shrink。
如果使用flex_grow,需要计算Flex项目的增长比例系数,并结合剩余空间计算出Flex项目的增长值,加上Flex项目的基准值,作为Flex项目的主轴尺寸。
Flex项目的增长长度与flex_grow数值成正比关系。

if growing {for target in &mut unfrozen {let child: &mut FlexItem = target;if free_space.is_normal() && sum_flex_grow > 0.0 {let grow_after = child.flex_basis + free_space * (self.nodes[child.node].style.flex_grow / sum_flex_grow);child.target_size.set_main(dir, grow_after);}}
}

如果使用flex_shrink,需要计算Flex项目的收缩比例系数,并结合剩余空间计算出Flex项目的收缩值,加上Flex项目的基准值,作为Flex项目的主轴尺寸。
Flex项目收缩的长度与flex_shrink成正比关系。

if shrinking && sum_flex_shrink > 0.0 {let sum_scaled_shrink_factor: f32 = unfrozen.iter().map(|child: &&mut FlexItem| {let child_style: Style = self.nodes[child.node].style;child.inner_flex_basis * child_style.flex_shrink}).sum();for target in &mut unfrozen {let child: &mut FlexItem = target;let scaled_shrink_factor = child.inner_flex_basis * self.nodes[child.node].style.flex_shrink;if free_space.is_normal() && sum_scaled_shrink_factor > 0.0 {let shrink_after = child.flex_basis + free_space * (scaled_shrink_factor / sum_scaled_shrink_factor);child.target_size.set_main(dir, shrink_after)}}
}
确定Flex项目的交叉轴尺寸

交叉轴的尺寸比较复杂一些,其中涉及到三个子环节:
● 确定每个Flex项目的猜测的交叉轴尺寸。
● 确定每行下的Flex项目交叉轴尺寸最大的高度。
● 根据行的交叉轴高度以及aspect-ratio、align-self: stretch等参数,计算出每个Flex项目交叉轴的实际宽度。

出现哪些问题,增加哪些新特性

当前Stretch也并非完美的,我们在使用的过程中遇到不少问题
● iOS的symbol覆盖问题导致crash
● Andriod端GC回收crash
● 多线程布局计算crash问题
● apsect-ratio属性重定义
● Flex多层嵌套规则下存在隐藏的问题 (flex-shrink/flex-grow)

这篇关于GaiaX开源解读 | 给Stretch(Rust编写的Flexbox布局引擎)新增特性,我掉了好多头发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/228674

相关文章

LiteFlow轻量级工作流引擎使用示例详解

《LiteFlow轻量级工作流引擎使用示例详解》:本文主要介绍LiteFlow是一个灵活、简洁且轻量的工作流引擎,适合用于中小型项目和微服务架构中的流程编排,本文给大家介绍LiteFlow轻量级工... 目录1. LiteFlow 主要特点2. 工作流定义方式3. LiteFlow 流程示例4. LiteF

SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程

《SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程》LiteFlow是一款专注于逻辑驱动流程编排的轻量级框架,它以组件化方式快速构建和执行业务流程,有效解耦复杂业务逻辑,下面给大... 目录一、基础概念1.1 组件(Component)1.2 规则(Rule)1.3 上下文(Conte

Python基于微信OCR引擎实现高效图片文字识别

《Python基于微信OCR引擎实现高效图片文字识别》这篇文章主要为大家详细介绍了一款基于微信OCR引擎的图片文字识别桌面应用开发全过程,可以实现从图片拖拽识别到文字提取,感兴趣的小伙伴可以跟随小编一... 目录一、项目概述1.1 开发背景1.2 技术选型1.3 核心优势二、功能详解2.1 核心功能模块2.

python编写朋克风格的天气查询程序

《python编写朋克风格的天气查询程序》这篇文章主要为大家详细介绍了一个基于Python的桌面应用程序,使用了tkinter库来创建图形用户界面并通过requests库调用Open-MeteoAPI... 目录工具介绍工具使用说明python脚本内容如何运行脚本工具介绍这个天气查询工具是一个基于 Pyt

Nacos注册中心和配置中心的底层原理全面解读

《Nacos注册中心和配置中心的底层原理全面解读》:本文主要介绍Nacos注册中心和配置中心的底层原理的全面解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录临时实例和永久实例为什么 Nacos 要将服务实例分为临时实例和永久实例?1.x 版本和2.x版本的区别

MyBatis编写嵌套子查询的动态SQL实践详解

《MyBatis编写嵌套子查询的动态SQL实践详解》在Java生态中,MyBatis作为一款优秀的ORM框架,广泛应用于数据库操作,本文将深入探讨如何在MyBatis中编写嵌套子查询的动态SQL,并结... 目录一、Myhttp://www.chinasem.cnBATis动态SQL的核心优势1. 灵活性与可

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一

MySQL的ALTER TABLE命令的使用解读

《MySQL的ALTERTABLE命令的使用解读》:本文主要介绍MySQL的ALTERTABLE命令的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、查看所建表的编China编程码格式2、修改表的编码格式3、修改列队数据类型4、添加列5、修改列的位置5.1、把列

Mybatis嵌套子查询动态SQL编写实践

《Mybatis嵌套子查询动态SQL编写实践》:本文主要介绍Mybatis嵌套子查询动态SQL编写方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、实体类1、主类2、子类二、Mapper三、XML四、详解总结前言MyBATis的xml文件编写动态SQL

Linux CPU飙升排查五步法解读

《LinuxCPU飙升排查五步法解读》:本文主要介绍LinuxCPU飙升排查五步法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录排查思路-五步法1. top命令定位应用进程pid2.php top-Hp[pid]定位应用进程对应的线程tid3. printf"%