Qt Quick裡的圖形效果:陰影(Drop Shadow)
阿新 • • 發佈:2019-01-05
Qt Quick提供了兩種陰影效果:
- DropShow,陰影。這個元素會根據源影象,產生一個彩色的、模糊的新影象,把這個新影象放在源影象後面,給人一種源影象從背景上凸出來的效果。
- InnerShadow,內陰影。這個元素會根據源影象,產生一個彩色的、模糊的新影象,與 DropShadow不同的是,新影象會放在源影象裡面。
效果
下面是我設計的示例效果。
首先是 DropShadow :
圖1 陰影效果
然後是內陰影效果:
圖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 屬性,其他基本一致,偷個懶,也不說了。
回顧一下: