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

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 sharedcontextmEGLDisplay = 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 WindowEGLint 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 fromint 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,//红色——面11,0,0, 1,0,0, 1,0,0,//红色——面21,0,0, 1,0,0, 1,0,0,//红色——面31,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 SHAREDmodule.cppapp_napi.cpptetrahedron.cppnapi_manager.cppnapi_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.mzph.cn/news/861889.shtml

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

相关文章

yaml文件的介绍-K8S

yaml 文件是我们使用K8S管理应用程序常用的部署方式&#xff0c;它主要是通过一系列键值对组成&#xff0c;键和值使用冒号和空格分隔。以下是对yaml的介绍 首先我们可以使用命令生成一个简单的YAML模版文件 Kubectl run nginx-pod –imagenginx:latest –port80 –dry-runcli…

使用FRP 0.58版本进行内网穿透的详细教程

什么是FRP&#xff1f; FRP&#xff08;Fast Reverse Proxy&#xff09;是一款高性能的反向代理应用&#xff0c;主要用于内网穿透。通过FRP&#xff0c;您可以将内网服务暴露给外网用户&#xff0c;无需进行复杂的网络配置。 准备工作 服务器&#xff1a;一台具备公网IP的服…

CSS基础学习记录(6)

目录 1、从最基本的页面开始 2、添加图像/浮层部分 3、位置调整 4、添加动效 4.1、添加浮层动效 4.2、添加背景动画 根据前面css的学习&#xff0c;本篇来实践下前面学习的知识&#xff0c;主要实现如下这样的效果。 下面我们一步步实现上面的效果。 1、从最基本的页面开…

【LeetCode】八、堆的使用:第K个最大元素 + 前K和高频单词

文章目录 1、Java中的堆结构2、leetcode215&#xff1a;数组中的第K个最大元素3、leetcode692&#xff1a;前K个高频单词 1、Java中的堆结构 PriorityQueue类取堆顶元素删除堆顶元素堆的元素个数遍历堆 2、leetcode215&#xff1a;数组中的第K个最大元素 这题应该快排来解&…

使用 privacyIDEA 实现 Windows RDP 多因素认证 (MFA)

前言 在等保 2.0 标准中有要求: d&#xff09;应采用口令、密码技术、生物技术等两种或两种以上组合的鉴别技术对用户进行身份鉴别&#xff0c;且其中一种鉴别技术至少应使用密码技术来实现。 可以借助开源的 privacyIDEA 配合 AD 域环境实现 RDP MFA 认证登录以满足上面的要…

音视频入门基础:H.264专题(7)——FFmpeg源码中 指数哥伦布编码的解码实现

一、引言 由于视频的传输和存贮是十分在乎体积的&#xff0c;对于每一个比特&#xff08;bit&#xff09;都要格外珍惜&#xff0c;所以H.264中用到了多种熵编码来对原本的数据进行压缩。 比如Sequence Paramater Set&#xff08;sps / 序列参数集&#xff09;中&#xff0c;s…

python获取快手账号列表数据

快手数据获取相对简单访问地址固定且不需要登录token 列表地址获取的固定接口 https://www.kuaishou.com/graphql 发送post请求注意每个快手账号对应的id import time from datetime import datetime import logging import json import pymysql import requests# 创建一个lo…

python爬虫之12306模拟登陆

python爬虫之12306模拟登陆 登录流程&#xff1a; 1、登录界面输入账号密码&#xff0c;点击立即登录 2、弹出手机验证界面&#xff0c;输入身份证后4位&#xff0c;点击获取验证码等待验证码后手动输入&#xff0c;点击确定登录 实现代码如下&#xff1a; #需求&#xff1…

详细分析Springmvc中的@ModelAttribute基本知识(附Demo)

目录 前言1. 注解用法1.1 方法参数1.2 方法1.3 类 2. 注解场景2.1 表单参数2.2 AJAX请求2.3 文件上传 3. 实战4. 总结 前言 将请求参数绑定到模型对象上&#xff0c;或者在请求处理之前添加模型属性 可以在方法参数、方法或者类上使用 一般适用这几种场景&#xff1a; 表单…

联想至像M3070DNA打印机加粉及清零方法

