鸿蒙Harmony开发实战案例:使用OpenGL绘制3D图形

news2024/10/5 13:20:10

XComponent控件常用于相机预览流的显示和游戏画面的绘制,在OpenHarmony上,可以配合Native Window创建OpenGL开发环境,并最终将OpenGL绘制的图形显示到XComponent控件。本文将采用"Native C++"模板,调用OpenGL ES图形库绘制3D图形(三棱锥),并将结果渲染到页面的XComponent控件中进行展示。同时,还可以在屏幕上通过触摸滑动手势对三棱锥进行旋转,最终得到不同角度的图形并显示到页面。

效果展示

首页滑动屏幕旋转变换

3d-graphic-index.png

3d-graphic-rotate.png

环境要求

  • 本示例仅支持在标准系统上运行。

  • IDE:DevEco Studio 3.1 Beta2

  • SDK:Ohos_sdk_public 3.2.11.9 (API Version 9 Release)

点击领取→纯血鸿蒙Next全套最新学习资料  希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

开发步骤

1、环境搭建

我们首先要完成应用开发环境的搭建,本示例运行DAYU200开发板上。

  • 搭建应用开发环境

    说明:

    为确保运行效果,本案例以使用DevEco Studio 3.1 Beta2 SDK:API9 (3.2.11.9)版本为例。

    3d-graphic-creat-project.png

    (2)开发环境配置完成后,创建工程(模板选择“Native C++”),选择eTS语言开发。

  • 应用调测工程创建完成后,选择使用真机进行调测。

    (1)将搭载OpenHarmony标准系统的开发板与电脑连接。

    (2)点击File> Project Structure... > Project>SigningConfigs界面勾选“Automatically generate signature”,等待自动签名完成即可,最后点击“OK”。如下图所示:

    3d-graphic-creat-signature.png

    (3)在编辑窗口右上角的工具栏,点击"运行"按钮运行。

    3d-graphic-run.png

2、源码结构

  • 代码结构分析,整个工程的代码结构如下:

    3d-graphic-creat-code-struct.png

  • 文件说明如下:

    .
    └── main
        ├── cpp
        │   ├── app_napi.cpp      //C++与ArkTS中XComponent控件交互的napi接口实现
        │   ├── CMakeLists.txt    //CMake规则配置文件,NAPI C/C++代码编译需要配置该文件
        │   ├── include
        │   │   ├── app_napi.h
        │   │   ├── tetrahedron.h //三棱锥类实现头文件
        │   │   └── util
        │   ├── module.cpp        //NAPI模块注册
        │   ├── napi_manager.cpp
        │   ├── napi_util.cpp
        │   ├── tetrahedron.cpp   //三棱锥的绘制OpenGL实现
        │   └── type
        │       └── libentry
        ├── ets
        │   ├── entryability
        │   │   └── EntryAbility.ts
        │   └── pages
        │       └── Index.ets      //主页面
        ├── module.json5
        └── resources              //资源文件目录
            ├── base
            │   ├── element
            │   ├── media
            │   └── profile
            ├── en_US
            │   └── element
            ├── rawfile
            └── zh_CN
                └── element

3、绘制流程

  • 3D绘制函数调用流程如下:
  • 在Tetrahedron类的Update方法中使用GLES3库着色器绘制,最终通过ArkUI的XComponent组件显示,流程如下:

4、C++(OpenGL)实现

