Qt Quick - 在组件状态更改时动画图像转换的最佳方法是啥?

Posted

技术标签:

【中文标题】Qt Quick - 在组件状态更改时动画图像转换的最佳方法是啥?【英文标题】:Qt Quick - What is the best way to animate an image transition while component state changes?Qt Quick - 在组件状态更改时动画图像转换的最佳方法是什么? 【发布时间】:2020-01-09 15:22:47 【问题描述】:

使用 Qt Quick,我创建了一个如下所示的自定义按钮:

这个按钮还包含几个状态(默认、悬停、按下),并且每个状态之间的转换是动画的。我找到了一种使用 Qt Quick 引擎轻松为每个属性设置动画的方法,图像除外。事实上,我想通过在状态之间执行交叉淡入淡出来为图像过渡设置动画。

但是我找不到这样的图像动画师。我发现的唯一方法是向我的按钮添加 3 个图像,每个状态一个,并为它们各自的不透明度设置动画。

下面是我的按钮代码:

Button

    property bool hoveredBtn: false
    property bool pressedBtn: false

    id: btAnimStateDemo
    height: 40
    anchors.right: parent.right
    anchors.rightMargin: 5
    anchors.left: parent.left
    anchors.leftMargin: 5
    anchors.top: parent.top
    anchors.topMargin: 290
    state: "DEFAULT"

    // the button background
    background: Rectangle
    
        id: btAnimStateDemoBg
        anchors.fill: parent
    

    // the button text
    Text
    
        id: btAnimStateDemoText
        text: qsTr("A button showing animated states (default, hovered, pressed)")
        renderType: Text.NativeRendering
        font.bold: true
        horizontalAlignment: Text.AlignHCenter
        verticalAlignment: Text.AlignVCenter
        anchors.fill: parent
    

    Image
    
        id: btAnimStateDemoDefaultImage
        width: 30
        height: 30
        anchors.left: parent.left
        anchors.leftMargin: 5
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 5
        anchors.top: parent.top
        anchors.topMargin: 5
        opacity: 1.0
        source: "Resources/Palette.svg"
    

    Image
    
        id: btAnimStateDemoHoverImage
        width: 30
        height: 30
        anchors.left: parent.left
        anchors.leftMargin: 5
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 5
        anchors.top: parent.top
        anchors.topMargin: 5
        opacity: 0.0
        source: "Resources/Smile.svg"
    

    Image
    
        id: btAnimStateDemoPressedImage
        width: 30
        height: 30
        anchors.left: parent.left
        anchors.leftMargin: 5
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 5
        anchors.top: parent.top
        anchors.topMargin: 5
        opacity: 0.0
        source: "Resources/Woman.svg"
    

    // the component state array
    states:
    [
        State
        
            name: "DEFAULT"
            PropertyChanges  target: btAnimStateDemoBg; color: "green"
            PropertyChanges  target: btAnimStateDemoBg; radius: 4
            PropertyChanges  target: btAnimStateDemoDefaultImage; opacity: 1.0
            PropertyChanges  target: btAnimStateDemoHoverImage; opacity: 0.0
            PropertyChanges  target: btAnimStateDemoPressedImage; opacity: 0.0
        ,
        State
        
            name: "HOVERED"
            PropertyChanges  target: btAnimStateDemoBg; color: "red"
            PropertyChanges  target: btAnimStateDemoBg; radius: 10
            PropertyChanges  target: btAnimStateDemoDefaultImage; opacity: 0.0
            PropertyChanges  target: btAnimStateDemoHoverImage; opacity: 1.0
            PropertyChanges  target: btAnimStateDemoPressedImage; opacity: 0.0
        ,
        State
        
            name: "PRESSED"
            PropertyChanges  target: btAnimStateDemoBg; color: "blue"
            PropertyChanges  target: btAnimStateDemoBg; radius: 15
            PropertyChanges  target: btAnimStateDemoDefaultImage; opacity: 0.0
            PropertyChanges  target: btAnimStateDemoHoverImage; opacity: 0.0
            PropertyChanges  target: btAnimStateDemoPressedImage; opacity: 1.0
        
    ]

    // the matching transitions between states
    transitions:
    [
        Transition
        
            from: "*"; to: "DEFAULT"
            ColorAnimation  property: "color"; easing.type: Easing.Linear; duration: 1000 
            NumberAnimation  properties: "radius, opacity"; easing.type: Easing.Linear; duration: 1000 
        ,
        Transition
        
            from: "*"; to: "HOVERED"
            ColorAnimation  property: "color"; easing.type: Easing.Linear; duration: 1000 
            NumberAnimation  properties: "radius, opacity"; easing.type: Easing.Linear; duration: 1000 
        ,
        Transition
        
            from: "*"; to: "PRESSED"
            ColorAnimation  property: "color"; easing.type: Easing.Linear; duration: 1000 
            NumberAnimation  properties: "radius, opacity"; easing.type: Easing.Linear; duration: 1000 
        
    ]

    // the mouse area which will apply the correct state in relation to the current mouse status
    MouseArea
    
        anchors.fill: parent
        hoverEnabled: true

        onEntered: btAnimStateDemo.state = "HOVERED"; btAnimStateDemo.hoveredBtn = true;
        onExited: btAnimStateDemo.state = btAnimStateDemo.pressedBtn ? "PRESSED" : "DEFAULT"; btAnimStateDemo.hoveredBtn = false;
        onPressed: btAnimStateDemo.state = "PRESSED"; btAnimStateDemo.pressedBtn = true;
        onReleased: btAnimStateDemo.state = btAnimStateDemo.hoveredBtn ? "HOVERED" : "DEFAULT"; btAnimStateDemo.pressedBtn = false;
    

