天天看點

Qt Quick裡的圖形效果:陰影(Drop Shadow)效果源碼分析 Qt Quick裡的圖形效果——漸變(Gradient) Qt Quick裡的圖形效果——顔色(Color) Qt Quick裡的圖形效果——混合(Blend) Qt Quick裡的圖形效果(Graphical Effects) Qt Quick專欄

    Qt Quick提供了兩種陰影效果:

  • DropShow,陰影。這個元素會根據源圖像,産生一個彩色的、模糊的新圖像,把這個新圖像放在源圖像後面,給人一種源圖像從背景上凸出來的效果。
  • InnerShadow,内陰影。這個元素會根據源圖像,産生一個彩色的、模糊的新圖像,與 DropShadow不同的是,新圖像會放在源圖像裡面。

效果

    下面是我設計的示例效果。

    首先是 DropShadow :

Qt Quick裡的圖形效果:陰影(Drop Shadow)效果源碼分析 Qt Quick裡的圖形效果——漸變(Gradient) Qt Quick裡的圖形效果——顔色(Color) Qt Quick裡的圖形效果——混合(Blend) Qt Quick裡的圖形效果(Graphical Effects) Qt Quick專欄

                       圖1 陰影效果

    然後是内陰影效果:

Qt Quick裡的圖形效果:陰影(Drop Shadow)效果源碼分析 Qt Quick裡的圖形效果——漸變(Gradient) Qt Quick裡的圖形效果——顔色(Color) Qt Quick裡的圖形效果——混合(Blend) Qt Quick裡的圖形效果(Graphical Effects) Qt Quick專欄

                    圖2 内陰影效果

源碼分析

    如圖1所示,界面被分為三部分。

    最上面的是源圖像。

    源圖像下面(即中間)是一個清單,你可以點選 DropShadow 和 InnerShadow 兩個子項,切換不同的陰影效果。每種陰影效果都對應一個 qml 文檔,當你點選這些子項時,對應的 qml 文檔動态加載。

陰影示例界面

    這個示例界面架構其實與“Qt Quick裡的圖形效果——顔色(Color)”是一緻的,隻是我把 ListView 從原來的豎向改為了橫向。對應的 DropShadowExample.qml 内容如下:

import QtQuick 2.2
import QtQuick.Controls 1.2

Rectangle {
    id: example;
    signal back();
    anchors.fill: parent;

    Text {
        id: origLabel;
        x: 10;
        y: 4;
        font.pointSize: 20;
        text: "Original Image";
    }

    Button {
        anchors.right: parent.right;
        anchors.top: parent.top;
        anchors.margins: 4;
        text: "Back";
        onClicked: example.back();
    }


    Image {
        id: origImage;
        width: 240;
        height: 240;
        anchors.left: parent.left;
        anchors.top: origLabel.bottom;
        anchors.margins: 4;
        source: "butterfly.png";
        sourceSize: Qt.size(240, 240);
        smooth: true;
    }

    Rectangle{
        anchors.left: parent.left;
        anchors.leftMargin: 4;
        anchors.right: parent.right;
        anchors.rightMargin: 4;
        anchors.top: origImage.bottom;
        height: 2;
        border.width: 1;
        border.color: "darkgray";
    }

    Text {
        id: effectsLabel;
        anchors.top: origImage.bottom;
        anchors.margins: 4;
        anchors.left: parent.left;
        font.pointSize: 20;
        font.bold: true;
        text: "Shadow Effects:";
        color: "blue";
    }

    Rectangle {
        id: shadowEffects;
        anchors.left: effectsLabel.right;
        anchors.leftMargin: 4;
        anchors.top: effectsLabel.top;
        anchors.right: parent.right;
        anchors.rightMargin: 4;
        height: 40;
        color: "gray";

        ListView {
            anchors.fill: parent;
            clip: true;
            focus: true;
            orientation: ListView.Horizontal;
            spacing: 20;
            delegate: Text {
                id: wrapper;
                height: 40;
                verticalAlignment: Text.AlignVCenter;
                text: name;
                font.pointSize: 18;
                Keys.onEnterPressed: {
                    event.accepted = true;
                    effectControl.source = example;
                }

                Keys.onReturnPressed: {
                    event.accepted = true;
                    effectControl.source = example;
                }

                MouseArea {
                    anchors.fill: parent;
                    onClicked: {
                        wrapper.ListView.view.currentIndex = index;
                        effectControl.source = example;
                    }
                }
            }
            highlight: Rectangle {
                height: parent.height;
                color: "lightblue";
            }

            model: shadowsModel;
        }
    }

    Loader {
        id: effectControl;
        anchors.top: shadowEffects.bottom;
        anchors.left: parent.left;
        anchors.bottom: parent.bottom;
        anchors.right: parent.right;
        anchors.margins: 4;
        source: "DropShadowEx.qml";
    }

    ListModel {
        id: shadowsModel;
        ListElement {
            name: "DropShadow";
            example: "DropShadowEx.qml";
        }
        ListElement {
            name: "InnerShadow";
            example: "InnerShadowEx.qml";
        }
    }
}
           

    DropShawExample.qml 會被“ Qt Quick裡的圖形效果(Graphical Effects)”裡介紹過的 main.qml 動态加載。

