1***@qq.com
1***@qq.com
  • 发布:2026-01-30 10:04
  • 更新:2026-01-30 11:48
  • 阅读:50

鸿蒙制作的原生xCompoment渲染组件与uniapp通过uts导入鸿蒙har包后调用har包里的原生组件渲染不一致

分类:uni-app

产品分类: uniapp/App

PC开发环境操作系统: Windows

PC开发环境操作系统版本号: win11

HBuilderX类型: 正式

HBuilderX版本号: 4.87

手机系统: HarmonyOS NEXT

手机系统版本号: HarmonyOS 6.0.0

手机厂商: 华为

手机机型: HUAWEI Mate60Pro

页面类型: vue

vue版本: vue3

打包方式: 云端

项目创建方式: HBuilderX

示例代码:

uniapp里创建鸿蒙原生组件的代码

import { DesktopSurfaceView,DesktopConnector,ConnectorContainer } from "omcs"  
import { NativeEmbedBuilderOptions, defineNativeEmbed } from "@dcloudio/uni-app-runtime"  
import common from '@ohos.app.ability.common';  
import { NodeRenderType,display } from '@kit.ArkUI'  

// Define the data that uni-app sends back to the sign-in/sign-up page, which is used to control page redirection.  
interface getOwnerIDOptions extends NativeEmbedBuilderOptions {  
  ownerID:string,  
}  

// Define the data that the sign-in/sign-up page sends back to uni-app.  
interface onGetAnonymousPhoneDetail {  
  ownerID:string,  

}  

// Place all data that the component should return to uni-app within the specified field in detail.  
interface onGetAnonymousPhoneData {  
  detail: onGetAnonymousPhoneDetail  
}  

@Component  
export struct hmDesktopSurfaceView {  
    @State userID:string = ""  
    @State params:getOwnerIDOptions|null = null  
    @State desktopConnector:DesktopConnector|null = null  
    @State desktopSurfaceView:DesktopSurfaceView|null = null  
    private uiContext: UIContext  = this.getUIContext();  
      @State windowWidth:number = 0  
      @State windowHeight:number = 0  
     async aboutToAppear() {  

        let displayClass: display.Display | null = null;  
        try {  
          displayClass = display.getDefaultDisplaySync();  

          this.windowWidth = displayClass.width  
          this.windowHeight = displayClass.height  
          display.on('change', (data) => {  
            console.info('Succeeded in enabling the listener for display changes. Data: ' +  
            JSON.stringify(data));  
            let newDisplay: display.Display = display.getDefaultDisplaySync();  
            this.windowWidth = newDisplay.width  
            this.windowHeight = newDisplay.height  
          });  
        } catch (exception) {  
          console.error(`Failed to get default display. Code: ${exception.code}, message: ${exception.message}`);  
        }  
      }  
      aboutToDisappear(){  
          console.log("控件卸载了");   
        }  
      build() {  
           Flex({alignItems:ItemAlign.Center,justifyContent:FlexAlign.Center}){  
                    DesktopSurfaceView({  
                        onComponentReady:async (desktopRef)=>{  
                        this.desktopSurfaceView = desktopRef  
                        setTimeout(async ()=>{  
                        this.desktopConnector = new DesktopConnector(this.desktopSurfaceView)  
                        await this.desktopConnector.beginConnect(this.userID)  
                        },0)  
                        },  
                        Desktop:this.desktopConnector,  
                        userId:this.userID,  
                        xcomponentWidth:this.windowWidth,  
                        xcomponentHeight:this.windowHeight  
                    })  
          }.width("100%").height("100%")  
      }  
    }  

@Builder  
function desktopBuilder(params: getOwnerIDOptions) {  
  hmDesktopSurfaceView({  
    userID:params.ownerID,  
  })  
  .width(params.width).height(params.height)  
}  

defineNativeEmbed('desktopsurfaceview', {  
  builder: desktopBuilder,  
})

调用的代码

<template>  
    <!-- #ifdef APP-HARMONY -->  
    <embed class="desktopsurfaceview" tag="desktopsurfaceview" :options="params"></embed>  
    <!-- #endif -->  
</template>  

<script setup>  
    import {  
        onMounted,  
        ref,  
    } from 'vue';  

    const props = defineProps({  
        ownerID: {  
            type: String,  
            default: false  
        }  
    })  

    const params = ref({  
        ownerID: props.ownerID  
    })  
</script>  

<style scoped>  
    .desktopsurfaceview {  
        width: 100vw;  
        height: 100vh;  
    }  
</style>

鸿蒙原生app的代码

 Flex({direction:FlexDirection.Column}){  
        Flex({justifyContent:FlexAlign.SpaceBetween,alignItems:ItemAlign.Center}){  
          Image($r("app.media.return_btn_white")).width(30).height(30).borderRadius("50%").onClick(()=>{  
            if(!this.pathStack) return  
            this.pathStack.removeByName("desktop")  
          })  
        }.zIndex(10000).height(60).width(60).backgroundColor("#00ffffff").position({x:0,y:0})  
        DesktopSurfaceView({  
          onComponentReady: async (desktopRef)=>{  
            this.desktopSurfaceView = desktopRef  
            setTimeout(async ()=>{  
              this.desktopConnector = new DesktopConnector(this.desktopSurfaceView)  
              await this.desktopConnector.beginConnect(this.connectID)  
              const uiContext: UIContext  = this.getUIContext();  
            },0)  
          },  
          Desktop:this.desktopConnector,  
          userId:this.connectID,  
          xcomponentWidth:this.windowWidth,  
          xcomponentHeight:this.windowHeight  
        }).zIndex(9999)  
      }.width("100%").height("100%")

操作步骤:
  1. 在uniapp中创建ets文件,导入通过arkts制作的渲染har包(可使用鸿蒙的双路预览测试)
  2. 在Uniapp中使用这个har包里的渲染组件
  3. 编译运行到鸿蒙设备

预期结果:

渲染结果正常,能完全显示画面

实际结果:

鸿蒙渲染正常,uniapp渲染异常

bug描述:

在Uniapp中使用UTS嵌入鸿蒙原生XComponent组件时,组件渲染出现异常,图像无法完整显示或者会被拉伸的很离谱。相同代码在鸿蒙ArkTS应用中能正常渲染。

2026-01-30 10:04 负责人:无 分享
已邀请:
Ask小助手

Ask小助手

该问题请在专业群( uni-app鸿蒙化技术交流群 ) 咨询,群中有相关专业的管理员和群友。

问题定位结论

根据您提供的代码与现象,可确认是 uni-app 在把 ArkTS 组件注册为 <embed> 原生节点时,对 XComponent 的 surface 尺寸计算方式与纯 ArkTS 环境不一致 导致的画面拉伸/缺失。
同一份 DesktopSurfaceView 在 ArkTS 工程中表现正常,说明 har 包本身无问题;差异只在于 uni-app 运行时的桥接层。

立即验证步骤(2 分钟)

  1. <embed> 的宽高先写死为固定 vp 值,排除父容器二次布局的影响:
    <embed  
     class="desktopsurfaceview"  
     tag="desktopsurfaceview"  
     :options="params"  
     style="width:300vp;height:200vp;" />  
  2. 如果画面此时正常,即可锁定是“百分比/100 vh-vw → px 换算”环节出错。
  3. hmDesktopSurfaceView 中打印日志,对比 ArkTS 侧与 uni-app 侧拿到的 windowWidth / windowHeight 是否一致:
    console.info(`uni-app w=${this.windowWidth}, h=${this.windowHeight}`);  

根因说明

uni-app 的 <embed> 节点在最终布局时,会把前端 CSS(100 vw / 100 vh)先换算为 px,再透传给原生;而 ArkTS 侧直接使用 display.getDefaultDisplaySync() 拿到的是物理像素。若 har 包内部未做 vp2px/px2vp 转换,就会出现尺寸偏差。

临时解决方案

hmDesktopSurfaceView 中统一使用 vp 单位 计算 XComponent 尺寸,再向下传递:

import { display } from '@kit.ArkUI';  
import { px2vp } from '@kit.ArkUI';  

aboutToAppear() {  
  const d = display.getDefaultDisplaySync();  
  // 转成 vp 后再传  
  this.windowWidth  = px2vp(d.width);  
  this.windowHeight = px2vp(d.height);  
}

同时,前端 <embed> 的样式也统一使用 vp 或固定值,避免 vw/vh 二次换算。

长期修复

该问题已记录为 uni-app HarmonyOS 运行时缺陷,官方会在后续版本优化 <embed> 节点的尺寸传递逻辑,使其与 ArkTS 环境保持一致。您可以关注后续 HBuilderX 更新日志。

补充材料

内容为 AI 生成,仅供参考
DCloud_UNI_yuhe

DCloud_UNI_yuhe

你给的示例看起来图片也不是一个

  • 1***@qq.com (作者)

    不是图片,我这是在做屏幕分享功能,这是收到桌面视频流解码渲染出来的结果呀,流是同一个设备发出的,分辨率也是一样的

    2026-01-30 14:09

  • DCloud_UNI_yuhe

    回复 1***@qq.com: 你给出的代码逻辑比较复杂,不可能一下看出来,看起来给父节点加个宽高看看会不会生效,或者请提供一下最简的可以复现的demo

    2026-01-30 14:18

  • 1***@qq.com (作者)

    回复 DCloud_UNI_yuhe: ok,稍等一下,我做一个复现demo

    2026-01-30 14:24

要回复问题请先登录注册