Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Dynamically assign Behavior animation to dynamically created components via JavaScript
QtWS25 Last Chance

Dynamically assign Behavior animation to dynamically created components via JavaScript

Scheduled Pinned Locked Moved QML and Qt Quick
7 Posts 2 Posters 4.7k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Q Offline
    Q Offline
    Quteroid
    wrote on last edited by Quteroid
    #1

    In QtQuick 2 (Qt 5.x), is it possible to assign animation Behaviors dynamically to properties of dynamically created QtQuick components during run time via JavaScript? If yes: how?

    Rationale: I want to support multiple different animation types (scrolling, fading, scaling, etc.) for components and let the user of the app configure which one to use. So the selection of properties to animate (x, y, width, height, scale, rotation, opacity, etc.) has to be applied dynamically at run time rather than statically predefined in QML. Since I don't use predefined states - about everything is to be dynamic here - Transition-type animations are not an option either.

    If I were using QML with a predefined property to animate I would statically code something like:

    MyComponent {
        width: 100; height: 200
        Behavior on width { NumberAnimation { duration: 1000 } } }
    

    However, I mostly do without static QML but instantiate my components dynamically using JavaScript with Qt.createQmlObject() or Qt.createComponent() instead, like this:

    var comp = Qt.createQmlObject ('import QtQuick 2.3; Rectangle { }', root);
        comp.width  = 100;  // I actually use variables as values
        comp.height = 200;
    

    Then, how can I assign a Behavior animation to this dynamically created comp, e.g. to its width property, so that a NumberAnimation of duration 1000 applies to it?

    // ??? <-- script code to assign Behavior animation for width property of comp
        comp.width += 200; // This change of width should be animated
    

    I have been experimenting with approaches like this but they don't seem to work:

    var behavior = Qt.createQmlObject ('import QtQuick 2.3; Behavior { }', root);
    var anim = Qt.createQmlObject ('import QtQuick 2.3; NumberAnimation { }', root);
        anim.target = comp;
        anim.duration = 1000;
        anim.property = "width";
        behavior.animation = anim;
        myGlobals.behavior = behavior; // Prevent from being garbage collected
        comp.width += 200; // Should be animated but changes instantly
    

    Am I missing something or is this approach completely wrong?

    p3c0P 1 Reply Last reply
    0
    • Q Quteroid

      In QtQuick 2 (Qt 5.x), is it possible to assign animation Behaviors dynamically to properties of dynamically created QtQuick components during run time via JavaScript? If yes: how?

      Rationale: I want to support multiple different animation types (scrolling, fading, scaling, etc.) for components and let the user of the app configure which one to use. So the selection of properties to animate (x, y, width, height, scale, rotation, opacity, etc.) has to be applied dynamically at run time rather than statically predefined in QML. Since I don't use predefined states - about everything is to be dynamic here - Transition-type animations are not an option either.

      If I were using QML with a predefined property to animate I would statically code something like:

      MyComponent {
          width: 100; height: 200
          Behavior on width { NumberAnimation { duration: 1000 } } }
      

      However, I mostly do without static QML but instantiate my components dynamically using JavaScript with Qt.createQmlObject() or Qt.createComponent() instead, like this:

      var comp = Qt.createQmlObject ('import QtQuick 2.3; Rectangle { }', root);
          comp.width  = 100;  // I actually use variables as values
          comp.height = 200;
      

      Then, how can I assign a Behavior animation to this dynamically created comp, e.g. to its width property, so that a NumberAnimation of duration 1000 applies to it?

      // ??? <-- script code to assign Behavior animation for width property of comp
          comp.width += 200; // This change of width should be animated
      

      I have been experimenting with approaches like this but they don't seem to work:

      var behavior = Qt.createQmlObject ('import QtQuick 2.3; Behavior { }', root);
      var anim = Qt.createQmlObject ('import QtQuick 2.3; NumberAnimation { }', root);
          anim.target = comp;
          anim.duration = 1000;
          anim.property = "width";
          behavior.animation = anim;
          myGlobals.behavior = behavior; // Prevent from being garbage collected
          comp.width += 200; // Should be animated but changes instantly
      

      Am I missing something or is this approach completely wrong?

      p3c0P Offline
      p3c0P Offline
      p3c0
      Moderators
      wrote on last edited by p3c0
      #2

      Hi @Quteroid,
      If I understood you correctly I think you are close. Instead of creating two separate components create single component for Rectangle, Behavior and NumberAnimation. Try the following code. I Hope this is what you were trying :)

      import QtQuick 2.4
      
      Rectangle {
          id: root
          width: 300
          height: 300
      
          MouseArea {
              anchors.fill: parent
              onClicked: {
                  var prop = "width"
                  var rect = Qt.createQmlObject ('import QtQuick 2.4; Rectangle { color: "red" \n Behavior on '+prop+' { NumberAnimation { duration: 1000 } } }', root);
                  rect.width = 300
                  rect.height = 300
              }
          }
      }
      
      

      To animate other properties, assign that property as a string to the prop variable.

      157

      Q 1 Reply Last reply
      0
      • p3c0P p3c0

        Hi @Quteroid,
        If I understood you correctly I think you are close. Instead of creating two separate components create single component for Rectangle, Behavior and NumberAnimation. Try the following code. I Hope this is what you were trying :)

        import QtQuick 2.4
        
        Rectangle {
            id: root
            width: 300
            height: 300
        
            MouseArea {
                anchors.fill: parent
                onClicked: {
                    var prop = "width"
                    var rect = Qt.createQmlObject ('import QtQuick 2.4; Rectangle { color: "red" \n Behavior on '+prop+' { NumberAnimation { duration: 1000 } } }', root);
                    rect.width = 300
                    rect.height = 300
                }
            }
        }
        
        

        To animate other properties, assign that property as a string to the prop variable.

        Q Offline
        Q Offline
        Quteroid
        wrote on last edited by
        #3

        @p3c0 said:

        Hi @Quteroid,
        If I understood you correctly I think you are close. Instead of creating two separate components create single component for Rectangle, Behavior and NumberAnimation. Try the following code. I Hope this is what you were trying :)

        It is close and if I won't find a better way this is probably what I'll have to stick with. However, it falls short of what I'm trying to achieve in three aspects:

        1.: depending on the type of animation selected by the user, the number of Behaviors + Animations may vary. For instance, moving component from top left to middle would require both x and y. Additionally animating width and height (for a shift-in effect) and opacity could also be an option I'd like to implement. So the number of Behaviors + Animations should be dynamic as well.

        2.: type of animation may change during existence of a component. For instance, it may fade in during creation but be moved away to bottom right during disposal. What type of animation to apply for disposal may not be known during (or change after) creation of the component. So the animation behavior should be applied / changeable without (re)creating the component.

        3.: as I'm working on a highly dynamic GUI concept for my app (you guessed it?), I try to make as little use of static QML as possible and use JavaScript (for the time being, later hopefully mostly C++) to build the GUI. So I'd like to figure how to recreate the "Behavior on" construct of QML in code. First in JavaScript, later perhaps in C++.

        Perhaps I should rephrase (or extend) my question to: is it possible to recreate / emulate the "Behavior on" syntax and animation assignment in JavaScript (or C++) on a per QmlObject basis?

        I've also tried changing parents for Qt.createQmlObject() calls from root to various combinations of comp, anim and behavior, but to no avail.

        I also noticed that PropertiesAnimation has property "properties", to animate multiple properties within one animation instance. If possible, I'd like to utilize this optimization as well (via code). This seems not possible with "Behavior on" syntax in QML, as it requires one Behavior + Animation per property?

        p3c0P Q 2 Replies Last reply
        0
        • Q Quteroid

          @p3c0 said:

          Hi @Quteroid,
          If I understood you correctly I think you are close. Instead of creating two separate components create single component for Rectangle, Behavior and NumberAnimation. Try the following code. I Hope this is what you were trying :)

          It is close and if I won't find a better way this is probably what I'll have to stick with. However, it falls short of what I'm trying to achieve in three aspects:

          1.: depending on the type of animation selected by the user, the number of Behaviors + Animations may vary. For instance, moving component from top left to middle would require both x and y. Additionally animating width and height (for a shift-in effect) and opacity could also be an option I'd like to implement. So the number of Behaviors + Animations should be dynamic as well.

          2.: type of animation may change during existence of a component. For instance, it may fade in during creation but be moved away to bottom right during disposal. What type of animation to apply for disposal may not be known during (or change after) creation of the component. So the animation behavior should be applied / changeable without (re)creating the component.

          3.: as I'm working on a highly dynamic GUI concept for my app (you guessed it?), I try to make as little use of static QML as possible and use JavaScript (for the time being, later hopefully mostly C++) to build the GUI. So I'd like to figure how to recreate the "Behavior on" construct of QML in code. First in JavaScript, later perhaps in C++.

          Perhaps I should rephrase (or extend) my question to: is it possible to recreate / emulate the "Behavior on" syntax and animation assignment in JavaScript (or C++) on a per QmlObject basis?

          I've also tried changing parents for Qt.createQmlObject() calls from root to various combinations of comp, anim and behavior, but to no avail.

          I also noticed that PropertiesAnimation has property "properties", to animate multiple properties within one animation instance. If possible, I'd like to utilize this optimization as well (via code). This seems not possible with "Behavior on" syntax in QML, as it requires one Behavior + Animation per property?

          p3c0P Offline
          p3c0P Offline
          p3c0
          Moderators
          wrote on last edited by
          #4

          @Quteroid

          is it possible to recreate / emulate the "Behavior on" syntax and animation assignment in JavaScript (or C++) on a per QmlObject basis?

          It has to be on per QML Object basis. AFAIK you can't load just Behavior or NumberAnimation using createQmlObject as they are not Item's.

          I also noticed that PropertiesAnimation has property "properties", to animate multiple properties within one animation instance. If possible, I'd like to utilize this optimization as well (via code).

          yes multiple properties are possible but as said earlier cant just inject them into existing QML.

          157

          1 Reply Last reply
          0
          • Q Quteroid

            @p3c0 said:

            Hi @Quteroid,
            If I understood you correctly I think you are close. Instead of creating two separate components create single component for Rectangle, Behavior and NumberAnimation. Try the following code. I Hope this is what you were trying :)

            It is close and if I won't find a better way this is probably what I'll have to stick with. However, it falls short of what I'm trying to achieve in three aspects:

            1.: depending on the type of animation selected by the user, the number of Behaviors + Animations may vary. For instance, moving component from top left to middle would require both x and y. Additionally animating width and height (for a shift-in effect) and opacity could also be an option I'd like to implement. So the number of Behaviors + Animations should be dynamic as well.

            2.: type of animation may change during existence of a component. For instance, it may fade in during creation but be moved away to bottom right during disposal. What type of animation to apply for disposal may not be known during (or change after) creation of the component. So the animation behavior should be applied / changeable without (re)creating the component.

            3.: as I'm working on a highly dynamic GUI concept for my app (you guessed it?), I try to make as little use of static QML as possible and use JavaScript (for the time being, later hopefully mostly C++) to build the GUI. So I'd like to figure how to recreate the "Behavior on" construct of QML in code. First in JavaScript, later perhaps in C++.

            Perhaps I should rephrase (or extend) my question to: is it possible to recreate / emulate the "Behavior on" syntax and animation assignment in JavaScript (or C++) on a per QmlObject basis?

            I've also tried changing parents for Qt.createQmlObject() calls from root to various combinations of comp, anim and behavior, but to no avail.

            I also noticed that PropertiesAnimation has property "properties", to animate multiple properties within one animation instance. If possible, I'd like to utilize this optimization as well (via code). This seems not possible with "Behavior on" syntax in QML, as it requires one Behavior + Animation per property?

            Q Offline
            Q Offline
            Quteroid
            wrote on last edited by
            #5

            Meanwhile I have found a way to achieve at least what I wanted to achieve, although not quite how I originally intended it. Reminder: my goal is to assign animations fully dynamically to any number of properties of a component, and possibly change these assignments during lifetime of the object.

            I have found no way to create operational Behavior objects in JavaScript. Furthermore, when attaching dynamically created Animation objects to Behavior objects and trying to trigger them manually (via start(), restart() or running = true) the error "setRunning() cannot be used on non-root animation nodes" occurs.

            However, just creating Animation objects and attaching them to target objects and their properties without Behaviors at all permits at least functional animations, created in a fully dynamic way. Something like this works:

            function testAnim (parent) {
                var comp = Qt.createQmlObject ('import QtQuick 2.3; Rectangle { }', parent);
                var anim = Qt.createQmlObject ('import QtQuick 2.3; PropertyAnimation { }', parent);
                    comp.width = 100;
                    comp.height = 100;
                    anim.target = comp;
                    anim.property = "width";
                    anim.from = comp.width;
                    anim.to = 200;
                    anim.duration = 5000;
                    anim.restart ();
            }
            

            Downside of this approach is that I cannot simply assign comp.width = 200 to get it animated, as would be the case with working Behaviors. Instead I'll have to create a little framework module (I name it AnimMonitor) that keeps track of what properties of what components are attached to animations. It's sort of an own implementation of Behavior. Fortunately, this is not too complex and consists mainly of four methods:

            // Register new animated property for a component
            AnimMonitor.register (targetComponent, componentProperty, animObject) {...}
            // Always use this method for all property accesses to all objects from now on.
            // If property is not animated, set it directly. Else reprogram attached Anim object.
            AnimMonitor.setProperty (targetComponent, componentProperty, newValue) {...}
            // Unregister particular property animation from existing component
            AnimMonitor.unregister (targetComponent, componentProperty) {...}
            // Unregister all animations of a component; must be called before destruction
            AnimMonitor.unregisterComponent (targetComponent) {...}
            

            So I'll always have to use the wrapper method setProperty() from now on. This and the fact that animations cannot get auto-garbage-collected on component destruction but must be explicitly unregistered seems a bit cumbersome but I think this is the price to pay for this fully dynamic approach, which QtQuick apparently is not prepared to support directly (as yet). Or is it?

            p3c0P 1 Reply Last reply
            0
            • Q Quteroid

              Meanwhile I have found a way to achieve at least what I wanted to achieve, although not quite how I originally intended it. Reminder: my goal is to assign animations fully dynamically to any number of properties of a component, and possibly change these assignments during lifetime of the object.

              I have found no way to create operational Behavior objects in JavaScript. Furthermore, when attaching dynamically created Animation objects to Behavior objects and trying to trigger them manually (via start(), restart() or running = true) the error "setRunning() cannot be used on non-root animation nodes" occurs.

              However, just creating Animation objects and attaching them to target objects and their properties without Behaviors at all permits at least functional animations, created in a fully dynamic way. Something like this works:

              function testAnim (parent) {
                  var comp = Qt.createQmlObject ('import QtQuick 2.3; Rectangle { }', parent);
                  var anim = Qt.createQmlObject ('import QtQuick 2.3; PropertyAnimation { }', parent);
                      comp.width = 100;
                      comp.height = 100;
                      anim.target = comp;
                      anim.property = "width";
                      anim.from = comp.width;
                      anim.to = 200;
                      anim.duration = 5000;
                      anim.restart ();
              }
              

              Downside of this approach is that I cannot simply assign comp.width = 200 to get it animated, as would be the case with working Behaviors. Instead I'll have to create a little framework module (I name it AnimMonitor) that keeps track of what properties of what components are attached to animations. It's sort of an own implementation of Behavior. Fortunately, this is not too complex and consists mainly of four methods:

              // Register new animated property for a component
              AnimMonitor.register (targetComponent, componentProperty, animObject) {...}
              // Always use this method for all property accesses to all objects from now on.
              // If property is not animated, set it directly. Else reprogram attached Anim object.
              AnimMonitor.setProperty (targetComponent, componentProperty, newValue) {...}
              // Unregister particular property animation from existing component
              AnimMonitor.unregister (targetComponent, componentProperty) {...}
              // Unregister all animations of a component; must be called before destruction
              AnimMonitor.unregisterComponent (targetComponent) {...}
              

              So I'll always have to use the wrapper method setProperty() from now on. This and the fact that animations cannot get auto-garbage-collected on component destruction but must be explicitly unregistered seems a bit cumbersome but I think this is the price to pay for this fully dynamic approach, which QtQuick apparently is not prepared to support directly (as yet). Or is it?

              p3c0P Offline
              p3c0P Offline
              p3c0
              Moderators
              wrote on last edited by
              #6

              @Quteroid

              Downside of this approach is that I cannot simply assign comp.width = 200 to get it animated, as would be the case with working Behaviors

              Property bindings from JavaScripts can be created too.

              property bool animationState: false
              
              function testAnim (parent) {
                  var comp = Qt.createQmlObject ('import QtQuick 2.3; Rectangle { }', parent);
                  var anim = Qt.createQmlObject ('import QtQuick 2.3; PropertyAnimation { }', parent);
                      comp.width = 100;
                      comp.height = 100;
                      anim.target = comp;
                      anim.property = "width";
                      anim.from = comp.width;
                      anim.to = 200;
                      anim.duration = 5000;
                      anim.running = Qt.binding(function() { return animationState })
              }
              

              and then when required set animationState to true.

              157

              Q 1 Reply Last reply
              0
              • p3c0P p3c0

                @Quteroid

                Downside of this approach is that I cannot simply assign comp.width = 200 to get it animated, as would be the case with working Behaviors

                Property bindings from JavaScripts can be created too.

                property bool animationState: false
                
                function testAnim (parent) {
                    var comp = Qt.createQmlObject ('import QtQuick 2.3; Rectangle { }', parent);
                    var anim = Qt.createQmlObject ('import QtQuick 2.3; PropertyAnimation { }', parent);
                        comp.width = 100;
                        comp.height = 100;
                        anim.target = comp;
                        anim.property = "width";
                        anim.from = comp.width;
                        anim.to = 200;
                        anim.duration = 5000;
                        anim.running = Qt.binding(function() { return animationState })
                }
                

                and then when required set animationState to true.

                Q Offline
                Q Offline
                Quteroid
                wrote on last edited by
                #7

                @p3c0 said:

                Property bindings from JavaScripts can be created too.

                property bool animationState: false
                
                function testAnim (parent) {
                    var comp = Qt.createQmlObject ('import QtQuick 2.3; Rectangle { }', parent);
                    var anim = Qt.createQmlObject ('import QtQuick 2.3; PropertyAnimation { }', parent);
                        comp.width = 100;
                        comp.height = 100;
                        anim.target = comp;
                        anim.property = "width";
                        anim.from = comp.width;
                        anim.to = 200;
                        anim.duration = 5000;
                        anim.running = Qt.binding(function() { return animationState })
                }
                

                and then when required set animationState to true.

                No, property bindings are not of any help here. They are just used to synchronize values of two properties, i.e. provide another sort of alias mechanism. Your example just provides an alternate way to state "anim.running = true" by stating "animationState = true". It doesn't provide new functionality but just consumes a bit more of memory and, in this example, pollutes the (global) namespace.

                Besides: the statement "property bool animationState: false" is static QML and not JavaScript. However, as mentioned earlier, except for the root element I work essentially without static QML but prefer to create objects and values dynamically in code. Also regard that I may have n Animation objects (dynamically) instantiated. They may be triggered independently. So one global state property to control them all is not appropriate.

                Property bindings have other uses. For instance, I think one could synchronize animations for several components (or properties) with just one Animation object. For instance, if comp is the original component and comp2 is another one in reach, then "comp2.width = Qt.binding (function () { return comp.width; })" would synchronously animate the widths of both comp and comp2 with just one Animation instance. This without QML properties or namespace pollutions ;-)

                Thanks for your thoughts on the matter, nevertheless.

                1 Reply Last reply
                0

                • Login

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • Users
                • Groups
                • Search
                • Get Qt Extensions
                • Unsolved