首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Qt-for-鸿蒙PC-AnimationDemo 动画演示开源鸿蒙开发实践

Qt-for-鸿蒙PC-AnimationDemo 动画演示开源鸿蒙开发实践

作者头像
红目香薰
发布2025-12-16 16:56:53
发布2025-12-16 16:56:53
1220
举报
文章被收录于专栏:CSDNToQQCodeCSDNToQQCode

📋 项目概述

本文档基于一个完整的 AnimationDemo 项目,详细介绍了如何在 HarmonyOS 平台上使用 Qt 实现各种动画效果。项目实现了6种不同类型的动画演示(呼吸灯、渐变效果、移动动画、旋转动画、缩放动画、文字动画),展示了 Qt Quick 动画系统在 HarmonyOS 平台上的实际应用,为开发者提供了丰富的动画效果参考。

项目地址:https://gitcode.com/szkygc/HarmonyOs_PC-PGC/blob/main/AnimationDemo

✨ 主要功能
在这里插入图片描述
在这里插入图片描述
  • 6种动画效果:呼吸灯、渐变效果、移动动画、旋转动画、缩放动画、文字动画
  • 动画控制:支持开始、停止、暂停、继续等控制操作
  • 动画类型:透明度动画、位置动画、旋转动画、缩放动画、组合动画
  • ComboBox 选择器:通过下拉框切换不同的动画演示
  • 响应式布局:适配不同屏幕尺寸
  • 窗口状态处理:正确处理窗口最大化、最小化等状态变化
  • 动画生命周期管理:自动启动、停止、重置动画状态
  • 完整的触摸交互支持

🛠️ 技术栈

  • 开发框架: Qt 5.15+ for HarmonyOS
  • 编程语言: C++ / QML / JavaScript
  • 动画框架: Qt Quick Animation
  • 界面框架: Qt Quick Controls 2
  • 构建工具: CMake
  • 目标平台: HarmonyOS (OpenHarmony) / PC

🏗️ 项目架构

目录结构
代码语言:javascript
复制
AnimationDemo/
├── entry/src/main/
│   ├── cpp/
│   │   ├── main.cpp              # 应用入口(HarmonyOS适配)
│   │   ├── main.qml              # 主界面(6种动画演示)
│   │   ├── CMakeLists.txt        # 构建配置
│   │   └── qml.qrc               # QML资源文件
│   ├── module.json5              # 模块配置
│   └── resources/                # 资源文件
└── image/
    └── 演示示例.gif              # 演示动图
组件层次结构
代码语言:javascript
复制
ApplicationWindow (main.qml)
├── Column (主布局)
│   ├── Rectangle (标题栏)
│   │   └── Row
│   │       └── Text (标题)
│   ├── Rectangle (Demo选择器)
│   │   └── Row
│   │       ├── Text (标签)
│   │       └── ComboBox (动画选择器)
│   └── Rectangle (动画演示区域)
│       └── Item (动画容器)
│           ├── Rectangle (呼吸灯动画)
│           │   └── SequentialAnimation on alpha
│           ├── Rectangle (渐变效果动画)
│           │   ├── NumberAnimation (淡出)
│           │   └── NumberAnimation (淡入)
│           ├── Rectangle (移动动画)
│           │   └── SequentialAnimation on x
│           ├── Rectangle (旋转动画)
│           │   └── NumberAnimation on rotation
│           ├── Rectangle (缩放动画)
│           │   └── NumberAnimation on scaleValue
│           └── Item (文字动画)
│               └── NumberAnimation on yPosition

📝 核心功能实现

1. HarmonyOS 入口函数:qtmain()

⚠️ 关键要点:HarmonyOS 真机上必须使用 qtmain() 而不是 main()

代码语言:javascript
复制
// ✅ 正确写法
extern "C" int qtmain(int argc, char **argv)
{
    // Qt 应用作为共享库加载,生命周期由 HarmonyOS 管理
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return app.exec();  // ⚠️ 重要:必须调用 exec() 启动事件循环
}