C++端方法源码是工程的entry/src/main/cpp/tetrahedron.cpp文件。

  • 注册模块先定义一个模块,在entry/src/main/cpp/module.cpp文件中,对应结构体类型为napi_module,模块定义好后,调用NAPI提供的模块注册函数napi_module_register(napi_module* mod)注册到系统中;

    /*
     * Napi Module define
     */
    static napi_module appNapiModule = {
        .nm_version = 1,
        .nm_flags = 0,
        .nm_filename = nullptr,
        .nm_register_func = Init,
        .nm_modname = "tetrahedron_napi",
        .nm_priv = ((void*)0),
        .reserved = { 0 },
    };
    
    /*
     * Module register function
     */
    extern "C" __attribute__((constructor)) void RegisterModule(void)
    {
        napi_module_register(&appNapiModule);
    }
  • 调用OpenGL相关图形API绘制三棱锥

    (1)初始化

    int32_t Tetrahedron::Init(void *window, int32_t width,  int32_t height)
    {
        window_ = window;
        width_ = width;
        height_ = height;
    
        LOGI("Init window = %{public}p, w = %{public}d, h = %{public}d.", window, width, height);
        mEglWindow = reinterpret_cast<EGLNativeWindowType>(window);
    
        // 1. create sharedcontext
        mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if (mEGLDisplay == EGL_NO_DISPLAY) {
            LOGE("unable to get EGL display.");
            return -1;
        }
    
        EGLint eglMajVers, eglMinVers;
        if (!eglInitialize(mEGLDisplay, &eglMajVers, &eglMinVers)) {
            mEGLDisplay = EGL_NO_DISPLAY;
            LOGE("unable to initialize display");
            return -1;
        }
    
        int version = 3;
        mEGLConfig = getConfig(version, mEGLDisplay);
        if (mEGLConfig == nullptr) {
            LOGE("GLContextInit config ERROR");
            return -1;
        }
    
        // 2. Create EGL Surface from Native Window
        EGLint winAttribs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE};
        if (mEglWindow) {
            mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mEglWindow, winAttribs);
            if (mEGLSurface == nullptr) {
                LOGE("eglCreateContext eglSurface is null");
                return -1;
            }
        }
    
        // 3. Create EGLContext from
        int attrib3_list[] = {
            EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL_NONE
        };
    
        mEGLContext = eglCreateContext(mEGLDisplay, mEGLConfig, mSharedEGLContext, attrib3_list);
        if (!eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
            LOGE("eglMakeCurrent error = %{public}d", eglGetError());
        }
    
        mProgramHandle = CreateProgram(vertexShader, fragmentShader);
        if (!mProgramHandle) {
            LOGE("Could not create CreateProgram");
            return -1;
        }
    
        LOGI("Init success.");
    
        return 0;
    }

    其中,顶点着色器实现如下:

    char vertexShader[] =
        "attribute  vec4 apos;\n"
        "attribute  vec4 a_color;\n"
        "attribute  vec4 a_normal;\n"
        "uniform vec3 u_lightColor;\n"
        "uniform vec3 u_lightDirection;\n"
        "uniform mat4 a_mx;\n"
        "uniform mat4 a_my;\n"
        "varying  vec4 v_color;\n"
        "void main(){\n"
        "float radian = radians(30.0);\n"
        "float cos = cos(radian);\n"
        "float sin = sin(radian);\n"
        "  gl_Position = a_mx * a_my * vec4(apos.x, apos.y, apos.z, 1.0);\n"
        "  vec3 normal = normalize((a_mx * a_my * a_normal).xyz);\n"
        "  float dot = max(dot(u_lightDirection, normal), 0.0);\n"
        "  vec3 reflectedLight = u_lightColor * a_color.rgb * dot;\n"
        "  v_color = vec4(reflectedLight, a_color.a);\n"
        "}\n\0";

    (2)图像渲染

    ​ OpenGL ES图像渲染中着色器涉及到内置变量如下,所谓内置变量就是不用声明可以直接赋值,主要是为了实现特定的功能。

序号内置变量含义值数据类型
1gl_PointSize点渲染模式,方形点区域渲染像素大小float
2gl_Position顶点位置坐标vec4
3gl_FragColor片元颜色值vec4
4gl_FragCoord片元坐标,单位像素vec2
5gl_PointCoord点渲染模式对应点像素坐标vec2

​ 而本次渲染涉及到两个内建变量:gl_Position和gl_FragColor;

​ 其中,gl_Position变量表示最终传入片元着色器片元化要使用的顶点位置坐标,取值范围为-1.0到1.0,点超过该范围将自动被裁剪。初始化代码如下:

gl_Position = a_mx * a_my * vec4(apos.x, apos.y, apos.z, 1.0);

​a_my为y轴旋转矩阵,获取到旋转角度后初始化旋转矩阵;a_mx为x轴旋转矩阵,apos为绘制多面体点矩阵;

这些值的初始化通过glUniformMatrix4fv函数实现:

    mxGL_APICALL void GL_APIENTRY glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)

其中参数的含义如下:

序号参数名含义
1locationuniform对应的变量名
2count需要加载数据的数组元素的数量或者需要修改的矩阵的数量
3transpose指明矩阵是列优先(column major)矩阵(GL_FALSE)还是行优先(row major)矩阵(GL_TRUE)
4value指向由count个元素的数组的指针

​ gl_FragColor变量用于确定图形的颜色,可通过设置不同片段着色器的颜色,实现立体效果。

        片段着色器实现如下:

char fragmentShader[] =
    "precision mediump float;\n"
    "varying vec4 v_color;\n"
    "void main () {\n"
    "   gl_FragColor = v_color;\n"
    "}\n\0";

       三棱锥核心绘制代码如下:

 void Tetrahedron::Update(float angleX, float angleY)
 {
      angleY_ = angleY;
      angleX_ = angleX;
      glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
      glClear(GL_COLOR_BUFFER_BIT);
      glUseProgram(mProgramHandle);

      unsigned int aposLocation = glGetAttribLocation(mProgramHandle, "apos");
      unsigned int a_color = glGetAttribLocation(mProgramHandle, "a_color");
      unsigned int a_normal = glGetAttribLocation(mProgramHandle, "a_normal");
      unsigned int u_lightColor = glGetUniformLocation(mProgramHandle, "u_lightColor");
      unsigned int u_lightDirection = glGetUniformLocation(mProgramHandle, "u_lightDirection");
      unsigned int mx = glGetUniformLocation(mProgramHandle, "a_mx");
      unsigned int my = glGetUniformLocation(mProgramHandle, "a_my");

     /**
     y轴旋转度
     **/
     float radianY = angleY * PI /180.0;
     float cosY = cosf(radianY);
     float sinY = sinf(radianY);
     float myArr[] = {
         cosY,0,-sinY,0,  0,1,0,0,  sinY,0,cosY,0,  0,0,0,1
    };
     glUniformMatrix4fv(my, 1,false, myArr);

     /**
     x轴旋转度
     **/
     float radianX = angleX * PI /180.0;
     float cosX = cosf(radianX);
     float sinX = sinf(radianX);
     float mxArr[] = {
         1,0,0,0,  0,cosX,-sinX,0,  0,sinX,cosX,0,  0,0,0,1
    };
     glUniformMatrix4fv(mx, 1,false, mxArr);

     /**
      给平行光传入颜色和方向数据,RGB(1,1,1),单位向量(x,y,z)
      **/
     glUniform3f(u_lightColor, 1.0, 1.0, 1.0);
     // 保证向量(x,y,z)的长度为1,即单位向量
     float x = 1.0/sqrt(15), y = 2.0/sqrt(15), z = 3.0/sqrt(15);
     glUniform3f(u_lightDirection, x,-y,z);

     /**
      创建顶点位置数据数组data,原点到各顶点的距离都为1
     **/
     float data[] = {
         -0.75, -0.50, -0.43, 0.75, -0.50, -0.43, 0.00, -0.50, 0.87,
         0.75, -0.50, -0.43, 0.00, -0.50, 0.87, 0.00, 1.00, 0.00,
         0.00, -0.50, 0.87, 0.00, 1.00, 0.00, -0.75, -0.50, -0.43,
         0.00, 1.00, 0.00, -0.75, -0.50, -0.43, 0.75, -0.50, -0.43,
     };

     /**
      创建顶点颜色数组colorData
      **/
     float colorData[] = {
         1,0,0, 1,0,0, 1,0,0,//红色——面1
         1,0,0, 1,0,0, 1,0,0,//红色——面2
         1,0,0, 1,0,0, 1,0,0,//红色——面3
         1,0,0, 1,0,0, 1,0,0 //红色——面4
     };

     /**
      顶点法向量数组normalData
      **/
     float normalData[] = {
         0.00, -1.00, 0.00,  0.00, -1.00, 0.00,  0.00, -1.00, 0.00,
         -0.83, -0.28, -0.48,  -0.83, -0.28, -0.48,  -0.83, -0.28, -0.48,
         -0.83, 0.28, 0.48,  -0.83, 0.28, 0.48,  -0.83, 0.28, 0.48,
         0.00, -0.28, 0.96,  0.00, -0.28, 0.96,  0.00, -0.28, 0.96,
     };

     /**
      创建缓冲区buffer,传入顶点位置数据data
      **/
     unsigned int buffer;
     glGenBuffers(1, &buffer);
     glBindBuffer(GL_ARRAY_BUFFER, buffer);
     glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
     glVertexAttribPointer(aposLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
     glEnableVertexAttribArray(aposLocation);

     unsigned int normalBuffer;
     glGenBuffers(1, &normalBuffer);
     glBindBuffer(GL_ARRAY_BUFFER, normalBuffer);
     glBufferData(GL_ARRAY_BUFFER, sizeof(normalData), normalData, GL_STATIC_DRAW);
     glVertexAttribPointer(a_normal, 3, GL_FLOAT, GL_FALSE, 0, 0);
     glEnableVertexAttribArray(a_normal);

     /**
      创建缓冲区colorBuffer,传入顶点颜色数据colorData
      **/
     unsigned int colorBuffer;
     glGenBuffers(1, &colorBuffer);
     glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
     glBufferData(GL_ARRAY_BUFFER, sizeof(colorData), colorData, GL_STATIC_DRAW);
     glVertexAttribPointer(a_color, 3, GL_FLOAT, GL_FALSE, 0, 0);
     glEnableVertexAttribArray(a_color);

     /* 执行绘制命令 */
     glDrawArrays(GL_TRIANGLES, 0, 12);
 }

5、NAPI接口定义

接口定义为固定写法,在napi_property_descriptor desc[]中,我们需要使用DECLARE_NAPI_FUNCTION宏,以Add函数为例,将函数名字符串"Add"与具体的实现方法napi_value Add(napi_env env, napi_callback_info info)进行关联,即DECLARE_NAPI_FUNCTION("Add", Add)最终添加到desc[]。如下所示,其中UpdateAngle对应的是Native C++的接口,其应用端的接口对应为UpdateAngle,NAPI通过napi_define_properties接口将napi_property_descriptor结构体中的2个接口绑定在一起,并通过exports变量对外导出,使应用层可以调用UpdateAngle和getContext方法。

/*
 * function for module exports
 */
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    LOGE("Init");
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("getContext", NapiManager::GetContext),
        DECLARE_NAPI_FUNCTION("UpdateAngle", AppNapi::UpdateAngle),
    };

    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));

    bool ret = NapiManager::GetInstance()->Export(env, exports);
    if (!ret) {
        LOGE("Init failed");
    }

    return exports;
}
EXTERN_C_END