陰影效果

    陰影效果對應的 DropShadowEx.qml 内容如下:

import QtQuick 2.2
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.2

Rectangle {
    anchors.fill: parent;
    Image {
        id: opImage;
        x: 4;
        y: 4;
        width: 250;
        height: 250;
        source: "butterfly.png";
        sourceSize: Qt.size(250, 250);
        smooth: true;
        visible: false;
    }

    DropShadow {
        id: dropshadow;
        anchors.fill: opImage;
        source: opImage;
    }

    Rectangle {
        anchors.left: opImage.right;
        anchors.top: opImage.top;
        anchors.right: parent.right;
        anchors.bottom: parent.bottom;
        anchors.margins: 2;
        color: "lightsteelblue";

        CheckBox {
            id: fast;
            anchors.top: parent.top;
            anchors.topMargin: 4;
            anchors.left: parent.left;
            anchors.leftMargin: 4;
            checked: false;
            text: "fast";
        }
        CheckBox {
            id: transparentBorder;
            anchors.left: fast.right;
            anchors.leftMargin: 8;
            anchors.top: fast.top;
            checked: false;
            text: "transparentBorder";
        }

        Text {
            id: colorLabel;
            anchors.left: fast.left;
            anchors.top: fast.bottom;
            anchors.topMargin: 8;
            text: "shadow color:";
        }

        ColorPicker {
            id: shadowColor;
            anchors.left: colorLabel.right;
            anchors.leftMargin: 4;
            anchors.top: colorLabel.top;
            width: 90;
            height: 28;
            color: "#ff000000";
        }

        Text {
            id: sampleLabel;
            anchors.left: fast.left;
            anchors.top: shadowColor.bottom;
            anchors.topMargin: 8;
            text: "samples:";
        }

        Slider {
            id: sampleSlider;
            anchors.left: sampleLabel.right;
            anchors.leftMargin: 4;
            anchors.top: sampleLabel.top;
            minimumValue: 0;
            maximumValue: 32;
            value: 0.0;
            width: 160;
            height: 30;
            stepSize: 1.0;
        }

        Text {
            id: spreadLabel;
            anchors.left: fast.left;
            anchors.top: sampleSlider.bottom;
            anchors.topMargin: 8;
            text: "spread:";
        }

        Slider {
            id: spreadSlider;
            anchors.left: spreadLabel.right;
            anchors.leftMargin: 4;
            anchors.top: spreadLabel.top;
            value: 0.5;
            width: 160;
            height: 30;
        }

        Text {
            id: radiusLabel;
            anchors.left: fast.left;
            anchors.top: spreadSlider.bottom;
            anchors.topMargin: 8;
            text: "radius:";
        }

        Rectangle {
            id: radiusArea;
            anchors.left: radiusLabel.right;
            anchors.leftMargin: 4;
            anchors.top: radiusLabel.top;
            height: 30;
            width: 160;
            color: "lightgray";
            border.width: 1;
            border.color: "darkgray";
            TextInput {
                anchors.fill: parent;
                anchors.margins: 2;
                id: radiusEdit;
                font.pointSize: 18;
                text: "0.0";
                validator: DoubleValidator{bottom: 0;}
            }
        }


        Text {
            id: voffLabel;
            anchors.left: fast.left;
            anchors.top: radiusArea.bottom;
            anchors.topMargin: 8;
            text: "verticalOffset:";
        }

        Rectangle {
            id: voffArea;
            anchors.left: voffLabel.right;
            anchors.leftMargin: 4;
            anchors.top: voffLabel.top;
            height: 30;
            width: 160;
            color: "lightgray";
            border.width: 1;
            border.color: "darkgray";
            TextInput {
                anchors.fill: parent;
                anchors.margins: 2;
                id: voffEdit;
                font.pointSize: 18;
                text: "0.0";
                validator: DoubleValidator{}
            }
        }


        Text {
            id: hoffLabel;
            anchors.left: fast.left;
            anchors.top: voffArea.bottom;
            anchors.topMargin: 8;
            text: "horizontalOffset:";
        }

        Rectangle {
            id: hoffArea;
            anchors.left: hoffLabel.right;
            anchors.leftMargin: 4;
            anchors.top: hoffLabel.top;
            height: 30;
            width: 160;
            color: "lightgray";
            border.width: 1;
            border.color: "darkgray";
            TextInput {
                anchors.fill: parent;
                anchors.margins: 2;
                id: hoffEdit;
                font.pointSize: 18;
                text: "0.0";
                validator: DoubleValidator{}
            }
        }

        Button {
            id: applyBtn;
            anchors.left: parent.left;
            anchors.leftMargin: 4;
            anchors.top: hoffArea.bottom;
            anchors.topMargin: 12;
            text: "Apply";
            onClicked: {
                dropshadow.color = shadowColor.color;
                dropshadow.fast = fast.checked;
                dropshadow.transparentBorder = transparentBorder.checked;
                dropshadow.samples = sampleSlider.value;
                dropshadow.radius = parseFloat(radiusEdit.text);
                dropshadow.verticalOffset = voffEdit.text;
                dropshadow.horizontalOffset = hoffEdit.text;
                dropshadow.spread = spreadSlider.value;
            }
        }
    }
}
           

    代碼比較簡單,不細說了。我們看看 DropShadow 元素的各個屬性都什麼含義吧。

  • source,variant類型,指向源Item
  • horizontalOffset 與verticalOffset,real類型,指定陰影相對于源Item的水準和垂直偏移量,預設為 0 
  • radius,real類型,設定陰影的柔和程度,值越大,陰影的邊緣就會顯得越柔和
  • sample,int類型,指定生成陰影時陰影的每個像素由多少個采樣點産生,采樣點越多陰影效果越好,不過也越慢。一般可以把這個值設定為 radius的2倍。
  • spread,real類型,指定如何強化陰影接近源 Item 邊緣的部分,取值範圍為 0.0 -- 1.0 ,預設為 0.5

    未提及的屬性都比較簡單,想 cached 、 fast 、 transparentBorder 等,之前的文章也提到過。