// ❌ 错误写法(桌面应用方式)
int main(int argc, char *argv[])
{
    // 这种方式在 HarmonyOS 上会导致应用无法正常启动
}

原因说明

  • HarmonyOS 将 Qt 应用作为共享库(.so)加载
  • 应用生命周期由 HarmonyOS 的 Ability 管理
  • qtmain() 是 HarmonyOS Qt 插件的标准入口点

2. OpenGL ES 表面格式配置

⚠️ 关键要点:必须在创建 QGuiApplication 之前配置 QSurfaceFormat

代码语言:javascript
复制
// Step 1: 配置 OpenGL ES 表面格式(必须在创建应用之前!)
QSurfaceFormat format;

// 设置 Alpha 通道(透明度)
format.setAlphaBufferSize(8);      // 8 位 Alpha 通道

// 设置颜色通道(RGBA 32 位真彩色)
format.setRedBufferSize(8);        // 8 位红色通道
format.setGreenBufferSize(8);      // 8 位绿色通道
format.setBlueBufferSize(8);       // 8 位蓝色通道

// 设置深度和模板缓冲区
format.setDepthBufferSize(24);     // 24 位深度缓冲
format.setStencilBufferSize(8);    // 8 位模板缓冲

// 指定渲染类型为 OpenGL ES(HarmonyOS要求)
format.setRenderableType(QSurfaceFormat::OpenGLES);

// 指定 OpenGL ES 版本为 3.0(推荐)
format.setVersion(3, 0);

// ⚠️ 关键:必须在创建 QGuiApplication 之前设置默认格式!
QSurfaceFormat::setDefaultFormat(format);

// Step 2: 创建 Qt 应用实例(必须在设置格式之后)
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
QGuiApplication app(argc, argv);

配置说明

  • OpenGL ES 3.0:HarmonyOS 推荐使用 OpenGL ES 3.0
  • RGBA 8-8-8-8:32 位真彩色,支持透明度
  • 深度缓冲 24 位:用于 3D 渲染和层级管理
  • 模板缓冲 8 位:用于复杂图形效果

3. 呼吸灯动画实现

核心思想:通过 SequentialAnimation 实现透明度的循环变化,形成呼吸灯效果。

代码语言:javascript
复制
Rectangle {
    visible: currentDemoIndex === 0
    anchors.fill: parent
    color: "#1E1E1E"
  
    property int alpha: 20  // 透明度值(0-255)
  
    Column {
        anchors.centerIn: parent
        spacing: 30
      
        Text {
            anchors.horizontalCenter: parent.horizontalCenter
            text: "呼吸灯动画"
            font.pixelSize: 36
            font.bold: true
            color: "#FFFFFF"
        }
      
        Rectangle {
            anchors.horizontalCenter: parent.horizontalCenter
            width: 200
            height: 200
            radius: 100
            color: Qt.rgba(0, 1, 0, parent.parent.alpha / 255.0)  // 使用 alpha 值
            border.color: "#FFFFFF"
            border.width: 2
        }
    }
  
    SequentialAnimation on alpha {
        running: currentDemoIndex === 0  // 只在选中时运行
        loops: Animation.Infinite  // 无限循环
        NumberAnimation {
            from: 20
            to: 255
            duration: 2500
            easing.type: Easing.InOutQuad
        }
        NumberAnimation {
            from: 255
            to: 20
            duration: 2500
            easing.type: Easing.InOutQuad
        }
    }
}

关键点

  • SequentialAnimation on alpha:直接在 alpha 属性上应用顺序动画
  • 循环动画:先增大透明度(20→255),再减小透明度(255→20)
  • 缓动函数:使用 Easing.InOutQuad 实现平滑的加速和减速
  • 条件运行:使用 running: currentDemoIndex === 0 控制动画运行

4. 渐变效果动画实现

核心思想:使用独立的 NumberAnimation 实现淡入淡出效果,通过按钮控制。