6、NAPI接口实现

​ Tetrahedron::UpdateAngle:传入angleX和angleY两个参数,分别为为绕X,Y轴的旋转角度;作为参数调用Tetrahedron::UpdateAngle(float angleX, float angleY)重新渲染,具体代码如下:

napi_value AppNapi::UpdateAngle(napi_env env, napi_callback_info info){
    LOGE("Tetrahedron UpdateAngle");
    size_t requireArgc = 2;
    size_t argc = 2;
    int speed = 3;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double offsetX;
    napi_get_value_double(env, args[0], &offsetX);

    double offsetY;
    napi_get_value_double(env, args[1], &offsetY);

    /* 处理offsetX偏移角度 */
    float tetrahedron_angleX = tetrahedron_->GetAngleX();
    float tetrahedron_angleY = tetrahedron_->GetAngleY();

    /* 上下滑动绕x轴 */
    if(offsetY < 0){
        tetrahedron_angleX = tetrahedron_angleX + speed;
    }
    else{
        tetrahedron_angleX = tetrahedron_angleX - speed;
    }

    /* 左右滑动绕y轴 */
    if(offsetX < 0){
        triangles_angleY = triangles_angleY + speed;
    }
    else{
        triangles_angleY = triangles_angleY - speed;
    }

    tetrahedron_angleY = normalize(tetrahedron_angleY);
    tetrahedron_angleX = normalize(tetrahedron_angleX);
    tetrahedron_->Update(tetrahedron_angleX, tetrahedron_angleY);

    /* 创建一个数组 */
    napi_value ret;
    napi_create_array(env, &ret);

    /* 设置数组并返回 */
    napi_value num;
    napi_create_int32(env, tetrahedron_angleX, &num);
    napi_set_element(env, ret, 0, num);
    napi_create_int32(env, tetrahedron_angleY, &num);
    napi_set_element(env, ret, 1, num);

    return ret;
}

​ GetContext:得到渲染所XComponent的上下文context,以便后续绑定XComponentID渲染,具体代码如下:

napi_value NapiManager::GetContext(napi_env env, napi_callback_info info)
{
    napi_status status;
    napi_value exports;
    size_t argc = 1;
    napi_value args[1];
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

    if (argc != 1) {
        napi_throw_type_error(env, NULL, "Wrong number of arguments");
        return nullptr;
    }

    napi_valuetype valuetype;
    status = napi_typeof(env, args[0], &valuetype);
    if (status != napi_ok) {
        return nullptr;
    }
    if (valuetype != napi_number) {
        napi_throw_type_error(env, NULL, "Wrong arguments");
        return nullptr;
    }

    int64_t value;
    NAPI_CALL(env, napi_get_value_int64(env, args[0], &value));
    NAPI_CALL(env, napi_create_object(env, &exports));

    switch (value) {
        case int64_t(ContextType::APP_LIFECYCLE):
            {
                /* AppInit 对应 app.ets中的应用生命周期 onCreate, onShow, onHide, onDestroy */
                LOGD("GetContext APP_LIFECYCLE");
                /* Register App Lifecycle */
                napi_property_descriptor desc[] = {
                    DECLARE_NAPI_FUNCTION("onCreate", NapiManager::NapiOnCreate),
                    DECLARE_NAPI_FUNCTION("onShow", NapiManager::NapiOnShow),
                    DECLARE_NAPI_FUNCTION("onHide", NapiManager::NapiOnHide),
                    DECLARE_NAPI_FUNCTION("onDestroy", NapiManager::NapiOnDestroy),
                };
                NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
            }

            break;
        case int64_t(ContextType::JS_PAGE_LIFECYCLE):
            {
                /* JS Page */
                LOGD("GetContext JS_PAGE_LIFECYCLE");
                napi_property_descriptor desc[] = {
                    DECLARE_NAPI_FUNCTION("onPageShow", NapiManager::NapiOnPageShow),
                    DECLARE_NAPI_FUNCTION("onPageHide", NapiManager::NapiOnPageHide),
                };
                NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
            }
            break;
        default:
            LOGE("unknown type");
    }
    return exports;
}

​ Export:先拿到XComponentID等信息后,通过NapiManager得到context,再通过context得到处理3D绘画的appNapi类并进行相应输出处理。部分代码如下(具体请查看源码):

bool NapiManager::Export(napi_env env, napi_value exports)
{
    napi_status status;
    napi_value exportInstance = nullptr;
    OH_NativeXComponent *nativeXComponent = nullptr;
    int32_t ret;
    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { };
    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;

    status = napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance);
    if (status != napi_ok) {
        return false;
    }

    status = napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent));
    if (status != napi_ok) {
        return false;
    }

    ret = OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize);
    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
        return false;
    }

    std::string id(idStr);
    auto context = NapiManager::GetInstance();
    if (context) {
        context->SetNativeXComponent(id, nativeXComponent);
        auto app = context->GetApp(id);
        app->SetNativeXComponent(nativeXComponent);
        app->Export(env, exports);
        return true;
    }

    return false;
}

7、ArkTS接口定义

(1)修改 index.d.ts 用于对外提供方法、说明(命名为tetrahedron_napi.d.ts)。

//传入x,y偏移量并返回x,y旋转角
export const UpdateAngle:(offsetX:number,offsetY:number)=>Array;

(2)在同目录下的 oh-package.json5 文件中将 tetrahedron_napi.d.ts 与cpp文件关联起来。

{
  "name": "libtetrahedron_napi.so",
  "types": "./tetrahedron_napi.d.ts",
  "version": "1.0.0",
  "description": "Please describe the basic information."
}

(3)修改项目的oh-package.json5文件,添加动态库。

{
  "license": "",
  "devDependencies": {
    "@types/libtetrahedron_napi.so": "file:./src/main/cpp/type/libentry"
  },
  "author": "",
  "name": "entry",
  "description": "Please describe the basic information.",
  "main": "",
  "version": "1.0.0",
  "dependencies": {}
}

8、CMake规则配置

entry/src/main/cpp/CMakeLists.txt是CMake规则文件。

project:用于设置项目(project)的名称。

set(CMAKE_CXX_STANDARD 11):设置C++标准。

include_directories:用于包含头文件。

add_library:编译产生链接库。

target_link_libraries:指定链接给定目标和/或其依赖项时要使用的库或标志,在PUBLIC字段后的库会被链接到tetrahedron_napi中。

# the minimum version of CMake.
cmake_minimum_required(VERSION 3.4.1)

project(TetrahedronHap)

