You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QtQuick3D物体顺逆时针顺序旋转动画问题求助

QtQuick3D实现物体交替顺逆时针旋转的解决方法

为啥你的代码只执行最后一次旋转?

你用的for循环是同步执行的,JS会瞬间跑完所有6次迭代,angle变量直接被最后一次赋值(i=5时为-360)覆盖;同时每次调用start.restart()都会立刻终止当前运行的动画,最终就只剩最后一次设置的旋转效果。

两种可行的解决办法

办法1:用SequentialAnimation串联所有动画

直接创建一个顺序动画,把6次旋转的动画按顺序加入,动画会自动依次执行:

import QtQuick 2.15
import QtQuick.Window 2.14
import QtQuick3D 1.15
import QtQuick.Controls 2.14

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    visibility: "Maximized"
    property int scl: 5

    Node{
        id: standAloneScene
        DirectionalLight {
            ambientColor: Qt.rgba(1.0, 1.0, 1.0, 1.0)
        }
        Node {
            id: sphere
            Model {
                id: model_sphere
                source: "#Cube"
                x: 200
                y: 100
                z: 0
                materials: [
                    DefaultMaterial {
                        diffuseColor: Qt.rgba(0.053, 0.130, 0.219, 0.75)
                    }
                ]
            }
        }

        // 定义可复用的单次旋转动画组件
        Component {
            id: rotateAnimComponent
            ParallelAnimation {
                property int angleParam: 0
                NumberAnimation {
                    target: sphere
                    property: "eulerRotation.y"
                    from: sphere.eulerRotation.y
                    to: sphere.eulerRotation.y + angleParam
                    duration: 2000
                    easing.type: Easing.InOutQuad
                }
                NumberAnimation {
                    target: model_sphere
                    property: "eulerRotation.y"
                    from: model_sphere.eulerRotation.y
                    to: model_sphere.eulerRotation.y + 2*angleParam
                    duration: 2000
                    easing.type: Easing.InOutQuad
                }
            }
        }

        // 顺序动画,按顺-逆-顺的顺序排列6次旋转
        SequentialAnimation {
            id: rotateSequence
            rotateAnimComponent.createObject(standAloneScene, {angleParam: 360})
            rotateAnimComponent.createObject(standAloneScene, {angleParam: -360})
            rotateAnimComponent.createObject(standAloneScene, {angleParam: 360})
            rotateAnimComponent.createObject(standAloneScene, {angleParam: -360})
            rotateAnimComponent.createObject(standAloneScene, {angleParam: 360})
            rotateAnimComponent.createObject(standAloneScene, {angleParam: -360})
        }

        OrthographicCamera {
            id: cameraOrthographicFront
            eulerRotation.y: 45
            eulerRotation.x: -45
            x: 600
            y: 800
            z: 600
        }
    }
    Rectangle {
        id: view
        anchors.top: parent.top
        anchors.left: parent.left
        width: parent.width
        height: parent.height
        color: "#848895"
        border.color: "black"

        View3D {
            id: topLeftView
            anchors.fill: parent
            importScene: standAloneScene
            camera: cameraOrthographicFront
        }
        Button {
            id: posmoveZ
            width: view.width/8
            height: view.height/16
            anchors.top: view.top
            anchors.right: view.right
            text: "start"
            font.pixelSize: height
            onClicked: {
                // 每次点击重置旋转角度,从头开始
                sphere.eulerRotation.y = 0
                model_sphere.eulerRotation.y = 0
                rotateSequence.start()
            }
        }
    }
}

办法2:利用动画结束信号递归触发下一次旋转

这种方式更灵活,修改循环次数只需改变量,还能方便添加暂停/中断逻辑:

import QtQuick 2.15
import QtQuick.Window 2.14
import QtQuick3D 1.15
import QtQuick.Controls 2.14

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    visibility: "Maximized"
    property int scl: 5
    property int currentRotation: 0
    property int totalRotations: 6

    Node{
        id: standAloneScene
        DirectionalLight {
            ambientColor: Qt.rgba(1.0, 1.0, 1.0, 1.0)
        }
        Node {
            id: sphere
            Model {
                id: model_sphere
                source: "#Cube"
                x: 200
                y: 100
                z: 0
                materials: [
                    DefaultMaterial {
                        diffuseColor: Qt.rgba(0.053, 0.130, 0.219, 0.75)
                    }
                ]
            }
        }

        ParallelAnimation{
            id: start
            running: false
            // 动画结束后触发下一次旋转
            onFinished: {
                currentRotation++
                if(currentRotation < totalRotations){
                    // 根据当前次数判断旋转方向
                    const angle = currentRotation % 2 === 0 ? 360 : -360
                    // 更新动画目标值,基于当前角度累加
                    sphereAnim.to = sphere.eulerRotation.y + angle
                    modelAnim.to = model_sphere.eulerRotation.y + 2*angle
                    start.start()
                } else {
                    currentRotation = 0 // 重置计数,方便下次点击
                }
            }

            NumberAnimation {
                id: sphereAnim
                target: sphere
                property: "eulerRotation.y"
                duration: 2000
                easing.type: Easing.InOutQuad
            }
            NumberAnimation {
                id: modelAnim
                target: model_sphere
                property: "eulerRotation.y"
                duration: 2000
                easing.type: Easing.InOutQuad
            }
        }

        OrthographicCamera {
            id: cameraOrthographicFront
            eulerRotation.y: 45
            eulerRotation.x: -45
            x: 600
            y: 800
            z: 600
        }
    }
    Rectangle {
        id: view
        anchors.top: parent.top
        anchors.left: parent.left
        width: parent.width
        height: parent.height
        color: "#848895"
        border.color: "black"

        View3D {
            id: topLeftView
            anchors.fill: parent
            importScene: standAloneScene
            camera: cameraOrthographicFront
        }
        Button {
            id: posmoveZ
            width: view.width/8
            height: view.height/16
            anchors.top: view.top
            anchors.right: view.right
            text: "start"
            font.pixelSize: height
            onClicked: {
                // 重置状态,从头开始旋转
                currentRotation = 0
                sphere.eulerRotation.y = 0
                model_sphere.eulerRotation.y = 0
                // 设置第一次旋转的目标值
                sphereAnim.to = 360
                modelAnim.to = 720
                start.start()
            }
        }
    }
}

关键注意点

  • 别用同步循环修改动画参数:JS循环执行速度极快,动画还没开始就被参数覆盖和中断了。
  • 旋转要基于当前角度累加:如果直接设置固定的to值,多次旋转后会回到初始角度,无法实现持续交替旋转。
  • 方案选择:固定旋转次数用顺序动画更直观,需要动态调整次数或添加控制逻辑选递归触发的方式。

内容的提问来源于stack exchange,提问作者Vladimir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 11:55:24