代码语言:javascript
复制
Rectangle {
    id: fadeDemo
    visible: currentDemoIndex === 1
    anchors.fill: parent
    color: "#F5F5F5"
  
    property real targetOpacity: 1.0  // 目标透明度
  
    Column {
        anchors.centerIn: parent
        spacing: 30
      
        Text {
            anchors.horizontalCenter: parent.horizontalCenter
            text: "渐变效果动画"
            font.pixelSize: 36
            font.bold: true
            color: "#333333"
        }
      
        Rectangle {
            id: fadeTarget
            anchors.horizontalCenter: parent.horizontalCenter
            width: 300
            height: 200
            color: "#2196F3"
            radius: 10
            opacity: fadeDemo.targetOpacity  // 绑定到目标透明度
        }
      
        Row {
            anchors.horizontalCenter: parent.horizontalCenter
            spacing: 20
          
            Button {
                text: "淡出"
                width: 150
                height: 60
                font.pixelSize: 28
                onClicked: {
                    fadeOutAnimation.start()  // 启动淡出动画
                }
            }
          
            Button {
                text: "淡入"
                width: 150
                height: 60
                font.pixelSize: 28
                onClicked: {
                    fadeInAnimation.start()  // 启动淡入动画
                }
            }
        }
    }
  
    // 淡出动画
    NumberAnimation {
        id: fadeOutAnimation
        target: fadeDemo
        property: "targetOpacity"
        from: 1.0
        to: 0.0
        duration: 1000
    }
  
    // 淡入动画
    NumberAnimation {
        id: fadeInAnimation
        target: fadeDemo
        property: "targetOpacity"
        from: 0.0
        to: 1.0
        duration: 1000
    }
}

关键点

  • 独立动画对象:使用 NumberAnimation 创建独立的动画对象
  • 手动控制:通过 start() 方法手动启动动画
  • 属性绑定:目标元素的 opacity 绑定到 targetOpacity 属性
  • 双向动画:淡出(1.0→0.0)和淡入(0.0→1.0)两个独立的动画

5. 移动动画实现

核心思想:使用 SequentialAnimation 实现元素在左右两个位置之间的循环移动。

代码语言:javascript
复制
Rectangle {
    id: moveDemo
    visible: currentDemoIndex === 2
    anchors.fill: parent
    color: "#F5F5F5"
  
    Text {
        anchors.top: parent.top
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.topMargin: 30
        text: "移动动画"
        font.pixelSize: 36
        font.bold: true
        color: "#333333"
    }
  
    Rectangle {
        id: movingLabel
        width: 150
        height: 60
        color: "#3498db"
        radius: 5
        x: 0
        y: (moveDemo.height - height) / 2  // 垂直居中
      
        Text {
            anchors.centerIn: parent
            text: "移动的标签"
            font.pixelSize: 24
            color: "#FFFFFF"
        }
      
        SequentialAnimation on x {
            id: moveAnimation
            running: currentDemoIndex === 2 && moveDemo.visible
            loops: Animation.Infinite
            NumberAnimation {
                from: 0
                to: moveDemo.width - movingLabel.width  // 移动到右边界
                duration: 2000
                easing.type: Easing.InOutQuad
            }
            NumberAnimation {
                from: moveDemo.width - movingLabel.width
                to: 0  // 移动回左边界
                duration: 2000
                easing.type: Easing.InOutQuad
            }
        }
    }
  
    // 当切换到移动动画时,重置位置并启动动画
    onVisibleChanged: {
        if (visible) {
            movingLabel.x = 0
            moveAnimation.restart()
        }
    }
}

关键点

  • SequentialAnimation on x:直接在 x 属性上应用顺序动画
  • 动态计算:使用 moveDemo.width - movingLabel.width 计算右边界位置
  • 循环移动:先向右移动(0→右边界),再向左移动(右边界→0)
  • 状态重置:使用 onVisibleChanged 在显示时重置位置

6. 旋转动画实现

核心思想:使用 NumberAnimation 实现元素的持续旋转。