set(NATIVE_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

include_directories(${NATIVE_ROOT_PATH}
                    ${NATIVE_ROOT_PATH}/include
                    ${NATIVE_ROOT_PATH}/include/util)

add_library(triangles_napi SHARED
            module.cpp
            app_napi.cpp
            tetrahedron.cpp
            napi_manager.cpp
            napi_util.cpp)


target_link_libraries(tetrahedron_napi PUBLIC EGL)
target_link_libraries(tetrahedron_napi PUBLIC GLESv3)
target_link_libraries(tetrahedron_napi PUBLIC hilog_ndk.z)
target_link_libraries(tetrahedron_napi PUBLIC ace_ndk.z)
target_link_libraries(tetrahedron_napi PUBLIC ace_napi.z)
target_link_libraries(tetrahedron_napi PUBLIC libc++.a)
target_link_libraries(tetrahedron_napi PUBLIC z)
target_link_libraries(tetrahedron_napi PUBLIC uv)
target_link_libraries(tetrahedron_napi PUBLIC libace_napi.z.so)

9、ArkTS实现

界面实现部分代码如下(具体请参考源码),其中:libraryname参数对应先前设置的模块名:tetrahedron_napi

import hilog from '@ohos.hilog';
import tetrahedron_napi from 'libtetrahedron_napi.so'

@Entry
@Component
struct Index {
  private xcomponentContext = null;
  private xcomponentId = 'tetrahedron';
  private offset_x: number = 0.000;
  private offset_y: number = 0.000;
  private index: number = 0;
  private type_: number = 5;
  private touchTypeDown: number = 0;
  private touchTypeUp: number = 1;
  private touchTypeMove: number = 2;
  private touchTypeCancel: number = 3;
  @State startVisible: Visibility = Visibility.Visible;

  @State angleArray: Array<number> = new Array<number>();
  private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.All })
  @State offsetX: number = 0
  @State offsetY: number = 0
  @State positionX: number = 0
  @State positionY: number = 0
  @State message: string = 'wu'

  async aboutToAppear() {
  }

  build() {
    Column() {
      Text($r('app.string.EntryAbility_desc'))
        .fontSize($r('app.float.head_font_24'))
        .lineHeight($r('app.float.wh_value_33'))
        .fontFamily('HarmonyHeiTi-Bold')
        .fontWeight(FontWeight.Bold)
        .fontColor($r('app.color.font_color_182431'))
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .textAlign(TextAlign.Start)
        .margin({ top: $r('app.float.wh_value_13'), bottom: $r('app.float.wh_value_15') });

      Text(this.angleArray[0]&this.angleArray[1]?'X轴旋转:'+this.angleArray[0].toString() +'°\nY轴旋转:'+this.angleArray[1].toString() + '°':'')
        .fontSize($r('app.float.head_font_24'))
        .lineHeight($r('app.float.wh_value_33'))
        .fontFamily('HarmonyHeiTi-Bold')
        .fontWeight(FontWeight.Bold)
        .fontColor($r('app.color.font_color_182431'))
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .textAlign(TextAlign.Start)
        .margin({ top: $r('app.float.wh_value_13'), bottom: $r('app.float.wh_value_15') });

      Stack({ alignContent: Alignment.Center }) {
        XComponent({ id: this.xcomponentId, type: 'surface', libraryname: 'tetrahedron_napi' })
          .onLoad((context) => {
            hilog.info(0x0000, 'Xcomponent', 'onLoad')
            this.xcomponentContext = context;
            globalThis.xcomponentContext = this.xcomponentContext;
            globalThis.xcomponentId = this.xcomponentId;
            globalThis.touchTypeDown = this.touchTypeDown;
            globalThis.touchTypeUp = this.touchTypeUp;
            globalThis.type_ = this.type_;
            globalThis.index = this.index;
            globalThis.touchTypeMove = this.touchTypeMove;
            globalThis.touchTypeCancel = this.touchTypeCancel;
            globalThis.offset_x = this.offset_x;
            globalThis.offset_y = this.offset_y;
          })
          .width($r('app.float.wh_value_362'))
          .height($r('app.float.wh_value_362'))
          .key('tetrahedron')
          .backgroundColor('#00000000')
          .onDestroy(() => {
            globalThis.flag = false;
            hilog.info(0x0000, "Xcomponent", 'onDestroy')
          })
      }
      .gesture(
        PanGesture(this.panOption)
          .onActionStart((event: GestureEvent) => {
            console.info('onActionStart');
          })
          .onActionUpdate((event: GestureEvent) => {
            this.angleArray = tetrahedron_napi.UpdateAngle(event.offsetX, event.offsetY);
            hilog.info(0x0000, "Gesture", 'offSet:' + event.offsetX + "," + event.offsetY);
          })
          .onActionEnd(() => {
            this.positionX = this.offsetX;
            this.positionY = this.offsetY;
            console.info('onActionEnd');
          })
      )
      .width('100%')
      .height('100%')
      .backgroundColor('#00000000')
    }
  }
}


