Mit*_*tch 6 qt button qml qt-quick qtquickcontrols
我想用Qt Quick创建下面的光泽按钮(最好是纯QML,没有C++):

它需要是可扩展的,所以我不能使用PNG等.
我的代码到目前为止:
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
}
}
}
}
Run Code Online (Sandbox Code Playgroud)

我有两个问题:
Mit*_*tch 21
使用Rectangle无法做到这一点.但是,您可以使用Canvas.我将引导您完成整个过程.
由于有几个"层",我们必须创建一个Item来包含它们.我们将根据它们的Z顺序添加图层,从平面颜色开始:
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
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Canvas项应该填充按钮,所以我们写anchors.fill: parent.
然后我们获得用于在画布上绘制的2D上下文.我们还调用reset(),在每次绘画之前清除画布.
该按钮具有圆角,因此我们定义只读radius属性并将其设置为我们所需的值,在这种情况下,该值是按钮高度的20%.
接下来,我们打电话beginPath().这将启动一个新路径,并关闭所有以前的路径.
我们将笔划的线宽设置为按钮高度的10%.
Canvas 内部使用QPainter.QPainter在目标内部冲击50%,在外部冲击50%.绘制圆角矩形时必须考虑到这一点,否则笔划将隐藏在画布外部.我们可以通过绘制边距等于线宽一半的矩形来实现.
在定义了圆角矩形路径之后,我们留下了一个我们需要描边和填充的路径.
这一步的结果是:

由于我们希望文本在按钮的光泽下,我们必须在下一步定义它:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,label样式的组件设置为null.这是因为我们不希望文本高于其他所有内容.如果ButtonStyle有一个foreground组件,则没有必要.相反,我们将Label项添加为子项background.
此代码的可视结果与上一步骤相同.
画布可以绘制线性,径向和圆锥形渐变.我们将使用线性渐变在我们的按钮上绘制"光泽"效果:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
我们绘制与步骤#1相同的圆角矩形,除了这次,我们用从上到下的透明渐变填充它.

看起来不错,但还不是很好.闪光效果在按钮的中间停止,为了实现Canvas的效果,我们需要在绘制渐变矩形之前进行一些修剪.您可以将Canvas视为与Photoshop中的"减法" 矩形选框工具类似,除非使用您定义的任何形状.
如果我们很幸运并且光泽的曲线是凹的,我们可以在绘制渐变矩形之前添加以下行:
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();
Run Code Online (Sandbox Code Playgroud)

相反,我们将使用bezierCurveTo()手动绘制曲线.
确定要传递给的值bezierCurveTo()并不容易,这就是为什么我建议使用像Craig Buckler的CanvasBézier曲线示例这样的好工具找到你想要的曲线.这将让你操纵曲线,直到你找到你想要的东西,但最重要的是,它会给你创建这些曲线的代码.如果你想做相反的事情,并编辑代码以实时查看曲线,请查看HTML5 Canvas Bezier曲线教程.
下面,我做了一个小例子来描绘剪切路径,以便更容易可视化:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)

红色区域的倒数是我们将在其中绘制光泽的区域.
所以,剪辑的代码如下:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)

按钮现在看起来是部件,可以单击,但它没有任何鼠标交互的可视指示.我们也加上这个.
它只需要两行代码即可实现.第一行使光泽画布部分透明:
opacity: !button.pressed ? 1 : 0.75
Run Code Online (Sandbox Code Playgroud)
第二个增加按钮悬停时文本的亮度:
color: button.hovered && !button.pressed ? "white" : "#ddd"
Run Code Online (Sandbox Code Playgroud)
您可以更进一步,将样式分离到自己的QML文件中,提供颜色属性,并方便地允许不同的彩色按钮.