代码语言:javascript
复制
Rectangle {
    visible: currentDemoIndex === 3
    anchors.fill: parent
    color: "#F5F5F5"
  
    property real rotation: 0  // 旋转角度
  
    Column {
        anchors.centerIn: parent
        spacing: 30
      
        Text {
            anchors.horizontalCenter: parent.horizontalCenter
            text: "旋转动画"
            font.pixelSize: 36
            font.bold: true
            color: "#333333"
        }
      
        Rectangle {
            anchors.horizontalCenter: parent.horizontalCenter
            width: 200
            height: 200
            color: "transparent"
          
            Rectangle {
                anchors.centerIn: parent
                width: 150
                height: 150
                color: "#9C27B0"
                radius: 10
                rotation: parent.parent.parent.rotation  // 绑定到旋转角度
              
                Text {
                    anchors.centerIn: parent
                    text: "旋转"
                    font.pixelSize: 28
                    color: "#FFFFFF"
                }
            }
        }
    }
  
    NumberAnimation on rotation {
        running: currentDemoIndex === 3
        loops: Animation.Infinite
        from: 0
        to: 360  // 360度旋转
        duration: 5000
        easing.type: Easing.Linear  // 线性缓动,匀速旋转
    }
}

关键点

  • NumberAnimation on rotation:直接在 rotation 属性上应用动画
  • 360度旋转:从 0 度旋转到 360 度
  • 线性缓动:使用 Easing.Linear 实现匀速旋转
  • 属性绑定:子元素的 rotation 绑定到父元素的 rotation 属性

7. 缩放动画实现

核心思想:使用独立的 NumberAnimation 实现元素的缩放效果,支持手动控制。

代码语言:javascript
复制
Rectangle {
    id: scaleDemo
    visible: currentDemoIndex === 4
    anchors.fill: parent
    color: "#F5F5F5"
  
    property real scaleValue: 1.0  // 缩放值
  
    Column {
        anchors.centerIn: parent
        spacing: 30
      
        Text {
            anchors.horizontalCenter: parent.horizontalCenter
            text: "缩放动画"
            font.pixelSize: 36
            font.bold: true
            color: "#333333"
        }
      
        Rectangle {
            id: scaleTarget
            anchors.horizontalCenter: parent.horizontalCenter
            width: 200 * scaleDemo.scaleValue  // 使用缩放值
            height: 200 * scaleDemo.scaleValue
            color: "#FF5722"
            radius: 10
          
            Text {
                anchors.centerIn: parent
                text: "缩放"
                font.pixelSize: 28
                color: "#FFFFFF"
            }
        }
      
        Button {
            anchors.horizontalCenter: parent.horizontalCenter
            text: scaleAnimation.running ? "停止动画" : "开始动画"
            width: 200
            height: 60
            font.pixelSize: 28
            onClicked: {
                if (scaleAnimation.running) {
                    scaleAnimation.stop()  // 停止动画
                } else {
                    // 根据当前缩放值决定目标值
                    if (scaleDemo.scaleValue === 1.0) {
                        scaleAnimation.from = 1.0
                        scaleAnimation.to = 2.0  // 放大到2倍
                    } else {
                        scaleAnimation.from = 2.0
                        scaleAnimation.to = 1.0  // 缩小到1倍
                    }
                    scaleAnimation.start()
                }
            }
        }
    }
  
    NumberAnimation {
        id: scaleAnimation
        target: scaleDemo
        property: "scaleValue"
        duration: 1000
        easing.type: Easing.InOutQuad
    }
  
    // 切换到缩放动画时,重置缩放值
    onVisibleChanged: {
        if (visible) {
            scaleValue = 1.0
        }
    }
}

关键点

  • 动态缩放:使用 scaleValue 属性控制元素大小
  • 双向缩放:支持放大(1.0→2.0)和缩小(2.0→1.0)
  • 手动控制:通过按钮控制动画的开始和停止
  • 状态重置:切换时重置缩放值

8. 文字动画实现

核心思想:使用 NumberAnimation 实现文字从下往上的移动效果。

