当前位置: 首页 > news >正文

鸿蒙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学习资料

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

                        

相关文章:

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

XComponent控件常用于相机预览流的显示和游戏画面的绘制,在OpenHarmony上&#xff0c;可以配合Native Window创建OpenGL开发环境&#xff0c;并最终将OpenGL绘制的图形显示到XComponent控件。本文将采用"Native C"模板&#xff0c;调用OpenGL ES图形库绘制3D图形&…...

DM达梦数据库存储过程

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…...

【python】OpenCV—Color Correction

文章目录 cv2.aruco 介绍imutils.perspective.four_point_transform 介绍skimage.exposure.match_histograms 介绍牛刀小试遇到的问题 参考学习来自 OpenCV基础&#xff08;18&#xff09;使用 OpenCV 和 Python 进行自动色彩校正 cv2.aruco 介绍 一、cv2.aruco模块概述 cv2.…...

Java基础知识整理笔记

目录 1.关于Java概念 1.1 谈谈对Java的理解&#xff1f; 1.2 Java的基础数据类型&#xff1f; 1.3 关于面向对象的设计理解 1.3.1 面向对象的特性有哪些&#xff1f; 1.3.2 重写和重载的区别&#xff1f; 1.3.3 面向对象的设计原则是什么&#xff1f; 1.4 关于变量与方…...

知识图谱——Neo4j数据库实战

数据与代码链接见文末 1.Neo4j数据库安装 JDK 安装:https://www.oracle.com/java/technologies/javase-downloads.html Neo4j 安装:https://neo4j.com/download-center/ 配置好 JDK 和 Neo4j 的环境变量...

第十一次Javaweb作业

4.登录校验 4.1会话 --用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断开连接&#xff0c;会话结束。在一次会话中可以包含多次请求和响应。 会话跟踪&#xff1a;一种维护浏览器状态的方法&#xff0c;服务器需要识别多次请求…...

人工智能AI风口已开:如何赋予UI设计与视频剪辑新生命

随着科技的浪潮不断向前推进&#xff0c;人工智能&#xff08;AI&#xff09;正以惊人的速度重塑着我们的世界&#xff0c;特别是在创意产业的核心领域——UI设计与视频剪辑中&#xff0c;AI正逐步成为驱动行业创新与变革的关键力量。在这个AI技术全面开花的新时代&#xff0c;…...

计算机专业课面试常见问题-编程语言篇

目录 1. 程序的编译执行流程&#xff1f; 2. C浅拷贝和深拷贝的区别&#xff1f; 3. C虚函数&#xff1f; …...

CSS|05 继承性与优先级

继承性 一、继承性的特点&#xff1a; 1.外层元素身上的样式会被内层元素所继承 2.如果内层元素与外层元素身上的演示相同时&#xff0c;外层元素的样式会被内层元素所覆盖 二、关于继承性的问题 是不是所有样式都能被继承&#xff1f; 答&#xff1a;并不是所有样式能被继承…...

KVM性能优化之内存优化(宿主机)

linux系统自带了一技术叫透明巨型页&#xff08;transparent huge page&#xff09;&#xff0c;它允许所有的空余内存被用作缓存以提高性能&#xff0c;而且这个设置是默认开启的&#xff0c;我们不需要手动去操作。 Centos下&#xff0c;我们用cat /sys/kernel/mm/transpare…...

【Linux杂货铺】Linux学习之路:期末总结篇1

第一章 什么是Linux? Linux 是 UNIX 操作系统的一个克隆&#xff1b;它由林纳斯 本纳第克特 托瓦兹从零开始编写&#xff0c;并在网络上众多松散的黑客团队的帮助下得以发展和完善&#xff1b;它遵从可移植操作系统接口&#xff08;POSIX&#xff09;标准和单一 UNIX 规范…...

GPT-5的到来:智能飞跃与未来畅想

IT之家6月22日消息&#xff0c;在美国达特茅斯工程学院的采访中&#xff0c;OpenAI首席技术官米拉穆拉蒂确认了GPT-5的发布计划&#xff0c;预计将在一年半后推出。穆拉蒂形象地将GPT-4到GPT-5的飞跃比作高中生到博士生的成长。这一飞跃将给我们带来哪些变化&#xff1f;GPT-5的…...

gin中间件

在web应用服务中&#xff0c;完整的业务处理在技术上包含客户端操作&#xff0c;服务端处理&#xff0c;返回处理结果给客户端三个步骤。但是在在更负责的业务和需求场景。一个完整的系统可能要包含鉴权认证&#xff0c;权限管理&#xff0c;安全检查&#xff0c;日志记录等多维…...

swagger常用注解