最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?但是又不知道从哪里下手,而且学习时频繁踩坑,最终浪费大量时间。所以本人整理了一些比较合适的鸿蒙(HarmonyOS NEXT)学习路径和一些资料的整理供小伙伴学习

点击领取→纯血鸿蒙Next全套最新学习资料(安全链接,放心点击

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

一、鸿蒙(HarmonyOS NEXT)最新学习路线

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)…等技术知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

二、HarmonyOS Next 最新全套视频教程

三、《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

四、大厂面试必问面试题

五、鸿蒙南向开发技术

六、鸿蒙APP开发必备

七、鸿蒙生态应用开发白皮书V2.0PDF


完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

                        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1869536.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智慧校园-宿舍管理系统总体概述

在教育信息化的不断推动下&#xff0c;智慧校园宿舍管理系统脱颖而出&#xff0c;它以一种全新的视角和方式&#xff0c;重塑了高校宿舍管理的传统模式。该系统深度融合了云计算、物联网、大数据等先进科技&#xff0c;旨在为学生提供一个既安全可靠又充满便捷与温馨的居住体验…

实现设计开挂|如何设计出坚不可摧的网球拍?

数字揭秘 我们发现自己可能偶尔会以过激的方式表达沮丧或愤怒&#xff0c;哪怕是在公共场合。就算是世界级的网球运动员也无法避免偶尔的情绪爆发&#xff0c;他们有时会砸球拍来释放被压抑的情绪或应对来自竞赛的压力。 网球运动员的情绪爆发已被证明是不可避免的。哪怕是包括…

Transformer教程之Transformer的历史背景

在现代人工智能领域&#xff0c;Transformer模型已经成为一种不可或缺的技术&#xff0c;它在自然语言处理&#xff08;NLP&#xff09;和计算机视觉等多个领域取得了巨大的成功。本文将带你回顾Transformer的历史背景&#xff0c;了解它是如何从最初的构想到今天的广泛应用的。…

SpringBoot启动出错:无法访问org.springframework.boot.autoconfigure.SpringBootApplication

无法访问org.springframework.boot.autoconfigure.SpringBootApplication类文件具有错误的版本 61.0&#xff0c;应为 52.0请删除该文件或确保该文件位于正确的类路径子目录中。 出现该问题是由于版本不兼容&#xff0c; 在pom.xml文件中&#xff0c;修改版本为2开头即可

企业工程项目管理系统源码:Java版源码解析

一、项目概述 鸿鹄工程项目管理系统是基于Spring Cloud、Spring Boot、Mybatis、Vue和ElementUI技术栈&#xff0c;采用前后端分离架构构建的工程管理软件。它旨在应对企业快速发展中的管理挑战&#xff0c;提升工程管理效率&#xff0c;减轻工作负担&#xff0c;加速信息处理…

10分钟微调专属于自己的大模型_10分钟微调大模型

1.环境安装 # 设置pip全局镜像 (加速下载) pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ # 安装ms-swift pip install ms-swift[llm] -U# 环境对齐 (通常不需要运行. 如果你运行错误, 可以跑下面的代码, 仓库使用最新环境测试) pip install -r r…

Transformer教程之循环神经网络(RNN)和长短期记忆网络(LSTM)

在当今人工智能和深度学习的世界中&#xff0c;Transformer模型已经成为了主流。然而&#xff0c;回顾过去&#xff0c;循环神经网络&#xff08;RNN&#xff09;和长短期记忆网络&#xff08;LSTM&#xff09;在序列数据处理上也曾风靡一时。本文将详细讲解RNN和LSTM的原理、应…

【项目实训】各种反爬策略及爬虫困难点总结

在这里&#xff0c;我总结了本次项目的数据收集过程中遇到的反爬虫策略以及一些爬虫过程中容易出现问题的地方。 user-agent 简单的设置user-agent头部为浏览器即可&#xff1a; 爬取标签中带href属性的网页 对于显示岗位列表的页面&#xff0c;通常检查其源代码就会发现&…

Vite 动态导入警告问题解决方案

如上图我要实现从后台获取权限菜单并动态导入进行渲染 但由于 vite 暂时不支持这种导入方式 图中也给出了提示 本人也是这么去做了 但并没什么卵用 后来参考了 vite 的 import.meta.glob 这种方式 我在处理菜单权限控制的菜单里进行了如下操作&#xff1a; …

day001 环境的配置与工具的安装