代码语言:javascript
复制
Item {
    id: textDemo
    visible: currentDemoIndex === 5
    anchors.fill: parent
  
    property real yPosition: -parent.height  // Y位置(初始在屏幕外)
  
    Rectangle {
        anchors.fill: parent
        color: "#1E1E1E"
      
        Text {
            anchors.top: parent.top
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.topMargin: 30
            text: "文字动画"
            font.pixelSize: 36
            font.bold: true
            color: "#FFFFFF"
        }
      
        Text {
            id: animatedText
            anchors.horizontalCenter: parent.horizontalCenter
            y: textDemo.height / 2 + textDemo.yPosition  // 使用 yPosition
            text: "欢迎使用Qt动画演示"
            font.pixelSize: 32
            color: "#00FF00"
        }
      
        Button {
            anchors.bottom: parent.bottom
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.bottomMargin: 50
            text: "重新播放"
            width: 200
            height: 60
            font.pixelSize: 28
            onClicked: {
                textDemo.yPosition = -textDemo.height  // 重置到屏幕外
                textAnimation.start()
            }
        }
    }
  
    NumberAnimation {
        id: textAnimation
        target: textDemo
        property: "yPosition"
        from: -textDemo.height  // 从屏幕外开始
        to: 0  // 移动到屏幕中央
        duration: 1000
        easing.type: Easing.OutInQuad
    }
  
    // 切换到文字动画时,自动播放
    onVisibleChanged: {
        if (visible) {
            yPosition = -height
            Qt.callLater(function() {
                textAnimation.start()
            })
        }
    }
}

关键点

  • Y位置动画:使用 yPosition 属性控制文字的垂直位置
  • 屏幕外开始:初始位置设置为 -height(屏幕外)
  • 自动播放:切换到此动画时自动播放
  • 重新播放:提供按钮手动重新播放动画

9. ComboBox 动画选择器

核心思想:使用 ComboBox 实现动画演示的切换。

代码语言:javascript
复制
// Demo名称列表
property var demoNames: [
    "呼吸灯",
    "渐变效果",
    "移动动画",
    "旋转动画",
    "缩放动画",
    "文字动画"
]

// 当前选中的动画demo索引
property int currentDemoIndex: 0

ComboBox {
    id: demoComboBox
    width: 300
    height: 60
    model: demoNames
    currentIndex: 0
    font.pixelSize: 26
    onCurrentIndexChanged: {
        currentDemoIndex = currentIndex  // 更新当前索引
    }
}

关键点

  • 数据模型:使用数组定义动画名称列表
  • 索引绑定currentDemoIndex 绑定到 ComboBoxcurrentIndex
  • 自动切换:选择不同的动画时,自动更新 currentDemoIndex
  • 条件显示:每个动画使用 visible: currentDemoIndex === index 控制显示

10. 窗口状态处理

核心思想:正确处理窗口状态变化,确保动画在窗口状态改变后正常工作。

代码语言:javascript
复制
ApplicationWindow {
    id: root
  
    // 窗口状态变化处理(修复最大化黑屏问题)
    onVisibilityChanged: {
        if (visibility === Window.Windowed || visibility === Window.Maximized || visibility === Window.FullScreen) {
            Qt.callLater(function() {
                console.log("AnimationDemo: 窗口可见性变化,强制更新")
                // 强制更新所有可见的动画组件
                if (currentDemoIndex === 2) {
                    // 移动动画需要重新启动
                    movingLabel.x = 0
                }
            })
        }
    }
  
    onWindowStateChanged: {
        console.log("AnimationDemo: 窗口状态变化 - state:", windowState)
        // 窗口状态变化时,延迟更新以确保尺寸已更新
        Qt.callLater(function() {
            console.log("AnimationDemo: 窗口尺寸:", width, "x", height)
        })
    }
}

关键点

  • 可见性处理:使用 onVisibilityChanged 处理窗口可见性变化
  • 状态重置:窗口状态改变时重置动画状态
  • 延迟更新:使用 Qt.callLater 确保在尺寸更新后再处理
  • 日志输出:输出日志便于调试

11. ⚠️ 关键配置:deviceTypes 必须包含 “2in1”

这是本文档最重要的发现!

entry/src/main/module.json5 文件中,deviceTypes 必须包含 "2in1"

代码语言:javascript
复制
{
  "module": {
    "name": "entry",
    "type": "entry",
    "deviceTypes": [
      "default",
      "tablet",
      "2in1"  // ⚠️ 必须添加!否则打包会失败
    ],
    // ...
  }
}

