首页 > 解决方案 > QML 中的多色渐变

问题描述

我开始学习 Qt 框架,我想为窗口创建一个多色渐变(如下图)背景:

多色渐变图像示例

我浏览了有关 QML 类型的官方文档Linear/Conical/Radial/Shape Gradient,也浏览了 Google。但看不到任何实现这一目标的选择。

有没有其他方法可以使它成为可能?最好在 QML 中。

编辑1:

如评论中所述,GradientStop可用于多种颜色。但它不能用来产生给定图像中的结果。

标签: qtqmlqt5gradient

解决方案


At first familiarize with Qt example quick\scenegraph\fboitem. It demonstrates creating custom QML control with C++/OpenGL backend.

Also you can implement it with ShaderEffect QML control. I recommend this site https://www.shadertoy.com/new for debugging your shaders. Also you can use, for example, Canvas for passing arrays to your shader as texture (ShaderEffect doesn't allow passing arrays normally, https://bugreports.qt.io/browse/QTBUG-50493).

Here is possible implementation of your control with dynamic points count, colors and positions.

import QtQuick 2.12
import QtQuick.Controls 2.5

ApplicationWindow {
    id: window
    visible: true
    width: 640
    height: 480
    title: "Custom gradient"

    Item {
        anchors.fill: parent

        layer.enabled: true
        layer.effect: ShaderEffect {
            readonly property var dataTex: dataCanvas
            readonly property int count: dataCanvas.colors.length

            fragmentShader: "
                  varying highp vec2 qt_TexCoord0;
                  uniform lowp float qt_Opacity;
                  uniform sampler2D dataTex;
                  const uniform lowp int count;
                  void main() {
                      vec3 col = 0.0;

                      for (int i = 0; i < count; i++) {
                          vec4 color = texture2D(dataTex, vec2(i / (float)count + 0.01, 0.0));
                          vec2 point = texture2D(dataTex, vec2(i / (float)count + 0.01, 1.0)).rg;

                          float dist = distance(qt_TexCoord0, point);
                          col += color * (1.0 - dist);
                      }

                      gl_FragColor = vec4(col, 1.0);
                  }
        ";
        }

        // Because ShaderEffect doesn't allow passing arrays to shader, we will
        // convert arrays to graphical representation and pass them as texture.
        Canvas {
            id: dataCanvas
            readonly property var colors: ["cyan", "red", "green", "darkblue", "yellow"]
            readonly property var positions: [Qt.point(0,0),
                                              Qt.point(10, parent.height - 20),
                                              Qt.point(parent.width / 2, parent.height / 4),
                                              Qt.point(parent.width, 0),
                                              Qt.point(parent.width, parent.height)]

            height: 2
            width: colors.length
            antialiasing: false
            visible: false

            onPaint: {
                if (colors.length !== positions.length) {
                    console.error("Array size of 'colors' doesn't equal array size of 'positions'");
                    return;
                }

                var ctx = getContext("2d");
                ctx.reset();
                ctx.lineWidth = 1;

                for (var i = 0; i < colors.length; i++) {
                    ctx.beginPath();
                    ctx.strokeStyle = colors[i];
                    ctx.moveTo(i, 0);
                    ctx.lineTo(i+1, 0);
                    ctx.stroke();
                    ctx.closePath();

                    ctx.beginPath();
                    ctx.strokeStyle = Qt.rgba(positions[i].x / parent.width, positions[i].y / parent.height, 0, 1);
                    ctx.moveTo(i, 1);
                    ctx.lineTo(i+1, 1);
                    ctx.stroke();
                    ctx.closePath();
                }
            }
        }
    }
}

That is how it looks enter image description here

I'm not OpenGL guru, so this code isn't perfect. I'll be glad if someone will edit this answer to make it better.


推荐阅读