最近查看接口文档的时候发现&#xff0c;POST方法中的query没法在swagger中显示&#xff0c;查了才发现这是因为Swagger或OpenAPI规范默认将HTTP POST请求的参数识别为请求体&#xff08;body&#xff09;参数&#xff0c;而不是查询字符串&#xff08;query&#xff09;参数。…...

【Flink metric(1)】Flink指标系统的系统性知识:获取metric以及注册自己的metric

文章目录 一. Registering metrics&#xff1a;向flink注册新自己的metrics1. 注册metrics2. Metric types:指标类型2.1. Counter2.2. Gauge2.3. Histogram(ing)2.4. Meter 二. Scope:指标作用域1. User Scope2. System Scope ing3. User Variables 三. Reporter ing四. System…...

命令模式(Command Pattern)

命令模式&#xff08;Command Pattern&#xff09; 定义 命令模式是对命令的封装&#xff0c;每一个命令都是一个操作&#xff1a;请求的一方发出请求要求执行一个操作&#xff1b;接收的一方收到请求&#xff0c;并执行操作。 命令模式解耦了请求方和接收方&#xff0c;请求…...

掌握Symfony的模板继承:构建强大且灵活的Web界面

掌握Symfony的模板继承&#xff1a;构建强大且灵活的Web界面 在Symfony框架中&#xff0c;模板继承是一个强大的功能&#xff0c;它允许开发者创建可重用的布局模板&#xff0c;并通过扩展这些模板来构建具体的页面。这种机制不仅提高了代码的可维护性&#xff0c;还使得页面结…...

uboot基本使用网络命令和从服务器端下载linux内核启动

网络命令ip地址设置: setenv gmac_debug 0; setenv mdio_intf rgmii; setenv bootdelay 1; setenv ethaddr 00:xxxx:81:70; // mac地址 setenv ipaddr xxx; //开发板 IP 地址 setenv netmask 255.255.255.0; setenv gatewayip xxx.1; setenv serverip xxxx; //服…...

解决ArcGIS导出的svg格式的图片插入Word后的字体问题

背景 在ArcGIS中设置字体为Times New Roman&#xff0c;但导入Word后字体转为等线。 ArcGIS中的Layout 导入Word​​​​​​ 原因分析 Word无法识别嵌入进SVG格式文件中的字体。 解决方案 在Export Layer窗口中&#xff0c;将Embed fonts取消勾选&#xff0c;Convert cha…...

如何确保 Puppet 配置在复杂网络环境中的可靠分发和同步?

在复杂网络环境中确保 Puppet 配置的可靠分发和同步可以采取以下措施&#xff1a; 网络拓扑规划&#xff1a;在复杂网络环境中&#xff0c;首先需要进行网络拓扑规划&#xff0c;确保网络结构合理&#xff0c;并能够支持可靠的分发和同步机制。 Puppet Master 多节点部署&…...

2024最新!将mysql的数据导入到Solr

Solr导入mysql的数据 如何安装导入数据前准备配置Solr的Jar包以及Mysql驱动包1.1、将solr-8.11.3\dist下的两个包进行移动1.2、将mysql-connect包也移动到该位置1.3、重启Solr项目 配置xml2.1、第一步我们需要创建核心2.2、第二步修改xml(这里是结合19年的教程)2.3、 创建data-…...

Python数据分析第二课:conda的基础命令

Python数据分析第二课&#xff1a;conda的基础命令 1.conda是什么? conda是一个开源的包管理系统&#xff0c;可以帮助我们进行管理多个不同版本的软件包&#xff0c;还可以帮助我们建立虚拟环境&#xff0c;以便对不同的项目进行隔离。 简单来说&#xff0c;conda是一个软…...

LayoutInflater加载流程

简介 LayoutInflater在日常的Android开发中是经常使用的类&#xff0c;常常用于XML中View的加载相关流程。本文主要总结一些其常见api的源码流程。 获取LayoutInflater 我们一般会在Activity的onCreate方法中会通过setContentView方法设置自己的布局layoutId&#xff0c;Act…...

PLC数据采集案例

--------天津三石峰科技案例分享 项目介绍 项目背景 本项目为天津某钢铁集团下数字化改造项目&#xff0c;主要解决天津大型钢厂加氢站数字化改造过程中遇到的数据采集需求。项目难点PLC已经在运行了&#xff0c;需要采集里面数据&#xff0c;不修改程序&#xff0c;不影响P…...

基于单片机和LabVIEW 的远程矿井水位监控系统设计

摘要 &#xff1a; 针 对 现 有 矿 井 水 位 监 控 系 统 存 在 结 构 复 杂 和 不 能 远 程 监 控 的 问 题 &#xff0c; 设计了基于单片机和&#xff2c;&#xff41;&#xff42;&#xff36;&#xff29;&#xff25;&#xff37; 的远程矿井水位监控系统 &#xff0c; 详…...