错误信息

代码语言:javascript
复制
hvigor ERROR: Failed :entry:default@PackageHap...
Ohos BundleTool [Error]: 10011001 Parse and check args invalid in hap mode.
Error Message: --json-path must be the config.json file or module.json file.

原因分析

  • HarmonyOS PC 设备(如 MateBook)被识别为 "2in1" 设备类型
  • 如果 deviceTypes 中缺少 "2in1",打包工具无法正确识别配置文件路径
  • 这会导致打包失败,即使应用能在真机上运行

最佳实践

代码语言:javascript
复制
"deviceTypes": [
  "default",   // 手机
  "tablet",    // 平板
  "2in1"       // ⚠️ PC/2合1设备(必须添加!)
]

🐛 常见问题与解决方案

问题 1:动画不运行

症状:选择动画后,动画效果没有显示。

原因

  1. running 属性没有正确设置
  2. currentDemoIndex 没有正确更新
  3. 动画目标属性没有正确绑定

解决方案

代码语言:javascript
复制
// ✅ 正确:使用条件运行
SequentialAnimation on alpha {
    running: currentDemoIndex === 0  // 明确指定运行条件
    loops: Animation.Infinite
    // ...
}

// ❌ 错误:没有设置运行条件
SequentialAnimation on alpha {
    loops: Animation.Infinite  // 缺少 running 属性
    // ...
}

问题 2:动画切换时状态不正确

症状:切换动画时,新动画从错误的状态开始。

原因

  1. 没有在切换时重置动画状态
  2. onVisibleChanged 没有正确处理

解决方案

代码语言:javascript
复制
Rectangle {
    id: moveDemo
    visible: currentDemoIndex === 2
  
    // ✅ 正确:切换时重置状态
    onVisibleChanged: {
        if (visible) {
            movingLabel.x = 0  // 重置位置
            moveAnimation.restart()  // 重新启动动画
        }
    }
}

问题 3:动画性能问题

症状:动画运行卡顿,不流畅。

原因

  1. 动画持续时间过长
  2. 同时运行多个动画
  3. 使用了复杂的计算

解决方案

代码语言:javascript
复制
// ✅ 正确:使用合适的持续时间
NumberAnimation {
    duration: 1000  // 1秒,性能良好
    easing.type: Easing.InOutQuad
}

// ❌ 错误:持续时间过长
NumberAnimation {
    duration: 10000  // 10秒,可能影响性能
}

问题 4:窗口最大化后动画停止

症状:窗口最大化后,动画不再运行。

原因

  1. 窗口状态变化时没有重新启动动画
  2. 窗口尺寸变化导致动画计算错误

解决方案

代码语言:javascript
复制
ApplicationWindow {
    id: root
  
    // ✅ 正确:处理窗口状态变化
    onVisibilityChanged: {
        if (visibility === Window.Windowed || visibility === Window.Maximized) {
            Qt.callLater(function() {
                // 重新启动动画
                if (currentDemoIndex === 2) {
                    movingLabel.x = 0
                    moveAnimation.restart()
                }
            })
        }
    }
}

问题 5:动画无法停止

症状:点击停止按钮后,动画仍在运行。

原因

  1. 使用了 SequentialAnimation on property 语法,无法直接停止
  2. 没有正确使用独立的动画对象

解决方案

代码语言:javascript
复制
// ✅ 正确:使用独立的动画对象
NumberAnimation {
    id: scaleAnimation
    target: scaleDemo
    property: "scaleValue"
    duration: 1000
}

Button {
    onClicked: {
        if (scaleAnimation.running) {
            scaleAnimation.stop()  // 可以停止
        } else {
            scaleAnimation.start()  // 可以启动
        }
    }
}

// ❌ 错误:使用 on property 语法,无法停止
NumberAnimation on scaleValue {
    duration: 1000
    // 无法直接停止
}

问题 6:打包失败 - json-path 错误

症状

代码语言:javascript
复制
hvigor ERROR: Failed :entry:default@PackageHap...
Error Message: --json-path must be the config.json file or module.json file.