基本参数&#xff1a; 产品类型&#xff1a;黑白激光多功能商用一体机&#xff08;打印/复印/扫描&#xff09; 网络功能&#xff1a;支持有线网络打印 最大处理幅面&#xff1a;A4 双面功能&#xff1a;自动 打印速度&#xff1a;30页/分钟&#xff08;高速激光打印&…

HarmonyOS NEXT:华为开启全新操作系统时代

在全球科技浪潮的汹涌澎湃中&#xff0c;华为再次以创新者的姿态&#xff0c;引领了一场关于操作系统的革命。HarmonyOS NEXT&#xff0c;这一由华为倾力打造的分布式操作系统&#xff0c;不仅是对现有技术的一次大胆突破&#xff0c;更是对未来智能生活的一次深邃展望。 Harmo…

【耐水好】强耐水UV胶水是怎样的?

【耐水好】强耐水UV胶水是怎样的&#xff1f; 强耐水UV胶水是一种特殊的胶水&#xff0c;其设计重点在于其出色的耐水性能。以下是关于强耐水UV胶水的特点&#xff1a; 优异的耐水性能&#xff1a;这种胶水能在水环境下保持稳定的粘接强度&#xff0c;不易被水分解或削弱。因…

TextRank 算法

第1关&#xff1a;Jieba 在关键词提取中的应用 任务描述 本关任务&#xff1a;根据本关所学有关使用 Jieba 库进行关键词提取的知识&#xff0c;编写使用 Jieba 模块进行关键词提取的程序&#xff0c;并通过所有测试用例。 相关知识 为了完成本关任务&#xff0c;你需要掌握…

uniapp生成微信小程序二维码

文章目录 一、获取不限制的小程序码1、第一步&#xff1a;需要先获取ACCESS_TOKEN2、第二步&#xff1a;获取微信小程序二维码 二、获取小程序码1、第一步&#xff1a;需要先获取ACCESS_TOKEN2、第二步&#xff1a;获取微信小程序二维码 三、扫普通链接二维码打开小程序1、协议…

反向代购是怎么火起来的?今后的发展趋势如何?

反向代购和反向海淘的兴起可以归因于多个因素&#xff0c;这些因素共同推动了海外消费者对中国商品的需求和购买热潮。以下是对其火起来的原因的详细分析&#xff1a; 海外华人华侨的需求增加&#xff1a; 随着中国国际移民群体的扩大&#xff0c;海外华人华侨数量不断增多。这…

第三届仿真模拟、电子信息科学与技术国际学术会议(SMEI 2024,8月02-04)

随着仿真模拟技术的成熟和进步&#xff0c;仿真模拟技术越来越广泛地应用于工业工程、管理科学、社会经济、交通运输、生态环境、军事装备等各个科学领域&#xff0c;并深刻影响着信息技术和信息产业的发展。围绕仿真模拟、电子信息科学与技术等方面内容&#xff0c;为更好地促…

Gartner发布2024年企业高管增长议程:使网络安全投资与业务增长保持一致

网络安全投资和准备被视为推动企业发展的关键因素。除了避免损失之外&#xff0c;高管还应利用有效的以业务为中心的安全方法&#xff0c;通过大规模实现敏捷性和创新来推动收入增长。 主要发现 高增长公司通过扩大商业足迹来推动业绩&#xff0c;这需要大规模的创新、敏捷性和…

002 使用kibana操作ElasticSearch7.x

文章目录 4.使用kibana操作es4.1.文档操作1.put方式发送数据2.post方式发送数据3.查看索引文档 GET4.更新文档 POST5.删除文档&索引 DELETE6.批量添加数据_bulk 4.2.Query DLS(查询领域对象语言)1.url 检索数据语法2.查询所有数据3.查询全部数据并排序4.查询全部数据排序并…

时序分析基本概念介绍——min period 最小时钟周期

文章目录 前言一、什么是 min period&#xff1f;二、为什么检查 min period&#xff1f;三、如何设置 min period&#xff1f;四、如何检查 min period&#xff1f;五、如何修复 min period&#xff1f;总结 前言 我们在实际设计中可能会碰到这种情况&#xff0c;如果我们的m…

介绍ES6中的class类:(一) 类的基本语法

一、类的由来与简介 1. 简介 很早很早之前&#xff0c;在JavaScript的世界里&#xff0c;生成实例对象的传统方法是通过构造函数。 嗯哼&#xff1f; function Point(x, y) {this.x x;this.y y; }Point.prototype.toString function () {return ( this.x , this.y )…