Pregunta

I need sequentially to load one by one components from array. Before loading next component to Loader it should be fade-out , loaded and then fade-in.

The code above blinks not correctly and show this message: "QML State: Binding loop detected for property "when"

What have I done wrong?

Thank you

import QtQuick 2.0

Rectangle {
    id: screen
    width: 400
    height: 400
    color: "black"

    Rectangle {
        id: view
        anchors.fill: parent

        Loader {
            id: loader
            onLoaded: { view.state="fadeIn"; }
        }

        states: [
            State {
                name: "fadeOut";
                PropertyChanges { target: view; opacity: 0.1; }
            },
            State {
                name: "fadeIn";
                PropertyChanges { target: view; opacity: 1; }
            },
            State {
                name: "load"; when: view.opacity == 0;
                StateChangeScript { script: { loader.sourceComponent=com_array[index]; } }
            }
        ]

        transitions: [
            Transition {
                to: "fadeIn"
                NumberAnimation { properties: "opacity"; from: 0.0; to: 0.99; duration: 2000 }
            },
            Transition {
                to: "fadeOut"
                NumberAnimation { properties: "opacity"; from: 0.99; to: 0; duration: 2000 }
            }
        ]
    }

    Timer {
        id: timer

        interval: 3000; running: true; repeat: true
        onTriggered:  {
            ++index;
            if( index >= com_array.length ) {
                index = 0
            }

            view.state="fadeOut"
        }
    }

    // -------------------------------------------------------------------
    // Components

    Item {
        id: list

        property Component red_rect_com : Component {
            Rectangle {
                width: screen.width; height: screen.height

                Rectangle {
                    anchors.fill: parent; color: "red";
                }
            }
        }

        property Component blue_rect_com : Component {
            Rectangle {
                width: screen.width; height: screen.height

                Rectangle {
                    anchors.fill: parent; color: "blue";
                }
            }
        }
    }

    property int index: 0
    property var com_array: [ list.red_rect_com, list.blue_rect_com ]
    Component.onCompleted: { loader.sourceComponent = com_array[index]; }
}

UPD

Possible this could be useful for other, full example with correction ( thanks to answer author ):

import QtQuick 2.0

Rectangle {
    id: screen
    width: 400
    height: 400
    color: "black"

    Rectangle {
        id: view
        anchors.fill: parent
        property var sourceComponent
        opacity: 0

        function loadComponent( component, is_first_start ) {
            sourceComponent = component;

            if( is_first_start ) {
                fadeOut.stop(); fadeIn.start();
            }
            else {
                fadeIn.stop(); fadeOut.start();
            }
        }

        Loader {
            id: loader
            onLoaded: {
                fadeOut.stop();
                fadeIn.start();
            }
        }

        NumberAnimation on opacity {
            id: fadeOut;
            to: 0.0;
            duration: 2000;
            onStopped: { loader.sourceComponent=view.sourceComponent; }
        }

        NumberAnimation on opacity {
            id: fadeIn;
            to: 1.0;
            duration: 2000
        }
    }

    Timer {
        id: timer

        interval: 4000; running: true; repeat: true
        onTriggered: {
            ++index;
            if( index >= com_array.length ) {
                index = 0
            }

            view.loadComponent( com_array[index], false );
        }
    }

    // -------------------------------------------------------------------
    // Components

    Item {
        id: list

        property Component red_rect_com : Component {
            Rectangle {
                width: screen.width; height: screen.height

                Rectangle {
                    anchors.fill: parent; color: "red";
                }
            }
        }

        property Component blue_rect_com : Component {
            Rectangle {
                width: screen.width; height: screen.height

                Rectangle {
                    anchors.fill: parent; color: "blue";
                }
            }
        }
    }

    property int index: 0
    property var com_array: [ list.red_rect_com, list.blue_rect_com ]
    Component.onCompleted: { view.loadComponent( com_array[index], true ); }
}
¿Fue útil?

Solución

It gives you the error because QML make connections to communicate between changes:

onStateChange is connected by PropertyChanges that changes opacity to a view's opacity property. The onOpacityChanged signal will be connected to State's when property. The onWhenChanged signal will be connected to the state property, thus making binding Loop.

Another thing is that when you make NumberAnimation within Transition, you do not need to specify the from and to properties. The PropertyChanges sets the correct value.

Anyway, your states does not make sense, you using them wrong. You should also wait for fadeOut, because it will load so fast that you will not see the effect. Simply make two animations:

Rectangle {
    id: view
    anchors.fill: parent
    property var sourceComponent
    function loadComponent(component){
        fadeIn.stop(); fadeOut.start();
        sourceComponent = component;
    }
    Loader {
        id: loader
        onLoaded: { fadeOut.stop(); fadeIn.start(); }
    }
    NumberAnimation on opacity {
        id: fadeOut
        to: 0.0
        onStopped: { loader.sourceComponent= sourceComponent; }
    }
    NumberAnimation on opacity {
        id: fadeIn
        to: 1.0
    }
}

You should also change the loading to:

property int index: 0
onIndexChanged:  { view.loadComponent(com_array[index]); }

But there is better solution: using ShaderEffectSource to take a frame of Loader before changing the source, and using it to fadeOut meanwhile loading the component.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top