内陰影

    内陰影效果對應的 InnerShadowEx.qml 内容如下:

import QtQuick 2.2
import QtGraphicalEffects 1.0
import QtQuick.Controls 1.2

Rectangle {
    anchors.fill: parent;
    Image {
        id: opImage;
        x: 4;
        y: 4;
        width: 250;
        height: 250;
        source: "butterfly.png";
        sourceSize: Qt.size(250, 250);
        smooth: true;
        visible: false;
    }

    InnerShadow {
        id: innershadow;
        anchors.fill: opImage;
        source: opImage;
    }

    Rectangle {
        anchors.left: opImage.right;
        anchors.top: opImage.top;
        anchors.right: parent.right;
        anchors.bottom: parent.bottom;
        anchors.margins: 2;
        color: "lightsteelblue";

        CheckBox {
            id: fast;
            anchors.top: parent.top;
            anchors.topMargin: 4;
            anchors.left: parent.left;
            anchors.leftMargin: 4;
            checked: false;
            text: "fast";
        }

        Text {
            id: colorLabel;
            anchors.left: fast.left;
            anchors.top: fast.bottom;
            anchors.topMargin: 8;
            text: "shadow color:";
        }

        ColorPicker {
            id: shadowColor;
            anchors.left: colorLabel.right;
            anchors.leftMargin: 4;
            anchors.top: colorLabel.top;
            width: 90;
            height: 28;
            color: "#ff000000";
        }

        Text {
            id: sampleLabel;
            anchors.left: fast.left;
            anchors.top: shadowColor.bottom;
            anchors.topMargin: 8;
            text: "samples:";
        }

        Slider {
            id: sampleSlider;
            anchors.left: sampleLabel.right;
            anchors.leftMargin: 4;
            anchors.top: sampleLabel.top;
            minimumValue: 0;
            maximumValue: 32;
            value: 0.0;
            width: 160;
            height: 30;
            stepSize: 1.0;
        }

        Text {
            id: spreadLabel;
            anchors.left: fast.left;
            anchors.top: sampleSlider.bottom;
            anchors.topMargin: 8;
            text: "spread:";
        }

        Slider {
            id: spreadSlider;
            anchors.left: spreadLabel.right;
            anchors.leftMargin: 4;
            anchors.top: spreadLabel.top;
            value: 0.5;
            width: 160;
            height: 30;
        }

        Text {
            id: radiusLabel;
            anchors.left: fast.left;
            anchors.top: spreadSlider.bottom;
            anchors.topMargin: 8;
            text: "radius:";
        }

        Rectangle {
            id: radiusArea;
            anchors.left: radiusLabel.right;
            anchors.leftMargin: 4;
            anchors.top: radiusLabel.top;
            height: 30;
            width: 160;
            color: "lightgray";
            border.width: 1;
            border.color: "darkgray";
            TextInput {
                anchors.fill: parent;
                anchors.margins: 2;
                id: radiusEdit;
                font.pointSize: 18;
                text: "0.0";
                validator: DoubleValidator{bottom: 0;}
            }
        }


        Text {
            id: voffLabel;
            anchors.left: fast.left;
            anchors.top: radiusArea.bottom;
            anchors.topMargin: 8;
            text: "verticalOffset:";
        }

        Rectangle {
            id: voffArea;
            anchors.left: voffLabel.right;
            anchors.leftMargin: 4;
            anchors.top: voffLabel.top;
            height: 30;
            width: 160;
            color: "lightgray";
            border.width: 1;
            border.color: "darkgray";
            TextInput {
                anchors.fill: parent;
                anchors.margins: 2;
                id: voffEdit;
                font.pointSize: 18;
                text: "0.0";
                validator: DoubleValidator{}
            }
        }


        Text {
            id: hoffLabel;
            anchors.left: fast.left;
            anchors.top: voffArea.bottom;
            anchors.topMargin: 8;
            text: "verticalOffset:";
        }

        Rectangle {
            id: hoffArea;
            anchors.left: hoffLabel.right;
            anchors.leftMargin: 4;
            anchors.top: hoffLabel.top;
            height: 30;
            width: 160;
            color: "lightgray";
            border.width: 1;
            border.color: "darkgray";
            TextInput {
                anchors.fill: parent;
                anchors.margins: 2;
                id: hoffEdit;
                font.pointSize: 18;
                text: "0.0";
                validator: DoubleValidator{}
            }
        }

        Button {
            id: applyBtn;
            anchors.left: parent.left;
            anchors.leftMargin: 4;
            anchors.top: hoffArea.bottom;
            anchors.topMargin: 12;
            text: "Apply";
            onClicked: {
                innershadow.color = shadowColor.color;
                innershadow.fast = fast.checked;
                innershadow.samples = sampleSlider.value;
                innershadow.radius = parseFloat(radiusEdit.text);
                innershadow.verticalOffset = voffEdit.text;
                innershadow.horizontalOffset = hoffEdit.text;
                innershadow.spread = spreadSlider.value;
            }
        }
    }
}
           

    源碼比較簡單,不說了。

    InnerShadow 比 DropShadow 少了一個 transparentBorder 屬性,其他基本一緻,偷個懶,也不說了。

    回顧一下:

  • Qt Quick裡的圖形效果——漸變(Gradient)
  • Qt Quick裡的圖形效果——顔色(Color)
  • Qt Quick裡的圖形效果——混合(Blend)
  • Qt Quick裡的圖形效果(Graphical Effects)
  • Qt Quick專欄

繼續閱讀