qtqmlqt-quick

How can I dynamically generate GradientStops in QML Gradient


I want to dynamically generate or remove GradientStops, and I also want to dynamically change it's colors using a NumberAnimation. I have a NumberAnimation to change a variety "idxx" to form a animation like below:

NumberAnimation {
    target: thiz
    property: "idxx"
    duration: settings.cycleTime ?? 500
    from: settings.cycleColorFrom ?? 0
    to: settings.cycleColorTo ?? 15 }

by using a function to change colors:

function getColor(index){
    var hueIndex = (15 - (((idxx + index) > 15) ? idxx - 15 + index : idxx + index));
    var hue = (hueIndex * cycleEnd / 255) + (cycleStart / 255);
    return Qt.hsva(hue, cycleSaturation, cycleValue, cycleOpacity); }

It works well when I use Gradient like:

Gradient {
    id: gradientRainbow
    GradientStop { position: 0.000; color: getColor(15) }
    GradientStop { position: 0.067; color: getColor(14) }
    GradientStop { position: 0.133; color: getColor(13) }
    GradientStop { position: 0.200; color: getColor(12) }
    GradientStop { position: 0.267; color: getColor(11) }
    GradientStop { position: 0.333; color: getColor(10) }
    GradientStop { position: 0.400; color: getColor(9) }
    GradientStop { position: 0.467; color: getColor(8) }
    GradientStop { position: 0.533; color: getColor(7) }
    GradientStop { position: 0.600; color: getColor(6) }
    GradientStop { position: 0.667; color: getColor(5) }
    GradientStop { position: 0.733; color: getColor(4) }
    GradientStop { position: 0.800; color: getColor(3) }
    GradientStop { position: 0.867; color: getColor(2) }
    GradientStop { position: 0.933; color: getColor(1) }
    GradientStop { position: 1.000; color: getColor(0) }
}

I tried to use:

property list<GradientStop> gsl: [GradientStop { position: 0.000; color: getColor(15) },             
                                    GradientStop { position: 0.067; color: getColor(14) },
                                    GradientStop { position: 0.133; color: getColor(13) },
                                    GradientStop { position: 0.200; color: getColor(12) },
                                    GradientStop { position: 0.267; color: getColor(11) },
                                    GradientStop { position: 0.333; color: getColor(10) },
                                    GradientStop { position: 0.400; color: getColor(9) },
                                    GradientStop { position: 0.467; color: getColor(8) },
                                    GradientStop { position: 0.533; color: getColor(7) },
                                    GradientStop { position: 0.600; color: getColor(6) },
                                    GradientStop { position: 0.667; color: getColor(5) },
                                    GradientStop { position: 0.733; color: getColor(4) },
                                    GradientStop { position: 0.800; color: getColor(3) },
                                    GradientStop { position: 0.867; color: getColor(2) },
                                    GradientStop { position: 0.933; color: getColor(1) },
                                    GradientStop { position: 1.000; color: getColor(0) }]
Gradient {
    id: gradientRainbow
    stops: gsl
}

It dosen't change colors when the NumberAnimation is running and I can't dynamically add or remove GradientStops in gsl which will lead to Unexpected token ]', Expected token {' or Expected a qualified name id. When I use (How to dynamically add stops in a Gradient):

Gradient {
    id: gradientRainbow
    Component.onCompleted: {
        var stops = [];
        for (var i = 15; i >= 0; i--) {
            var position = (15 - i) / 15.0;
            stops.push(gradientStopComponent.createObject(base, { "position": position, "color": getColor(i)}));
        }
        gradientRainbow.stops = stops;
    }
}
Component{
    id: gradientStopComponent
    GradientStop {}
}

It also will not change the colors when the NumberAnimation running


Solution

  • The following is a generalization of the Gradient example in https://doc.qt.io/qt-6/qml-qtquick-gradient.html and it illustrates how to dynamically create a list<GradientStop> array by using array.map on an existing array to make iterative calls to gradientStopComponent.createObject.

    property int index: 0
    property var stopdefs1: [{ position: 0.0, color: "red" }, { position: 0.33, color: "yellow" }, { position: 1.00, color: "green" } ]
    property var stopdefs2: [{ position: 0.0, color: "green" }, { position: 0.66, color: "yellow" }, { position: 1.00, color: "red" } ] 
    
    Rectangle {
        width: 100; height: 100
        gradient: makeGradient(index == 0 ? stopdefs1 : stopdefs2)
        TapHandler { onTapped: index = 1 - index }
    } 
    
    function makeGradient(stopdefs) { return gradientComponent.createObject(null, {stopdefs}); }
    
    Component {
        id: gradientComponent
        Gradient {
            property var stopdefs
            stops: stopdefs.map( d => gradientStopComponent.createObject(null, d) );
        }
    }
    
    Component { id: gradientStopComponent; GradientStop { } }
    

    You can Try it Online!