通过本文你将全面清晰的洞悉动态化跨端的实现原理,感受黑悟空(数据)一路打怪升级(在不同语言环境中流转改造),逆天改命(操控原生视图绘制),终成齐天大圣(完成视图渲染呈现)的艰辛历程。
动态化- 罗码(Roma,后文统称动态化)是一个完全自主研发的一站式跨平台解决方案,一份代码,可以在 Android、iOS、Harmony 及 Web 上运行。动态化的跨端原理与 React Native 、Weex 一致,在吸取业界各跨端框架优势的基础上,加上自主创新,打造的一个完全自主可控的综合跨端解决方案。其跨端的理论基础在于各端都具备统一解析和执行 JavaScript 的能力(JS 虚拟机)。业务代码被打包成 js 文件,在各端被 JS 虚拟机加载,被解析成多条不同功能的指令,调用原生宿主能力,完成数据的传递和视图的绘制。示意图如下:
为了让不同业务场景下的动态化产物高效地被 JS 虚拟机解析执行,需要扩充 JS 虚拟机的能力,提供适合动态化跨端场景下的功能,包括实例的创建和管理,视图的增、删、改、查以及便捷的跨语言通讯等功能。当然,为了使各平台更好的接入和使用动态化能力,动态化提供了一套统一接口,各平台依靠自身原生能力实现即可。
为了让App更好的接入动态化,我们提供了鸿蒙端的静态库文件 libRomaSdk.so (后文简称 SDK,由 NDK 通过 CMake 和 Ninja 将 C/C++ 代码编译而得来,这个过程可参考官网,这里不展开介绍),只要在工程中配置相关依赖即可使用动态化能力。
App启动时第一时间就会加载动态化 SDK,包括 JS Engine(动态化自主研发的为各平台的 JS 虚拟机扩展的功能) 和 Jue Instance (鸿蒙端对 JS Engine Interface 的实现)两部分,完成动态化运行环境的初始化。具体包括业务代码实例管理、任务管理、虚拟Dom树管理、虚拟Dom树 Differ、业务页面渲染管理、生命周期管理、事件分发、业务逻辑处理等一系列功能。
<template style="border-width: 2px;">
<div class="normalClass" style="margin-top: 100px;">
<image class="normalClass" :src="imageUrl" style="height: 200;">
</image>
<div class="normalClass" :style="{'background-color':bgColor}" style="height:60px;">
<text
style="border-width: 2px;width:260px;height:40px;align-self: center;text-align: center;background-color: white;"
@click="change()">
更新节点数据
</text>
</div>
</div>
</template>
<script>
export default {
data() {
return {
bgColor: "white",
imageUrl: "https://static.foodtalks.cn/company/images/434/121logo.png"
}
},
mounted() {
},
methods: {
change() {
this.bgColor = "red";
this.imageUrl = "https://www.szniego.com/uploads/image/20210202/1612232326.png";
this.updateInstance();
}
}
}
</script>
<style scoped>
.normalClass {
margin: 10px;
justify-content: center;
align-items: center;
align-self: stretch;
border-width: 2px;
}
</style>
/******/ (function(modules) { })
/************************************************************************/
/******/ ({
/***/ "./src/jueDemoList/ADemo/ADemo.jue":
/*!*******************************************!*\
!*** ./src/jueDemoList/ADemo/ADemo.jue ***!
\*******************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
var template = JRTemplateManager._jr_create_jue_template('ADemo.jue', {
"id": "ADemo",
"version": "11",
"dependencies": {
"JSEngine": "0.9.4"
}
});
JRTemplateManager._jr_create_t_node('ADemo.jue', '0', '40e129af-5e6a-70b8-a757-28a22785dc2f', 'document', {
"style": "border-width: 2px;"
});
JRTemplateManager._jr_create_t_node('ADemo.jue', '40e129af-5e6a-70b8-a757-28a22785dc2f', '918d8bb8-9362-bcd6-00ee-35b85c435072', 'div', {
"class": "normalClass",
"style": "margin-top: 100px;"
});
JRTemplateManager._jr_create_t_node('ADemo.jue', '918d8bb8-9362-bcd6-00ee-35b85c435072', '9e848985-59ac-bd8b-e85a-617a6e9a08dd', 'image', {
"class": "normalClass",
":src": "imageUrl",
"style": "height: 200;"
});
JRTemplateManager._jr_create_t_node('ADemo.jue', '918d8bb8-9362-bcd6-00ee-35b85c435072', 'b56d24a9-c08e-6b32-9558-162f2aece68d', 'div', {
"class": "normalClass",
":style": "{'background-color':bgColor}",
"style": "height:60px;"
});
JRTemplateManager._jr_create_t_node('ADemo.jue', 'b56d24a9-c08e-6b32-9558-162f2aece68d', '440bad33-f8bb-6baf-fe4f-b9bf768d4cc1', 'text', {
"style": "border-width: 2px;width:260px;height:40px;align-self: center;text-align: center;background-color: white;",
"@click": "change()"
});
JRTemplateManager._jr_add_t_node_value('ADemo.jue', '440bad33-f8bb-6baf-fe4f-b9bf768d4cc1', 'value', '更新节点数据');
var __default__ = {
data: function data() {
return {
bgColor: "white",
imageUrl: "https://static.foodtalks.cn/company/images/434/121logo.png"
};
},
mounted: function mounted() {},
methods: {
change: function change() {
this.bgColor = "red";
this.imageUrl = "https://www.szniego.com/uploads/image/20210202/1612232326.png";
this.updateInstance();
}
}
};
template.script = __default__;
__default__.filename = 'ADemo.jue';
__default__.__template__ = function () {
return template;
};
/* harmony default export */ __webpack_exports__["default"] = (__default__);
template.globalStyle = {};
template.style = {
".normalClass": {
"margin": "10px",
"justify-content": "center",
"align-items": "center",
"align-self": "stretch",
"border-width": "2px"
}
};
/***/ })
/******/ });
//# sourceMappingURL=ADemo.js.map
上图展示了动态化在鸿蒙端绘制页面的过程,总结一下就是三种语言环境,三个 Instance,三个 Thread 。三个 Instance 独立且一一对应。ArkTs 中的 Instance 在 UI 线程中用来完成页面的绘制,数据的绑定和事件的触发等。C++ 中的 Instance 作为数据的中转存储和事件转发,在 bg Thread 处理复杂耗时的逻辑,避免阻塞 UI Thread 和 js Thread。JS 中的 Instance 负责搜集产物信息,构建 V-Dom Tree,传递数据和事件等都在 js Thread 中处理。C++ 和 ArkTs 中根据 V-Dom Tree 创建 Component Tree 和 Render Tree。
public aboutToAppear() {
// 确保获取资源js文件并加载到内存中
romaAssetsManager.ensureAsset(this.jueName!, progress).then((version) => {
// 完成不同语言环境下Instance实例的创建
// 1 创建 arkTS 和 cpp 实例
this.romaInstance = this.createInstance(this.rootContent, this.pageId);
// 2 创建 JS 实例
this.romaInstance!.startInstance(this.initialProps).then((result) => {});
}).catch((error: Error) => {
})
}
private createInstance(root: NodeContent, pageId: string | null): RomaInstance {
this.shouldDestroyRomaInstance = true
return RomaEnv.createAndRegisterRomaInstance(this.jueName!, {
root: root,
uiContext: this.getUIContext(),
abilityContext: getContext(this) as common.UIAbilityContext,
pageId: pageId,
errorListener: this.errorListener ? (error) => {
this.errorListener?.(error);
console.warn("CreateInstanceView", error.message);
this.pageStage = PageStage.ERROR
} : undefined
}, this.stateListener);
}
// 类型
public stateListener?: RomaStateListener;
export type RomaStateListener = (instanceId: string, state: "createFinish" | "updateFinish" | "createInstance",
romaInstance: RomaInstance) => void;
public createInstance(jueName:string, param: RomaInstanceParam, stateListener?:RomaStateListener): RomaInstance {
const id = ++this.nextInstanceId;
// 创建 arkTS 侧的实例
const instance = new RomaInstance(
jueName,
id.toString(),
"",
this.napiBridge,
param.uiContext,
param.abilityContext
)
// 给实例绑定状态变化的监听
if (stateListener) {
instance.addStateListeners(stateListener);
}
instance.setPageId(param.pageId);
instance.initialize(param.root);
this.instanceMap.set(id.toString(), instance);
return instance;
}
public initialize(root: NodeContent | null = null) {
// 注册实例变化监听器
this.napiBridge.createInstance( this.jueName, this.getId(), root,
(mutations: Mutation[], isFromCore: boolean) => {
this.descriptorManager.applyMutations(mutations, isFromCore)
},
(tag, commandName, args) => {
// 省略无关代码
},
(instanceId: string, state: string) => {
// 省略无关代码
})
}
static napi_value createInstance(napi_env env, napi_callback_info info) {
ArkTS arkJs(env);
auto args = arkJs.getCallbackArgs(info, 6);
auto jueName = arkJs.getString(args[0]);
InstanceId instanceId = arkJs.getString(args[1]);
ArkUI_NodeContentHandle nodeContentHandle_;
if(!arkJs.isUndefined(args[2])){
OH_ArkUI_GetNodeContentFromNapiValue(env, args[2], &nodeContentHandle_);
}
auto listener_ref = arkJs.createReference(args[3]);
auto componentMethod_ref = arkJs.createReference(args[4]);
auto stateListenerMethod_ref = arkJs.createReference(args[5]);
auto &engine = RomaEnv::getInstance();
engine.createInstance(jueName, instanceId, nodeContentHandle_,
[env, listener_ref](MutationsToNapiConverter mutationsToNapiConverter, auto const &mutations) {
// C++环境中 Instance 状态变化时触发
if (mutations.size() <= 0) {
return;
}
ArkTS arkJs(env);
auto napiMutations = mutationsToNapiConverter.convert(env, mutations);
std::array<napi_value, 1> args = {napiMutations};
// 获取从 ArkTS 环境中传递过来的监听对象并触发
auto listener = arkJs.getReferenceValue(listener_ref);
arkJs.call<1>(listener, args);
},
return arkJs.getUndefined();
}
void RomaEnv::createInstance(std::string jueName, InstanceId instanceId, ArkUI_NodeContentHandle nodeContentHandle_, MutationsListener mutationsListener,
ComponentMethodListener componentMethodListener, StateListener stateListener) {
RomaEnv::getInstance().getBackgroundExecutor()([=]() {
auto nonConstRef = RomaInstanceManager::getInstance().get(instanceId);
// 创建实例,页面 | 模板
RomaInstance::Shared instance = std::make_shared<RomaInstance>(jueName, instanceId);
instance->rootInstance_ = instance;
instance->attachNativeXComponent(nodeContentHandle_);
// 注册监听器
instance->registerInstanceChangeListener(mutationsListener);
……
});
}
public async startInstance(initialProps: TObject): Promise<void> {
return this.napiBridge.startInstance(this.getId(),initialProps);
}
static napi_value startInstance(napi_env env, napi_callback_info info) {
ArkTS arkJs(env);
arkJs.methodName = "startInstance";
auto args = arkJs.getCallbackArgs(info, 3);
InstanceId instanceId = arkJs.getString(args[0]);
auto onFinishRef = arkJs.createReference(args[2]);
auto &engine = RomaEnv::getInstance();
engine.startInstance(instanceId, arkJs.getDynamic(args[1]), [env, onFinishRef]() {
ArkTS arkJs(env);
auto listener = arkJs.getReferenceValue(onFinishRef);
arkJs.call<0>(listener, {});
arkJs.deleteReference(onFinishRef);
});
return arkJs.getUndefined();
}
// startInstance 的具体实现
void RomaEnv::startInstance(InstanceId instanceId, folly::dynamic &&initialProps,
std::function<void()> &&onFinish) {
try {
RomaEnv::getInstance().getBackgroundExecutor()([=]() {
auto nonConstRef = RomaInstanceManager::getInstance().get(instanceId);
if (nonConstRef) {
// 准备进入 js 环境创建 Instance
nonConstRef->start(instanceId, initialProps);
this->taskExecutor_->runTask(TaskThread::MAIN, [onFinish]() {
onFinish();
});
}
});
} catch (const std::exception &e) {
throw e.what();
};
}
void RomaInstance::start(SurfaceId surfaceId, folly::dynamic const &initialProps){
// 进入js线程执行实例创建
RomaEnv::getInstance().getRuntimeExecutor()([jueName_ = jueName_, initialProps, surfaceId](jsi::Runtime &runtime) {
// 判断 JS 的全局变量中是否有 JRPageManager
if (runtime.global().hasProperty(runtime, "JRPageManager")) {
jsi::Object JRPageManager = runtime.global().getPropertyAsObject(runtime, "JRPageManager");
if (JRPageManager.hasProperty(runtime, "createInstance")) {
jsi::Function method = JRPageManager.getPropertyAsFunction(runtime, "createInstance");
method.callWithThis(runtime, JRPageManager,
{
jsi::valueFromDynamic(runtime, jueName_+".jue"),
jsi::valueFromDynamic(runtime, surfaceId),
jsi::valueFromDynamic(runtime, initialProps),
});
}
}
});
}
export function createInstance(bundleName,instanceID,options){
// 判断是否传入实例bundleName以及对应bundle是否已经加载
if (!bundleName || !has_load_bundle(bundleName)) {
callLoadBundleJsFileFail(instanceID)
return;
}
JRTransUICore._jr_ydby_new_template_instance(bundleName,instanceID,options);
}
function _jr_ydby_new_template_instance(template_id, ctx_id, template_data, is_batchCreate) {
// 1.根据模板创建JUE实例
var ctx = new JueInstance(ctx_id, template, template_data);
ctx.initRootCtxAndStaticCss();
……
// 创建v-dom
var v_dom = new _jr_ydby_v_dom(template_id);
ctx.v_dom = v_dom;
// 2.构建v-dom 对应 root-node
var root_node = _jr_ydby_new_node_instance(ctx.c_id, v_dom, template.root_node, null, {}, is_batchCreate);
// 构建结束
_jr_ydby_create_finshed(ctx.c_id);
return ctx.c_id;
}
export function _jr_ydby_new_node_instance(ctx_id, v_dom, current_t_node, parent_node, v_f_ctx, is_batchCreate, itemIndex) {
var cur_env = _jr_ydby_node_parse_jscontext(ctx_id, v_f_ctx);
let ctx = __jr_template__ctx[ctx_id];
var current_node = null;
if (current_t_node.type === 'document') {
// 创建根节点
current_node = new JUE_NODE(v_f_ctx, ctx_id, "", current_t_node);
current_node.setAttr(cur_env, ctx)
_jr_ydby_create_body(current_node);
}else{
// 创建其他子节点
current_node = new JUE_NODE(v_f_ctx, ctx_id, parent_node.id, current_t_node);
current_node.setAttr(cur_env, ctx);
current_node.setStyle(cur_env, parent_node.style);
current_node.setValue(cur_env, v_f_ctx);
current_node.setEvent(cur_env);
// 组装数据,生成 _jr_ydby_v_node对象
parent_node.appendChild(current_node, v_dom);
// 添加节点
_jr_ydby_add_element(current_node, current_node.node_index);
}
// 循环创建当前节点下的子节点
if (current_t_node.sub_nodes) {
let v_if_else_parse_result = [];
for (let i = 0; i < current_t_node.sub_nodes.length; i++) {
v_f_ctx = Object.assign({}, v_f_ctx);
let sub_t_node = current_t_node.sub_nodes[i];
let att = sub_t_node.attr;
let v_for_value = att['v-for'];
……
// 递归创建子节点
_jr_ydby_new_node_instance(ctx_id, v_dom, sub_t_node, current_node, v_f_ctx, is_batchCreate);
}
}
return current_node;
}
current_node.node_index);
搜集节点信息
var nodePorperty = {
template_id: node.template_id,
ctx_d: node.ctx_id,
tag: node.tag,
id: node.id,
is_root: node.is_root,
type: node.type,
parent_node: node.parent_node,
style: node.style,
cache: node.cache,
attr: _jr_ydby_tools_deep_copy(node.attr),
value: node.value,
event: node.event,
index: node.index,//仅cell-slot节点使用
isComponentNode: node.isComponentNode,
componentInstanceId: node.componentInstanceId,
componentBundleName: node.componentBundleName
};
调用添加方法
node.parent_node, nodePorperty, index);
runtime_->global().setProperty( *runtime_, "callAddElement",
Function::createFromHostFunction( *runtime_,
PropNameID::forAscii(*runtime_, "callAddElement"), 4,
[](jsi::Runtime &runtime,
jsi::Value const & /*thisValue*/,
jsi::Value const *arguments,
size_t /*count*/) noexcept -> jsi::Value {
UIManager::callAddElement(surfaceIdFromValue(runtime, arguments[0]),
stringFromValue(runtime, arguments[1]),
commandArgsFromValue(runtime, arguments[2]),
arguments[3].getNumber());
return jsi::Value::undefined();
}));
void UIManager::callAddElement(SurfaceId surfaceId, std::string const &parent_id, folly::dynamic props, size_t index) {
ComponentName name = props["type"].asString();
RomaEnv::getInstance().getBackgroundExecutor()([=]() {
RomaNode::Shared node = nullptr;
auto nonConstRef= RomaInstanceManager::getInstance().get(surfaceId);
auto parent = nonConstRef->getNode(parent_id);
if(parent == nullptr){
return;
}
// 强引用保存节点到父节点的children_数组中
folly::dynamic style = props["style"];
node = RomaNodeFactory::createSharedNode(surfaceId, tag, name, parent_id, index, isComponentNode,
componentInstanceId, props["attr"], style, props["event"],
props["value"]);
size_t childIndex = index;
parent->appendChild(node, childIndex);
auto shadowView = std::make_shared<ShadowView>(*node);
// 组件节点,只插入,不创建,等组件实例创建时,再创建
if (!isComponentNode) {
// 增加普通节点的创建指令
nonConstRef->rootInstance_.lock()->lastMutations_.push_back(
ShadowViewMutation::CreateMutation(shadowView->getSharedShadowView()));
}
if ((parent->getComponentName() == "document") && nonConstRef->isComponent) {
// 如果是子组件中的节点,并且是document的直接子节点
auto parentNonConstRef = RomaInstanceManager::getInstance().get(nonConstRef->parentInstanceId_);
if (parentNonConstRef) {
auto componentNode = parentNonConstRef->getNode(nonConstRef->componentNodeId_);
if (componentNode) {
if (RomaEnv::getInstance().isUseYoga) {
// 父子组件衔接yoga树
componentNode->appendChild(node, index);
}
// 增加组件节点的插入指令
auto parentShadowView = componentNode->getShadowView();
auto documentShadowView = parent->getShadowView();
……
for (auto const &pair : documentShadowView->style_.items()) {
parentShadowView->style_[pair.first] = pair.second;
}
nonConstRef->rootInstance_.lock()->lastMutations_.push_back(ShadowViewMutation::InsertMutation(
parentShadowView, shadowView->getSharedShadowView(), index));
}
}
} else {
// 增加普通节点的插入指令
auto parentShadowView = parent->getShadowView();
nonConstRef->rootInstance_.lock()->lastMutations_.push_back(
ShadowViewMutation::InsertMutation(parentShadowView, shadowView->getSharedShadowView(), childIndex));
}
// 保存到节点
node->setShadowView(shadowView->getSharedShadowView());
// 弱引用保存节点到实例的map中
nonConstRef->addNode(tag, node);
});
export function _jr_ydby_create_finshed(ctx_id) {
let instance = getInstanceById(ctx_id);
callCreateFinish(ctx_id,{template_id:instance.template_id,version:instance.currentVersion});
}
runtime_->global().setProperty(
*runtime_, "callCreateFinish",
Function::createFromHostFunction(*runtime_,
PropNameID::forAscii(*runtime_, "callCreateFinish"), 1,
[](jsi::Runtime &runtime, jsi::Value const & ,
jsi::Value const *arguments, size_t /*count*/) noexcept -> jsi::Value {
auto surfaceId = surfaceIdFromValue(runtime, arguments[0]);
UIManager::callCreateFinish(surfaceId);
return jsi::Value::undefined();
}));
void UIManager::callCreateFinish(SurfaceId surfaceId) {
RomaEnv::getInstance().getBackgroundExecutor()([=]() {
auto nonConstRef = RomaInstanceManager::getInstance().get(surfaceId);
if (nonConstRef && !nonConstRef->isComponent) {
// 执行yoga布局
// Layout nodes.
std::vector<YogaLayoutableShadowNode const *> affectedLayoutableNodes{};
// affectedLayoutableNodes.reserve(1024);
LayoutContext layoutContext = LayoutContext();
……
if(starts_with(nonConstRef->rootInstance_.lock()->jueName_, "template")){
layoutContext.layoutType = TEMPLATE;
}
if (nonConstRef->rootInstance_.lock()) {
nonConstRef->rootInstance_.lock()->rootNode_->layoutIfNeeded(layoutContext);
}
if (nonConstRef->rootInstance_.lock()) {
ShadowViewMutationList mutableList = nonConstRef->rootInstance_.lock()->lastMutations_;
nonConstRef->rootInstance_.lock()->lastMutations_.clear();
RomaEnv::getInstance().taskExecutor_->runTask(TaskThread::MAIN, [nonConstRef, mutableList, surfaceId] {
// 使用 ArkUI 渲染 ,并触发 mutationsListener 监听
if (nonConstRef->rootInstance_.lock()) {
auto a = nonConstRef->rootInstance_.lock()->m_mutationsToNapiConverter;
nonConstRef->rootInstance_.lock()->mutationsListener(a, mutableList,true);
}
nonConstRef->createFinish();
});
}
} else if (nonConstRef) {
RomaEnv::getInstance().taskExecutor_->runTask(
TaskThread::MAIN, [nonConstRef, surfaceId] { nonConstRef->createFinish(); });
}
});
}
(mutations: Mutation[], isFromCore: boolean) => { this.descriptorManager.applyMutations(mutations, isFromCore) }
public applyMutations(mutations: Mutation[], isFromCore: boolean) {
// 去重
const tags = mutations.flatMap(mutation => this.applyMutation(mutation, isFromCore));
const tags = new Set(tags);
// 遍历各节点
tags.forEach(tag => {
// 取实例id,和tag
const strArr: string[] = tag.split("##");
const instanceId = strArr[0];//实例id
const nodeId = strArr[1];//节点id
if(instanceId === this.romaInstance.getId()){
// 更新节点
let updatedDescriptor = this.getDescriptor(nodeId);
if(!updatedDescriptor) return;
// 在当前实例中更新tag组件的UI描述信息
this.descriptorListenersSetByTag.get(nodeId)?.forEach(cb => {
onDescriptorChange(cb, updatedDescriptor);
});
}else {
// 创建节点
const instance: RomaInstance = RomaEnv.getRomaInstanceManager()?.getInstance(instanceId) as RomaInstance;
let updatedDescriptor = instance?.getDescriptor(nodeId);
if(!updatedDescriptor) return;
instance.refreshComponentUI(nodeId, updatedDescriptor);
}
});
}
public refreshComponentUI(tag: Tag, d: Descriptor) {
this.getDescriptorListenersSet(tag)?.forEach(cb => {
onDescriptorChange(cb,d);
});
}
aboutToAppear() {
if (!this.componentCtx) {
return;
}
this.componentCtx?.aboutToAppear((newDescriptor) => {
this.descriptor = newDescriptor;
// 链接自定义alt图方法
this.customAltImage = RomaConfig.instance().getImageAltMethod();
// 触发更新
this.onLoadStart();
this.updateImageSource();
// 处理图片 object-position 模式相关逻辑
this.initObjectPositionHandle()
// 解析占位图
this.altSource = this.getImageAlt();
this.hasPlaceHoldImage = this.altSource ? true : false;
// 解析背景色
let bgColorStr = RomaStyleParser.getStyleToString('background-color',this.descriptor);
……
// 设置tint-color
this.getTintColor();
// 是否开启抗锯齿
this.interpolation = this.colorFilter ? ImageInterpolation.High : ImageInterpolation.Low;
}, (methodName, args:TAny[]) => {
// 注册标签方法
if (methodName === 'loadRef') {
this.loadRef(args[0]);
}
});
}
public build() {
// 根据表述信息,构建
RomaComponentFactory.builder(new RomaComponentParam(this.romaInstance, this.descriptor.tag));
}
export const RomaComponentFactory: WrappedBuilder<[RomaComponentParam]> = wrapBuilder(RomaComponentFactoryBuilder);
在调用RomaComponentFactory.builder时,触发 RomaComponentFactoryBuilder 方法,如下只列出示例中用到的标签的实现,其他的省略了。
@Builder
function RomaComponentFactoryBuilder(param: RomaComponentParam) {
if (param.type == "document") {
RomaDocument({
componentCtx: param.componentCtx
})
} else if (param.type == "div") {
RomaDiv({
componentCtx: param.componentCtx
})
} else if (param.type === "text") {
RomaText({
componentCtx: param.componentCtx
})
} else if (param.type === "image" || param.type === "img") {
RomaImageView({
componentCtx: param.componentCtx
})
} else {
RomaCustomComponentFactory.customComponentBuilder.builder(param.componentCtx);
}
}
build() {
if(this.componentCtx && this.descriptor) {
Image(this.imgSource)
.attributeModifier(this.componentCtx?.build(this.descriptor))
.gestureModifier(this.componentCtx?.build(this.descriptor))
.alt(this.getImageAlt())
.objectFit(this.getResizeMode(RomaStyleParser.getStyleToString('object-fit', this.descriptor)))
.renderMode(this.getRenderMode())
.colorFilter(this.colorFilter)
.interpolation(this.interpolation)
.backgroundColor(this.showColor)
.blur(this.getBlurNumber())
.onComplete(event => this.onLoad(event as ImageOnCompleteEvent))
.onError(event => this.dispatchOnError(event as ImageOnErrorEvent))
.position(this.imgPosition)
.clipShape(this.imgClipShape)
}
}
扫一扫,加入技术交流群