原因module.json5 中的 deviceTypes 缺少 "2in1"

解决方案

代码语言:javascript
复制
// entry/src/main/module.json5
{
  "module": {
    "deviceTypes": [
      "default",
      "tablet",
      "2in1"  // ⚠️ 必须添加!
    ]
  }
}

💡 最佳实践

1. 动画类型选择
代码语言:javascript
复制
// 1. 属性动画(PropertyAnimation)
PropertyAnimation {
    target: myItem
    property: "x"
    from: 0
    to: 100
    duration: 1000
}

// 2. 数字动画(NumberAnimation)
NumberAnimation on opacity {
    from: 0.0
    to: 1.0
    duration: 1000
}

// 3. 顺序动画(SequentialAnimation)
SequentialAnimation {
    NumberAnimation { from: 0; to: 100; duration: 500 }
    NumberAnimation { from: 100; to: 0; duration: 500 }
}

// 4. 并行动画(ParallelAnimation)
ParallelAnimation {
    NumberAnimation { target: item; property: "x"; to: 100 }
    NumberAnimation { target: item; property: "y"; to: 100 }
}
2. 缓动函数选择
代码语言:javascript
复制
// 线性缓动(匀速)
easing.type: Easing.Linear

// 二次缓动(加速/减速)
easing.type: Easing.InOutQuad

// 弹性缓动(弹性效果)
easing.type: Easing.OutElastic

// 回弹缓动(回弹效果)
easing.type: Easing.OutBounce
3. 动画控制
代码语言:javascript
复制
// 启动动画
animation.start()

// 停止动画
animation.stop()

// 暂停动画
animation.pause()

// 恢复动画
animation.resume()

// 重新启动动画
animation.restart()
4. 条件运行
代码语言:javascript
复制
// 使用 running 属性控制运行
SequentialAnimation on alpha {
    running: currentDemoIndex === 0  // 条件运行
    loops: Animation.Infinite
    // ...
}

// 使用 visible 属性控制显示
Rectangle {
    visible: currentDemoIndex === 0
    // ...
}
5. 动画生命周期管理
代码语言:javascript
复制
Rectangle {
    id: demo
  
    // 显示时启动动画
    onVisibleChanged: {
        if (visible) {
            animation.start()
        } else {
            animation.stop()
        }
    }
  
    // 组件完成时初始化
    Component.onCompleted: {
        // 初始化代码
    }
}
6. 性能优化
代码语言:javascript
复制
// ✅ 正确:使用合适的持续时间
NumberAnimation {
    duration: 1000  // 1秒
}

// ✅ 正确:避免同时运行过多动画
// 只运行当前显示的动画

// ✅ 正确:使用硬件加速
// Qt Quick 默认使用硬件加速

📊 项目结构

代码语言:javascript
复制
AnimationDemo/
├── AppScope/
│   └── app.json5              # 应用配置
├── entry/
│   ├── build-profile.json5    # 构建配置
│   ├── src/
│   │   ├── main/
│   │   │   ├── cpp/
│   │   │   │   ├── main.cpp   # C++ 入口(qtmain)
│   │   │   │   ├── main.qml   # QML UI(6种动画演示)
│   │   │   │   ├── qml.qrc    # 资源文件
│   │   │   │   └── CMakeLists.txt
│   │   │   ├── module.json5   # ⚠️ 必须包含 "2in1"
│   │   │   └── ets/           # ArkTS 代码
│   │   └── ohosTest/
│   └── libs/                   # Qt 库文件
├── image/
│   └── 演示示例.gif           # 演示动图
└── build-profile.json5        # 根构建配置

🔧 构建配置要点

CMakeLists.txt
代码语言:javascript
复制
cmake_minimum_required(VERSION 3.5.0)
project(QtForHOSample)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

list(APPEND CMAKE_FIND_ROOT_PATH ${QT_PREFIX})
include_directories(${NATIVERENDER_ROOT_PATH}
                    ${NATIVERENDER_ROOT_PATH}/include)

