使用Qt Quick创建可缩放的光泽/光泽按钮 [英] Creating a scalable, glossy/shiny button with Qt Quick

查看:167
本文介绍了使用Qt Quick创建可缩放的光泽/光泽按钮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用Qt Quick(最好使用纯QML,没有C ++)在下面创建光泽按钮:

I'd like to create the glossy button below with Qt Quick (preferably with pure QML, no C++):

它必须具有可伸缩性,所以我不能使用PNG等.

It needs to be scalable, so I can't use PNGs, etc.

到目前为止,我的代码:

My code so far:

import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2

ApplicationWindow {
    id: window
    color: "#cccccc"
    width: 200
    height: 200

    Button {
        id: button
        width: Math.min(window.width, window.height) - 20
        height: width * 0.3
        anchors.centerIn: parent
        text: "Button"

        style: ButtonStyle {
            background: Rectangle {
                gradient: Gradient {
                    GradientStop {
                        position: 0
                        color: "#bbffffff"
                    }
                    GradientStop {
                        position: 0.6
                        color: "#00c0f5"
                    }
                }

                border.color: "grey"
                border.width: height * 0.05
                radius: height / 5
            }

            label: Label {
                text: button.text
                color: "#ddd"
                font.pixelSize: button.height * 0.5
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter
            }
        }
    }
}

我有两个问题:

  1. 我不知道如何创建弯曲的光泽效果.
  2. 我需要使文本显得有些刺眼,但目前它位于文本上方.

推荐答案

使用Rectangle不可能做到这一点.但是,您可以使用画布.我将引导您完成整个过程.

This is not possible using Rectangle. You can use Canvas, however. I'll walk you through the process.

由于存在多个层",因此我们必须创建一个项目来包含所有这些层".我们将根据其Z顺序(从纯色开始)添加图层:

Since there are several "layers", we must create an Item to contain them all. We'll add the layers according to their Z order, starting with the flat colours:

import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2

ApplicationWindow {
    id: window
    color: "#cccccc"
    width: 200
    height: 200

    Button {
        id: button
        width: Math.min(window.width, window.height) - 20
        height: width * 0.3
        anchors.centerIn: parent
        text: "Button"

        readonly property real radius: height / 5

        style: ButtonStyle {
            background: Item {
                Canvas {
                    anchors.fill: parent

                    onPaint: {
                        var ctx = getContext("2d");
                        ctx.reset();

                        ctx.beginPath();
                        ctx.lineWidth = height * 0.1;
                        ctx.roundedRect(ctx.lineWidth / 2, ctx.lineWidth / 2,
                            width - ctx.lineWidth, height - ctx.lineWidth, button.radius, button.radius);
                        ctx.strokeStyle = "grey";
                        ctx.stroke();
                        ctx.fillStyle = "#00c0f5";
                        ctx.fill();
                    }
                }
            }

            label: Label {
                text: button.text
                color: "white"
                font.pixelSize: button.height * 0.5
                horizontalAlignment: Text.AlignHCenter
                verticalAlignment: Text.AlignVCenter
            }
        }
    }
}

画布"项应填充按钮,因此我们写为"anchors.fill: parent".

The Canvas item should fill the button, so we write anchors.fill: parent.

然后我们获得用于在画布上绘制的2D上下文.我们还调用reset(),它会在每次绘制之前清除画布.

We then get the 2D context that we use to draw on the canvas with. We also call reset(), which clears the canvas before each paint.

按钮具有圆角,因此我们定义了只读的radius属性并将其设置为所需的值,在这种情况下,该值是按钮高度的20%.

The button has rounded corners, so we define the read-only radius property and set it to our desired value, which in this case is 20% of the height of the button.

接下来,我们叫beginPath().这将开始一个新路径,也将关闭以前的所有路径.

Next, we call beginPath(). This starts a new path, and also closes any previous paths.

我们将笔划的线宽设置为按钮高度的10%.