上面的代码运行良好,达到了我计划的目的,但在我看来有点复杂。如果图像中存在像NumberAnimationColorAnimation 这样的动画师,那就太好了,但我没有找到。

所以我的问题是:有没有比我上面提出的解决方案更简单的方法来动画组件状态之间的图像转换? Qt Quick 是否会提议一个动画师来达到这样的目的?

【问题讨论】:

我认为这不存在,所以也许您应该向 Qt 提交功能请求? (也许先等待一些答案;-)) 所以按照您的建议,我在 Qt 错误跟踪器上打开了一个线程:bugreports.qt.io/browse/QTBUG-81292 【参考方案1】:

创建一个 SpriteSheet,为三个帧中的每一个分配状态,然后将 interpolate 属性设置为 true 以使其在图像之间转换。

使用 SpriteSequence 的 Sprite 动画 SpriteSequence 演示了使用精灵序列来绘制动画和交互式熊。 SpriteSequence 对象定义了五个不同的精灵。熊最初处于静止状态:

      Sprite
          name: "still"
          source: "content/BearSheet.png"
          frameCount: 1
          frameWidth: 256
          frameHeight: 256
          frameDuration: 100
          to: "still":1, "blink":0.1, "floating":0
      

当点击场景时,动画将精灵序列设置为下降状态并为熊的 y 属性设置动画。

  SequentialAnimation 
      id: anim
      ScriptAction  script: image.goalSprite = "falling"; 
      NumberAnimation  target: image; property: "y"; to: 480; duration: 12000; 
      ScriptAction  script: image.goalSprite = ""; image.jumpTo("still"); 
      PropertyAction  target: image; property: "y"; value: 0 
  

在动画结束时,熊被设置回初始状态。

【讨论】:

以上是关于Qt Quick - 在组件状态更改时动画图像转换的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

使用Qt Quick 设计器 十五

Qt Quick 和 Widgets 的对比

Qt Quick:创建转换时的代码冗余

布局中的 Qt 状态机转换

在mat-sidenav组件的'closing'事件中更改状态时,角度动画不会触发?

Qt基于Qml图像帧动画播放