纯 Qml 实现仿画图3D的颜色选择器(更强更易用)

发布于:2022-11-16 ⋅ 阅读:(701) ⋅ 点赞:(0)

【写在前面】

        在做编辑相关的应用经常会用到颜色选择器。

        实际上, Qt Widgets 时代使用 QColorDialog 或者 Qml ColorDialog 即可完成一般的颜色选择的需求。

        然鹅现在都 Windows 11了!!还在用那么拉跨的东西,实在是看不下去了。

        然后自己一直很喜欢用 Windows 画图3D的那个颜色选择器,因此我决定仿照实现一个,并且还为它增加了一个透明度。


 【正文开始】

       不多bb,先直接看效果图,喜欢的话接着往下看~

       首先,需要实现一个渐变的面板,这里用了一点小小的技巧。

       底层用 白色=>当前颜色的渐变(左到右),上层用纯透明=>纯不透明黑色的渐变(上到下),然后他们混合起来就是我们一般见到的渐变面板。

Rectangle {
    x: pickerRect.cursorWidth * 0.5
    y: pickerRect.height - pickerRect.cursorWidth * 0.5
    width: pickerRect.height - pickerRect.cursorWidth
    height: pickerRect.width - pickerRect.cursorWidth
    rotation: -90
    transformOrigin: Item.TopLeft
    gradient: Gradient {
        GradientStop { position: 0.0; color: "white" }
        GradientStop { position: 1.0; color: pickerRect.hueColor }
    }
}

Rectangle {
    x: pickerRect.cursorWidth * 0.5
    y: pickerRect.cursorWidth * 0.5
    width: pickerRect.width - pickerRect.cursorWidth
    height: pickerRect.height - pickerRect.cursorWidth
    gradient: Gradient {
        GradientStop { position: 1.0; color: "#ff000000" }
        GradientStop { position: 0.0; color: "#00000000" }
    }
}

        接着,现在要实现一个 hue 的颜色选择条带:

Rectangle {
    id: huePicker
    width: pickerRect.width - pickerRect.cursorWidth
    height: 32
    anchors.top: pickerRect.bottom
    anchors.topMargin: 10
    anchors.left: contentText.left
    gradient: Gradient {
        orientation: Gradient.Horizontal
        GradientStop { position: 0.0;  color: "#ff0000" }
        GradientStop { position: 0.16; color: "#ffff00" }
        GradientStop { position: 0.33; color: "#00ff00" }
        GradientStop { position: 0.5;  color: "#00ffff" }
        GradientStop { position: 0.76; color: "#0000ff" }
        GradientStop { position: 0.85; color: "#ff00ff" }
        GradientStop { position: 1.0;  color: "#ff0000" }
    }

    Rectangle {
        id: hueSlider
        width: height
        height: parent.height
        anchors.verticalCenter: parent.verticalCenter
        border.color: "#e6e6e6"
        border.width: 2
        scale: 0.9
        color: pickerRect.hueColor

        property real value: x / (parent.width - width)

        Behavior on scale { NumberAnimation { easing.type: Easing.OutBack; duration: 300 } }

        Rectangle {
            anchors.fill: parent
            anchors.margins: 1
            color: "transparent"
            border.color: "white"
            border.width: 2
        }
    }

    MouseArea {
        anchors.fill: parent

        function handleCursorPos(x) {
            let halfWidth = hueSlider.width * 0.5;
            hueSlider.x = Math.max(0, Math.min(width, x + halfWidth) - hueSlider.width);
        }

        onPressed: (mouse) => {
            hueSlider.scale = 0.6;
            handleCursorPos(mouse.x);
        }
        onReleased: hueSlider.scale = 0.9;
        onPositionChanged: (mouse) => handleCursorPos(mouse.x);
    }
}

       渐变条带比较简单,精确控制好渐变位置即可,然后增加了一个选择的方块,用于判断和选择位置。

       接着我们要增加一个透明度选择条带,这里我们先生成一些黑白相间的格子作为底层,然后在上面放上透明=>不透明的条带即可:

Item {
    id: alphaPickerItem
    width: huePicker.width
    height: huePicker.height
    anchors.top: huePicker.bottom
    anchors.topMargin: 25
    anchors.left: huePicker.left

    Grid {
        id: alphaPicker
        anchors.fill: parent
        rows: 4
        columns: 29
        clip: true

        property real cellWidth: width / columns
        property real cellHeight: height / rows

        Repeater {
            model: parent.columns * parent.rows

            Rectangle {
                width: alphaPicker.cellWidth
                height: width
                color: (index % 2 == 0) ? "gray" : "transparent"
            }
        }
    }

    Rectangle {
        anchors.fill: parent
        gradient: Gradient {
            orientation: Gradient.Horizontal
            GradientStop { position: 1.0; color: "#ff000000" }
            GradientStop { position: 0.0; color: "#00ffffff" }
        }
    }

    Rectangle {
        id: alphaSlider
        x: parent.width - width
        width: height
        height: parent.height
        anchors.verticalCenter: parent.verticalCenter
        color: Qt.rgba(0.1, 0.1, 0.1, (value + 1.0) / 2.0)
        border.color: "#e6e6e6"
        border.width: 2
        scale: 0.9

        property real value: x / (parent.width - width)

        Behavior on scale { NumberAnimation { easing.type: Easing.OutBack; duration: 300 } }

        Rectangle {
            anchors.fill: parent
            anchors.margins: 1
            color: "transparent"
            border.color: "white"
            border.width: 1
        }
    }

    MouseArea {
        anchors.fill: parent

        function handleCursorPos(x) {
            let halfWidth = alphaSlider.width * 0.5;
            alphaSlider.x = Math.max(0, Math.min(width, x + halfWidth) - alphaSlider.width);
        }

        onPressed: (mouse) => {
            alphaSlider.scale = 0.6;
            handleCursorPos(mouse.x);
        }
        onReleased: alphaSlider.scale = 0.9;
        onPositionChanged: (mouse) => handleCursorPos(mouse.x);
    }
}

       和渐变条带一致,这里也放置了一个选择框。

       到这里,比较重要的部分就讲完了,不过我要提示一些 Qml 的小技巧:

       Qml color 类型有一些很好用的属性,例如:hsvHue、hsvSaturation、hsvValue、hslHue、 hslSaturation、 hslLightness,这些属性在实现颜色相关控件时非常有用。

       最后还有一些其他零碎的代码,例如编辑框,过渡动画等等就不再赘述了,自行下载源码编辑测试即可。


 【结语】

       一开始我实现的 ColorPicker 只是一个 Rectangle 中,然鹅很多时候也需要弹窗形式的,所以又增加了一个 ColorPickerPopup ,直接下载源码就可以获得了(多多star呀..⭐_⭐):。

​​​​​​​       Github的:​​​​​​​GitHub - mengps/QmlControls: Qt / Qml 控件Qt / Qml 控件. Contribute to mengps/QmlControls development by creating an account on GitHub.https://github.com/mengps/QmlControls​​​​​​​       CSDN的:https://download.csdn.net/download/u011283226/86995876https://download.csdn.net/download/u011283226/86995876

本文含有隐藏内容,请 开通VIP 后查看