element 表格嵌套表单验证指定行

elementui表格嵌套动态表单&#xff0c;单独验证某一行输入项是否符合校验规则&#xff1b; input动态绑定校验 :prop"imgTable. scope.$index .bxName" <el-form :model"formTable" ref"formTable" inline size"small"><…...

CORE Mobility Errorr的调试

在运行CORE tutorial 3中的mobility示例时&#xff0c;出现如下错误&#xff1a; 当看到这个问题的时候&#xff0c;并没有仔细去分析日志和现象&#xff0c;在core-daemon的进程打印界面只看了一下最后的出错堆栈&#xff1a; 2024-06-27 10:43:48,614 - ERROR - _server:_ca…...

基于weixin小程序乡村旅游系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商家管理&#xff0c;旅游景点管理&#xff0c;景点类型管理&#xff0c;景点路线管理&#xff0c;系统管理 商家帐号账号功能包括&#xff1a;系统首页&#xff0c;旅游景点管理&…...

详解三种常用标准化 Batch Norm Layer Norm RMSNorm

参考&#xff1a; BN究竟起了什么作用&#xff1f;一个闭门造车的分析《动手学深度学习》7.5 节 深度学习中&#xff0c;归一化是常用的稳定训练的手段&#xff0c;CV 中常用 Batch Norm&#xff1b; Transformer 类模型中常用 layer norm&#xff0c;而 RMSNorm 是近期很流行…...

云计算运维工程师面试

1. 云计算运维工程师的角色和职责是什么? 回答: 云计算运维工程师负责确保云计算环境(包括硬件和软件系统)的高可用性和稳定性。他们的主要职责包括: 监测系统和应用程序的性能,确保它们正常运行。故障排除,快速响应并解决系统或应用程序中出现的问题。容量规划,根据…...

聚观早报 | iPhone 16核心硬件曝光;三星Galaxy全球新品发布会

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 6月28日消息 iPhone 16核心硬件曝光 三星Galaxy全球新品发布会 苹果正多方下注布局AI商店 黄仁勋2024年薪酬3400…...

web前端之文档流、浮动、定位详解

目录 一、文档流 二、浮动 1.添加浮动 2.清除浮动 三、定位 1.相对定位 2.绝对定位 一、文档流 什么是文档流&#xff1f; ● 文档流指的是文档中的标签在排列时所占用的位置。 将窗体自上而下分成一行行 &#xff0c;并在每 行中按从左至右的顺序排放标签&#xff0c…...

[JS]节点操作

DOM节点 DOM树中的所有内容都是节点, 我们重点关注元素节点 作用 使开发者可以根据节点的关系获取元素, 而不是只能依赖选择器, 提高了编码的灵活性 节点分类 元素节点: 所有的标签都是元素节点, html是根节点属性节点: 所有的属性都是属性节点, 比如href文本节点: 所有的文…...

基于SpringBoot+Vue的论坛网站系统(带1w+文档)

基于SpringBootVue的论坛网站系统(带1w文档) 对于之前论坛网站的管理&#xff0c;大部分都是使用传统的人工方式去管理&#xff0c;这样导致了管理效率低下、出错频率高。而且&#xff0c;时间一长的话&#xff0c;积累下来的数据信息不容易保存&#xff0c;对于查询、更新还有…...

03逻辑门电路

分立门电路&#xff1a; 集成门电路&#xff1a; TTL门电路 MOS门电路&#xff1a;NMOS门电路、PMOS门电路、CMOS门电路 BICMOS门电路&#xff1a;CMOS的高输入阻抗和TTL的高放大倍数的结合 向更低功耗、更高速度发展 MOS管的Rdson在可变电阻区的阻值也一般会小于1000欧姆 …...

2毛钱的SOT23-5封装28V、1.5A、1.2MHz DCDC转换器用于LCD偏置电源和白光LED驱动等MT3540升压芯片

前言 之前发了一个TI的BOOST升压芯片&#xff0c;用于LCD偏置电压或LED驱动&#xff0c;请访问以下链接。 6毛钱SOT-23封装28V、400mA 开关升压转换器&#xff0c;LCD偏置电源和白光LED应用芯片TPS61040 国产半导体厂家发展迅猛&#xff0c;今天推荐一个公司带“航天”的升压…...

Zookeeper部署

Zookeeper部署 下载安装包Linux解压安装包修改配置文件编辑zoo.cf配置 启动服务停止服务常用zookeeper指令查看namespace列表创建namespace删除namespace 注意&#xff1a;该文章为简单部署操作&#xff0c;没有复杂的配置内容&#xff0c;用的是3.7.2版本。 下载安装包 进入z…...