We set the line width for our stroke to 10% of the height of the button.

Canvas在内部使用 QPainter . QPainter在目标内部抚摸50%,在外部抚摸50%.在绘制圆角矩形时,必须考虑到这一点,否则笔划将隐藏在画布之外.我们可以通过绘制边距等于线宽一半的矩形来做到这一点.

Canvas uses QPainter internally. QPainter strokes 50% on the inside of the target and 50% on the outside. We must account for this when drawing our rounded rectangle, otherwise the stroke will be hidden outside the canvas. We can do so by drawing the rectangle with margins equal to half the line width.

在定义了圆角矩形路径后,剩下的是需要笔触和填充的路径.

After the rounded rectangle path has been defined, we're left with a path that we need to stroke and fill.

此步骤的结果是:

由于我们希望文本位于按钮的光泽下,因此我们必须在其下进行定义:

As we want the text to be under the shine of the button, we must define it next:

import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2

ApplicationWindow {
    id: window
    color: "#cccccc"
    width: 200
    height: 200

    Button {
        id: button
        width: Math.min(window.width, window.height) - 20
        height: width * 0.3
        anchors.centerIn: parent
        text: "Button"

        readonly property real radius: height / 5

        style: ButtonStyle {
            background: Item {
                Canvas {
                    anchors.fill: parent

                    onPaint: {
                        var ctx = getContext("2d");
                        ctx.reset();

                        ctx.beginPath();
                        ctx.lineWidth = height * 0.1;
                        ctx.roundedRect(ctx.lineWidth / 2, ctx.lineWidth / 2,
                            width - ctx.lineWidth, height - ctx.lineWidth, button.radius, button.radius);
                        ctx.strokeStyle = "grey";
                        ctx.stroke();
                        ctx.fillStyle = "#00c0f5";
                        ctx.fill();
                    }
                }

                Label {
                    text: button.text
                    color: "white"
                    font.pixelSize: button.height * 0.5
                    anchors.centerIn: parent
                }
            }

            label: null
        }
    }
}

请注意,样式的label组件设置为null.这是因为我们不希望文本位于其他所有内容之上.如果ButtonStyle具有foreground组件,则没有必要.相反,我们将Label项添加为background的子项.

Notice that the label component of the style is set to null. This is because we don't want the text to be above everything else. If ButtonStyle had a foreground component, this wouldn't be necessary. Instead, we add the Label item as a child of background.

此代码的视觉结果与上一步相同.

The visual result of this code is identical to the previous step.

画布可以绘制线性径向圆锥形渐变.我们将使用线性渐变来绘制光泽"图.对我们按钮的影响:

Canvas can draw linear, radial and conical gradients. We'll use a linear gradient to draw the "shine" effect on our button:

import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2

ApplicationWindow {
    id: window
    color: "#cccccc"
    width: 200
    height: 200

    Button {
        id: button
        width: Math.min(window.width, window.height) - 20
        height: width * 0.3
        anchors.centerIn: parent
        text: "Button"

        readonly property real radius: height / 5

        style: ButtonStyle {
            background: Item {
                Canvas {
                    anchors.fill: parent

                    onPaint: {
                        var ctx = getContext("2d");
                        ctx.reset();

                        ctx.beginPath();
                        ctx.lineWidth = height * 0.1;
                        ctx.roundedRect(ctx.lineWidth / 2, ctx.lineWidth / 2,
                            width - ctx.lineWidth, height - ctx.lineWidth, button.radius, button.radius);
                        ctx.strokeStyle = "grey";
                        ctx.stroke();
                        ctx.fillStyle = "#00c0f5";
                        ctx.fill();
                    }
                }

                Label {
                    text: button.text
                    color: "white"
                    font.pixelSize: button.height * 0.5
                    anchors.centerIn: parent
                }

                Canvas {
                    anchors.fill: parent
                    onPaint: {
                        var ctx = getContext("2d");
                        ctx.reset();

                        ctx.beginPath();
                        ctx.lineWidth = height * 0.1;
                        ctx.roundedRect(ctx.lineWidth / 2, ctx.lineWidth / 2,
                            width - ctx.lineWidth, height - ctx.lineWidth, button.radius, button.radius);
                        ctx.moveTo(0, height * 0.4);
                        ctx.bezierCurveTo(width * 0.25, height * 0.6, width * 0.75, height * 0.6, width, height * 0.4);
                        ctx.lineTo(width, height);
                        ctx.lineTo(0, height);
                        ctx.lineTo(0, height * 0.4);
                        ctx.clip();

                        ctx.beginPath();
                        ctx.roundedRect(ctx.lineWidth / 2, ctx.lineWidth / 2,
                            width - ctx.lineWidth, height - ctx.lineWidth,
                            button.radius, button.radius);
                        var gradient = ctx.createLinearGradient(0, 0, 0, height);
                        gradient.addColorStop(0, "#bbffffff");
                        gradient.addColorStop(0.6, "#00ffffff");
                        ctx.fillStyle = gradient;
                        ctx.fill();
                    }
                }
            }

            label: null
        }
    }
}