VMware的软件包&#xff1a;https://pan.xunlei.com/s/VNs1KShnlZalTSJtejXXzchlA1?pwdudy5# 其他的软件可以在电脑管家中下载&#xff0c;注意不要安装到c盘&#xff0c;否则影响开机速度。 虚拟机工具&#xff1a;VMware17.5.1 1&#xff0c; 2&#xff0c; 3&#xff0c…

四川赤橙宏海商务信息咨询有限公司抖音电商领航者

在数字化浪潮席卷全球的今天&#xff0c;电商行业无疑是其中最为活跃、最具潜力的领域之一。而在中国这片广袤的土地上&#xff0c;四川赤橙宏海商务信息咨询有限公司以其独特的视角和前瞻性的战略布局&#xff0c;成为了抖音电商服务领域的佼佼者。今天&#xff0c;就让我们一…

Lesson 41 Penny‘s bag

Lesson 41 Penny’s bag 词汇 cheese n. 奶酪&#xff0c;芝士&#xff0c;起司【不可数】 搭配&#xff1a;a piece of cheese 一片奶酪    big cheese 大人物    cheese cake 芝士蛋糕 bread n. 面包【不可数】 各种面点&#xff1a;cake n. 蛋糕      cup cak…

我给我的学校写了个校园墙小程序

目录 前言 正文 事先声明 项目介绍 关于Github的一些点 ①贡献值的记录 ②Github中的Project是什么 ③Release和Package有什么区别 ④开源许可证之间的区别 尾声 &#x1f52d; Hi,I’m Pleasure1234&#x1f331; I’m currently learning Vue.js,SpringBoot,Computer Securit…

QT项目实战:拼图小游戏

一、拼图智益-经典游戏&#xff08;开发环境&#xff09; 1&#xff1a;操作系统&#xff1a;Windows 10 x64专业版。 2&#xff1a;开发工具&#xff1a;Qt 5.12.8。 二、拼图智益-经典游戏&#xff08;功能模块&#xff09; 1&#xff1a;功能模块1&#xff1a;游戏启动…

虚拟机使用的是此版本 VMware Workstation 不支持的硬件版本

复制了同事的VMware镜像&#xff0c;但是他的软件版本和我的不同&#xff0c;于是乎出现了这个报错&#xff1a;虚拟机使用的是此版本 VMwareWorkstation 不支持的硬件版本。 模块“Upgrade”启动失败。 解决办法&#xff0c;直接改.vmx文件的版本信息&#xff1a; 以文本格式打…

为什么带货主播,他突然就不吃香了?

为什么带货主播他突然就不吃香了&#xff1f;工资骤降50%。 相比 2023 年初主播的平均薪资降了50%&#xff0c;那不管你是头部主播还是腰部主播&#xff0c;全部都降薪了。那尾部主播就更不用说了&#xff0c;有的主播他的时薪已经低到 20 块钱一个小时&#xff0c;还不如大学…

YOLOV8图像分割预测后输出mask图

训练一个yolov8后&#xff0c;用官方的预测脚本一般是&#xff1a; results model.predict(img_path, saveTrue, save_diroutput_folder) 运行此代码会直接在run里面生成一个文件夹&#xff0c;保存预测图像。如果要获取分割后的mask点&#xff0c;或mask的轮廓点&#xff0…

redis需要知道的点

目录 一、为什么要用缓存 二、使用 Redis有哪些好处 三、什么是 redis&#xff1f; 四、redis和memcached区别 五、为什么redis单线程模型效率也能那么高 六、redis的线程模型 七、redis 6.0 引入多线程 八、为什么Redis需要把所有数据放到内存中&#xff1f; 九、Red…

利用圆上两点和圆半径求解圆心坐标

已知圆上两点P1&#xff0c;P2&#xff0c;坐标依次为 ( x 1 , y 1 ) , ( x 2 , y 2 ) (x_1,y_1),(x_2,y_2) (x1​,y1​),(x2​,y2​)&#xff0c;圆的半径为 r r r&#xff0c;求圆心的坐标。 假定P1&#xff0c;P2为任意两点&#xff0c;则两点连成线段的中点坐标是 x m i …

【精选】数据治理项目实施(合集)05——解码“数据架构”,数据架构包含哪些内容?

上一篇讲到了数据治理项目的前期调研工作&#xff0c;继数据调研工作完成之后&#xff0c;就要开始关于治理工作的各项方案设计&#xff0c;整体方案设计包括数据架构、元数据、主数据、数据质量、数据安全、指标标签体系、数据生命周期管理和管理评价等内容。这一篇重点讲一下…