2.x86游戏实战-跨进程读取血量

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 接下来会写C/C代码&#xff0c;C/C代码不是很难&#xff0c;然后为了快速掌握逆向这个技能&#xff0c;我…...

element-plus 日期选择添加确定按钮

需求&#xff1a;选择日期后&#xff0c;点击确定按钮关闭面板 思路&#xff1a; 使用shortcuts自定义确定和取消按钮选择日期后使用handleOpen()强制开启面板点击确定后使用handleClose()关闭面板 <template><el-date-pickerref"pickerRef"v-model"…...

Redis优化之持久化

目录 1.Redis高可用 2.Redis持久化 2.1 RDB持久化 2.1.1 触发条件 2.1.2 执行流程 2.1.3 启动时加载 2.2 AOF持久化 2.2.1 开启AOF 2.2.2 执行流程 2.2.3 文件重写触发方式 2.2.4 文件重写的流程 2.2.5 启动时加载 2.3 RDB和AOF的优缺点 3.Redis性能管理 3.1 查看…...

ubuntu22.04 编译安装libcurl C++ library

1. 安装必须的依赖项 sudo apt update #sudo apt install build-essential autoconf libtool pkg-config libssl-dev libz-dev 2. 下载及编译前准备 cd /opt mkdir curl && cd curl mkdir build && mkdir install git clone https://github.com/curl/curl.git…...

js函数闭包解析

闭包是JavaScript中非常重要的概念&#xff0c;理解闭包对于编写高质量的代码是至关重要的。本文将详细解析闭包的概念&#xff0c;并提供一些代码示例来帮助读者更好地理解闭包的使用。 什么是闭包&#xff1f; 闭包是指在一个函数内部定义的函数&#xff0c;该函数可以访问包…...

查看Oracle、MySQL、PostGreSQL中的依赖关系

查看Oracle、MySQL、PostGreSQL中的依赖关系 在有些程序员开发习惯中&#xff0c;喜欢为了应用代码的简洁或复用&#xff0c;而在数据库创建一个复杂关连查询的VIEW&#xff0c;甚至是VIEW套VIEW嵌套使用&#xff0c; 这里就有个问题如果上线后如发现依赖的表字段类型或长度不…...

多线程(基础)

前言&#x1f440;~ 上一章我们介绍了什么是进程&#xff0c;对于进程就了解那么多即可&#xff0c;我们作为java程序员更关注线程&#xff0c;线程内容比较多&#xff0c;所以我们要分好几部分才能讲完 目录 进程的缺点 多线程&#xff08;重要&#xff09; 进程和线程的区…...

BUG cn.bing.com 重定向的次数过多,无法搜索内容

BUG cn.bing.com 重定向的次数过多&#xff0c;无法搜索内容 环境 windows 11 edge浏览器详情 使用Microsoft Edge 必应搜索显示"cn.bing.com"重定向次数过多&#xff0c;无法进行正常的检索功能 解决办法 检查是否开启某些科_学_上_网&#xff08;翻_墙&#xf…...

【数据科学】学习资源汇总(不定时更新)

好书推荐&#xff1a;BooksPDF/数据科学/Python数据科学手册.pdf at master zhixingchou/BooksPDF GitHub...

完美解决ValueError: column index (256) not an int in range(256)的正确解决方法,亲测有效!!!

完美解决ValueError: column index (256) not an int in range(256)的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 亲测有效 完美解决ValueError: column index (256) not an int in range(256)的正确解决方法&#xff0c;亲测有效&#xff01;&…...

# 音频处理4_傅里叶变换

1.离散傅里叶变换 对于离散时域信号 x[n]使用离散傅里叶变换&#xff08;Discrete Fourier Transform, DFT&#xff09;进行频域分析。 DFT 将离散信号 x[n] 变换为其频谱表示 X[k]&#xff0c;定义如下&#xff1a; X [ k ] ∑ n 0 N − 1 x [ n ] e − j 2 π k n N X[k]…...

提升网络速度的几种有效方法

在数字化时代&#xff0c;网络速度对于我们的日常生活和工作至关重要。无论是观看高清视频、在线游戏&#xff0c;还是进行视频会议&#xff0c;快速稳定的网络连接都是不可或缺的。如果你发现自己当前的网络速度不尽如人意&#xff0c;那么不妨尝试以下几种方法来提升它。 升…...

@PathVariable注解的使用及源码解析

前言 PathVariable 注解是我们进行JavaEE开发&#xff0c;最常见的几个注解之一&#xff0c;这篇博文我们以案例和源码相结合&#xff0c;帮助大家更好的了解PathVariable 注解 使用案例 1.获取 URL 上的值 RequestMapping("/id/{id}") public Object getId(Path…...