我们绘制与步骤1中相同的圆角矩形,除了这次,我们从上到下用透明渐变填充它.

We draw the same rounded rectangle as in step #1, except this time, we fill it with a transparent gradient from top to bottom.

看起来不错,但还没到那儿.发光效果会在按钮的一半处停止,为了使用Canvas实现此效果,我们需要在绘制渐变矩形之前进行一些裁剪.您可以认为使用Canvas进行剪裁类似于减法"剪裁.在Photoshop中矩形选框工具,除非使用您定义的任何形状

Looking good, but not quite there yet. The shine effect stops halfway down the button, and in order to achieve that with Canvas, we need to do some clipping before we draw the gradient rectangle. You can think of clipping with Canvas as similar to the "subtractive" Rectangular Marquee Tool in Photoshop, except using any shape that you define.

如果幸运的话,闪耀的曲线是凹形的,我们可以在绘制渐变矩形之前简单地添加以下几行:

If we were lucky and the shine's curve was concave, we could simply add the following lines before we draw the gradient rectangle:

ctx.beginPath();
ctx.roundedRect(ctx.lineWidth / 2, ctx.lineWidth / 2, width - ctx.lineWidth, height - ctx.lineWidth,
    button.radius, button.radius);
ctx.moveTo(0, height / 2);
ctx.ellipse(-width / 2, height / 2, width * 2, height);
ctx.clip();

相反,我们将使用确定要传递给bezierCurveTo()的值并不容易,这就是为什么我建议使用Craig Buckler的 HTML5画布贝塞尔曲线教程.

Determining the values to pass to bezierCurveTo() is not easy, which is why I'd suggest finding the curve you want with a great tool like Craig Buckler's Canvas Bézier Curve Example. This will let you manipulate the curves until you find what you're after, but best of all, it will give you the code that creates those curves. If you'd like to do the opposite, and edit the code to see the curves in realtime, check out the HTML5 Canvas Bezier Curve Tutorial.

下面,我举了一个小例子,它画出了剪切路径,使可视化变得更容易:

Below, I've made a small example that strokes the clipping path to make it easier to visualise:

import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2

ApplicationWindow {
    id: window
    color: "#cccccc"
    width: 200
    height: 200

    Button {
        id: button
        width: Math.min(window.width, window.height) - 20
        height: width * 0.3
        anchors.centerIn: parent
        text: "Button"

        readonly property real radius: height / 5

        style: ButtonStyle {
            background: Item {
                Rectangle {
                    anchors.fill: parent
                    color: "transparent"
                    border.color: "black"
                    opacity: 0.25
                }

                Canvas {
                    anchors.fill: parent
                    onPaint: {
                        var ctx = getContext("2d");
                        ctx.reset();

                        var cornerRadius = height / 5;

                        ctx.beginPath();
                        ctx.moveTo(0, height * 0.4);
                        ctx.bezierCurveTo(width * 0.25, height * 0.6, width * 0.75, height * 0.6, width, height * 0.4);
                        ctx.lineTo(width, height);
                        ctx.lineTo(0, height);
                        ctx.lineTo(0, height * 0.4);
                        ctx.strokeStyle = "red";
                        ctx.stroke();
                    }
                }
            }

            label: null
        }
    }
}