find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core Widgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS 
    Concurrent Gui Network Qml Quick QuickControls2 
    Widgets QuickTemplates2 QmlWorkerScript)

add_library(entry SHARED main.cpp qml.qrc)

target_link_libraries(entry PRIVATE
    Qt${QT_VERSION_MAJOR}::Concurrent
    Qt${QT_VERSION_MAJOR}::Core
    Qt${QT_VERSION_MAJOR}::Gui
    Qt${QT_VERSION_MAJOR}::Network
    Qt${QT_VERSION_MAJOR}::Qml
    Qt${QT_VERSION_MAJOR}::Quick
    Qt${QT_VERSION_MAJOR}::Widgets
    Qt${QT_VERSION_MAJOR}::QuickControls2
    Qt${QT_VERSION_MAJOR}::QuickTemplates2
    Qt${QT_VERSION_MAJOR}::QmlWorkerScript
    Qt${QT_VERSION_MAJOR}::QOpenHarmonyPlatformIntegrationPlugin  # HarmonyOS 插件
)
module.json5(关键配置)
代码语言:javascript
复制
{
  "module": {
    "name": "entry",
    "type": "entry",
    "deviceTypes": [
      "default",
      "tablet",
      "2in1"  // ⚠️ 必须添加!否则打包会失败
    ],
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceType": [
      "default",
      "tablet",
      "2in1"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:icon",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:icon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      }
    ]
  }
}

📚 参考资源

Qt 官方文档
HarmonyOS 开发者社区

🎉 总结

通过本项目的开发实践,我们总结了以下关键要点:

  1. 必须使用 qtmain() 作为入口函数,而不是 main()
  2. OpenGL ES 配置必须在创建应用之前完成
  3. deviceTypes 必须包含 "2in1",否则打包会失败
  4. 使用 SequentialAnimation 实现顺序动画,适合循环效果
  5. 使用 NumberAnimation 实现属性动画,支持手动控制
  6. 使用 running 属性控制动画运行,实现条件运行
  7. 使用 onVisibleChanged 处理动画生命周期,确保状态正确
  8. 选择合适的缓动函数,提升动画效果
  9. 处理窗口状态变化,确保动画在窗口状态改变后正常工作
  10. 使用 ComboBox 实现动画切换,提升用户体验
  11. 避免同时运行过多动画,优化性能
  12. 使用合适的动画持续时间,平衡效果和性能

这些经验对于在 HarmonyOS 平台上开发 Qt 应用至关重要,特别是涉及动画效果的场景。希望本文档能帮助开发者避免常见陷阱,快速上手 Qt for HarmonyOS 开发,并创建出流畅美观的动画效果。


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 📋 项目概述
    • ✨ 主要功能
  • 🛠️ 技术栈
  • 🏗️ 项目架构
    • 目录结构
    • 组件层次结构
  • 📝 核心功能实现
    • 1. HarmonyOS 入口函数:qtmain()
    • 2. OpenGL ES 表面格式配置
    • 3. 呼吸灯动画实现
    • 4. 渐变效果动画实现
    • 5. 移动动画实现
    • 6. 旋转动画实现
    • 7. 缩放动画实现
    • 8. 文字动画实现
    • 9. ComboBox 动画选择器
    • 10. 窗口状态处理
    • 11. ⚠️ 关键配置:deviceTypes 必须包含 “2in1”
  • 🐛 常见问题与解决方案
    • 问题 1:动画不运行
    • 问题 2:动画切换时状态不正确
    • 问题 3:动画性能问题
    • 问题 4:窗口最大化后动画停止
    • 问题 5:动画无法停止
    • 问题 6:打包失败 - json-path 错误
  • 💡 最佳实践
    • 1. 动画类型选择
    • 2. 缓动函数选择
    • 3. 动画控制
    • 4. 条件运行
    • 5. 动画生命周期管理
    • 6. 性能优化
  • 📊 项目结构
  • 🔧 构建配置要点
    • CMakeLists.txt
    • module.json5(关键配置)
  • 📚 参考资源
    • Qt 官方文档
    • HarmonyOS 开发者社区
  • 🎉 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档