红色区域的反面是我们将在其中绘制光泽的区域.

The inverse of the red area is the area that we will be drawing the shine within.

因此,进行裁剪的代码如下:

So, the code to do the clipping is as follows:

import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2

ApplicationWindow {
    id: window
    color: "#cccccc"
    width: 200
    height: 200

    Button {
        id: button
        width: Math.min(window.width, window.height) - 20
        height: width * 0.3
        anchors.centerIn: parent
        text: "Button"

        readonly property real radius: height / 5

        style: ButtonStyle {
            background: Item {
                Canvas {
                    anchors.fill: parent

                    onPaint: {
                        var ctx = getContext("2d");
                        ctx.reset();

                        ctx.beginPath();
                        ctx.lineWidth = height * 0.1;
                        ctx.roundedRect(ctx.lineWidth / 2, ctx.lineWidth / 2,
                            width - ctx.lineWidth, height - ctx.lineWidth, button.radius, button.radius);
                        ctx.strokeStyle = "grey";
                        ctx.stroke();
                        ctx.fillStyle = "#00c0f5";
                        ctx.fill();
                    }
                }

                Label {
                    text: button.text
                    color: "#ddd"
                    font.pixelSize: button.height * 0.5
                    anchors.centerIn: parent
                }

                Canvas {
                    anchors.fill: parent
                    onPaint: {
                        var ctx = getContext("2d");
                        ctx.reset();

                        ctx.beginPath();
                        ctx.lineWidth = height * 0.1;
                        ctx.roundedRect(ctx.lineWidth / 2, ctx.lineWidth / 2,
                            width - ctx.lineWidth, height - ctx.lineWidth, button.radius, button.radius);
                        ctx.moveTo(0, height * 0.4);
                        ctx.bezierCurveTo(width * 0.25, height * 0.6, width * 0.75, height * 0.6, width, height * 0.4);
                        ctx.lineTo(width, height);
                        ctx.lineTo(0, height);
                        ctx.lineTo(0, height * 0.4);
                        ctx.clip();

                        ctx.beginPath();
                        ctx.roundedRect(ctx.lineWidth / 2, ctx.lineWidth / 2,
                            width - ctx.lineWidth, height - ctx.lineWidth,
                            button.radius, button.radius);
                        var gradient = ctx.createLinearGradient(0, 0, 0, height);
                        gradient.addColorStop(0, "#bbffffff");
                        gradient.addColorStop(0.6, "#00ffffff");
                        ctx.fillStyle = gradient;
                        ctx.fill();
                    }
                }
            }

            label: null
        }
    }
}

按钮现在看起来像零件,可以单击,但是它没有任何可视化的鼠标交互指示.我们还要添加它.

The button now looks the part, and can be clicked, but it doesn't have any visual indication of mouse interaction. Let's add that as well.

只需两行代码即可实现.第一行使光泽画布部分透明:

It only takes two lines of code to achieve this. The first line makes the shine canvas partially transparent:

opacity: !button.pressed ? 1 : 0.75

第二个按钮在悬停按钮时增加文本的亮度:

The second increases the brightness of the text when the button is hovered:

color: button.hovered && !button.pressed ? "white" : "#ddd"

您甚至可以进一步将样式分离到其自己的QML文件中,提供color属性并方便地使用不同颜色的按钮.

You could take this even further and separate the style out into its own QML file, provide a colour property and conveniently allow different coloured buttons.

这篇关于使用Qt Quick创建可缩放的光泽/光泽按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