第十章:形狀
第十章:形狀
QtQMLQt Quick形狀
目前為止,我們已經使用過矩形Rectangle
元素和控制元件,但還未用過不規則形狀,這隻能依賴圖片。使用Qt Quick形狀模組可以建立不規則形狀。這使得直接從QML中靈活地建立可視元素成為可能。
本章我們將研究如何使用形狀、各種可用的路徑元素,如何以不同方式填充形狀,以及形狀如何結合QML的強大能力來形成流暢的動畫的形狀。
一個簡單形狀
形狀模組可讓您建立任意路徑,然後描邊輪廓並填充內部。路徑的定義可以在其它使用路徑的地方重用,比如,用於模型的PathView
元素。但對於路徑的繪製,要用到Shape
元素,且各種路徑元素被放到了ShapePath
在下面的例子中,建立了這個抓屏所顯示的路徑。整個圖象,五塊填充區域,都是從一條路徑建立然後被描邊和填充的。
import QtQuick
import QtQuick.Shapes
Rectangle {
id: root
width: 600
height: 600
Shape {
anchors.centerIn: parent
ShapePath {
strokeWidth: 3
strokeColor: "darkGray"
fillColor : "lightGray"
startX: -40; startY: 200
// The circle
PathArc { x: 40; y: 200; radiusX: 200; radiusY: 200; useLargeArc: true }
PathLine { x: 40; y: 120 }
PathArc { x: -40; y: 120; radiusX: 120; radiusY: 120; useLargeArc: true; direction : PathArc.Counterclockwise }
PathLine { x: -40; y: 200 }
// The dots
PathMove { x: -20; y: 80 }
PathArc { x: 20; y: 80; radiusX: 20; radiusY: 20; useLargeArc: true }
PathArc { x: -20; y: 80; radiusX: 20; radiusY: 20; useLargeArc: true }
PathMove { x: -20; y: 130 }
PathArc { x: 20; y: 130; radiusX: 20; radiusY: 20; useLargeArc: true }
PathArc { x: -20; y: 130; radiusX: 20; radiusY: 20; useLargeArc: true }
PathMove { x: -20; y: 180 }
PathArc { x: 20; y: 180; radiusX: 20; radiusY: 20; useLargeArc: true }
PathArc { x: -20; y: 180; radiusX: 20; radiusY: 20; useLargeArc: true }
PathMove { x: -20; y: 230 }
PathArc { x: 20; y: 230; radiusX: 20; radiusY: 20; useLargeArc: true }
PathArc { x: -20; y: 230; radiusX: 20; radiusY: 20; useLargeArc: true }
}
}
}
路徑是由ShapePath
的子元素組成的,比如,上例中的PathArc
,PathLine
和PathMove
元素。下一節中,我們將仔細觀察路徑的構建部分。
建立路徑
正如在上一節看到的,形狀是由多個路徑構建的,即由一些路徑元素來構建。最通用的構建路徑的方式是閉合它,比如,確保開始及終止結點在同一位置。雖然可以建立非閉合路徑,比如,僅描畫線時。當填充非閉合路徑時,路徑是通過在直線上新增填充路徑所用的PathLine
來關閉的,而非通過描邊。
如下面截圖所示,一些基本的形狀有助於構建複雜路徑。它們是:線,弧線和各種曲線。也可以使用PathMove
元素僅移動不畫線。除了這些元素外,ShapePath
元素也允許使用startX
和startY
屬性來指定起始點。
如下所示,線是通過PathLine
元素來繪製的。想繪製多條獨立的線段,可以使用PathMultiline
。
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
startX: 20; startY: 70
PathLine {
x: 180
y: 130
}
}
}
當建立折線,比如,由多條線段組成的線,可以使用PathPolyline
元素。這樣可以少輸入些程式碼,因為上一條線段的終點被設定為下一條線段的起點。
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
PathPolyline {
path: [
Qt.point(220, 100),
Qt.point(260, 20),
Qt.point(300, 170),
Qt.point(340, 60),
Qt.point(380, 100)
]
}
}
}
建立弧線,比如圓形或橢圓形上的一段線,可以使用PathArc
和PathAngleArc
元素。它們提供了建立弧線的工具,當已知起始點與終止點時,使用PathArc
;當想要控制弧掃過的度數時,PathAngleArc
很有用。兩個元素都有同樣的效果,用哪一個,取決於對程式來講更看重哪個方面。
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
startX: 420; startY: 100
PathArc {
x: 580; y: 180
radiusX: 120; radiusY: 120
}
}
}
線與弧後緊接著的是各種曲線。這裡,Qt Quick形狀模組提供了三種風格。首先,一起來看下PathQuad
,允許根據起點和終點(起點是隱含的)和單個控制點建立二次貝塞爾曲線。
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
startX: 20; startY: 300
PathQuad {
x: 180; y: 300
controlX: 60; controlY: 250
}
}
}
PathCubic
元素從起點和終點(起點是隱含的)和兩個控制點建立一條三次貝塞爾曲線。
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
startX: 220; startY: 300
PathCubic {
x: 380; y: 300
control1X: 260; control1Y: 250
control2X: 360; control2Y: 350
}
}
}
最後,PathCurve
建立一條通過所定義的控制點列表的曲線。曲線是通過提供多個 PathCurve
元素建立的,每個元素都包含一個控制點。 Catmull-Rom 樣條用於建立通過控制點的曲線。
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
startX: 420; startY: 300
PathCurve { x: 460; y: 220 }
PathCurve { x: 500; y: 370 }
PathCurve { x: 540; y: 270 }
PathCurve { x: 580; y: 300 }
}
}
還有一個更有用的路徑元素 PathSvg
。此元素可描邊和填充 SVG 路徑。
注意
PathSvg
元素不能總是與其他路徑元素組合。這取決於採用的後端繪製方法,所以,請單獨在一個路徑裡使用PathSvg
或其它元素。如果將PathSvg
或其它路徑元素混用,效果可能跟你期待的不一樣。
填充形狀
圖形可以以不同方式進行填充。本節將講到通常的填充規則,以及各種不同的填充方式。
Qt Quick 形狀模組使用ShapePath
元素的fillRule
屬性提供了兩種填充控制規則。下面的截圖顯示了兩種不同的效果。它可以被設定成預設的ShapePath.OddEvenFill
(奇偶填充)。這將逐一填充路徑的每個部分,也就是可以建立有洞的形狀。另一條規則是ShapePath.WindingFill
,填充形狀上每條水平線的端點之間的所有內容。不管填充規則如何,接下來要使用筆繪製形狀輪廓,因此即使使用纏繞填充規則,輪廓也會在形狀內部繪製。
下面的例子演示瞭如何使用上圖所示效果的不同的填充方式。
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
fillColor: "orange"
fillRule: ShapePath.OddEvenFill
PathPolyline {
path: [
Qt.point(100, 20),
Qt.point(150, 180),
Qt.point( 20, 75),
Qt.point(180, 75),
Qt.point( 50, 180),
Qt.point(100, 20),
]
}
}
}
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
fillColor: "orange"
fillRule: ShapePath.WindingFill
PathPolyline {
path: [
Qt.point(300, 20),
Qt.point(350, 180),
Qt.point(220, 75),
Qt.point(380, 75),
Qt.point(250, 180),
Qt.point(300, 20),
]
}
}
}
一旦確定了填充規則,有多種方法可以填充輪廓。各種方法實現的效果如下截圖所示。這些方法或是純色,或是Qt Quick提供的三種漸變之一。
使用純色來填充形狀,要用到ShapePath
的fillColor
屬性。將其設定為顏色的名稱或顏色程式碼,形狀就會被其填充。
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
fillColor: "lightgreen"
startX: 20; startY: 140
PathLine {
x: 180
y: 140
}
PathArc {
x: 20
y: 140
radiusX: 80
radiusY: 80
direction: PathArc.Counterclockwise
useLargeArc: true
}
}
}
如果不想用純色,可以使用漸變。漸變是通過ShapePath
元素的fillGradient
屬性來應用的。
首先來看線性漸變LinearGradient
。其在起點與終點間建立了一個線性漸變。終點(漸變節點)可以在任意方式建立,比如,一定角度的漸變。在這些(漸變)節點之間,可以插入一系列GradientStop
元素。它們被放在position
裡,從0.0
,在x1,y1
座標位置,到1.0
,在x2,y2
座標位置。對於每一個節點,要指定一個顏色。漸變會在這些節點顏色間建立過渡。
注意
如果形狀超過最後的漸變節點,則首個或開個節點顏色將被繼續延用,或將漸變重複或鏡象。這是能過LinearGradient
元素的spread
屬性來指定的。
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
fillGradient: LinearGradient {
x1: 50; y1: 300
x2: 150; y2: 280
GradientStop { position: 0.0; color: "lightgreen" }
GradientStop { position: 0.7; color: "yellow" }
GradientStop { position: 1.0; color: "darkgreen" }
}
startX: 20; startY: 340
PathLine {
x: 180
y: 340
}
PathArc {
x: 20
y: 340
radiusX: 80
radiusY: 80
direction: PathArc.Counterclockwise
useLargeArc: true
}
}
}
要建立一個圍繞原點傳播的漸變,有點像時鐘那樣,要使用ConicalGradient
。這時,要通過centerX
和centerY
屬性來指定中間節點,通過angle
給出開始角度。漸變從給定的角度開始沿順時針方向做360度展開。
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
fillGradient: ConicalGradient {
centerX: 300; centerY: 100
angle: 45
GradientStop { position: 0.0; color: "lightgreen" }
GradientStop { position: 0.7; color: "yellow" }
GradientStop { position: 1.0; color: "darkgreen" }
}
startX: 220; startY: 140
PathLine {
x: 380
y: 140
}
PathArc {
x: 220
y: 140
radiusX: 80
radiusY: 80
direction: PathArc.Counterclockwise
useLargeArc: true
}
}
}
為了建立一個形成圓形的漸變,有點像水面上的環,使用了 RadialGradient
。這需要指定兩個圓,焦點圓和中心圓。漸變從焦點圓向中心圓擴充套件,越過這些圓後,最末節點的顏色會繼續,被鏡象或重複,依賴於spread
屬性。
Shape {
ShapePath {
strokeWidth: 3
strokeColor: "darkgray"
fillGradient: RadialGradient {
centerX: 300; centerY: 250; centerRadius: 120
focalX: 300; focalY: 220; focalRadius: 10
GradientStop { position: 0.0; color: "lightgreen" }
GradientStop { position: 0.7; color: "yellow" }
GradientStop { position: 1.0; color: "darkgreen" }
}
startX: 220; startY: 340
PathLine {
x: 380
y: 340
}
PathArc {
x: 220
y: 340
radiusX: 80
radiusY: 80
direction: PathArc.Counterclockwise
useLargeArc: true
}
}
}
注意
高階使用者可以使用片段著色器來填充形狀。這樣,您就可以完全自由地選擇形狀的填充方式。有關著色器的更多資訊,請參閱效果章節。
形狀動畫
Qt Quick形狀模組用法的妙處之一,就是可以在QML中直接完成路徑繪製。這意味著它們的屬性可以被繫結、實現過渡、動畫效果,就象QML中其它的屬性那樣。
下面的例子中,我們將再次用到本章第一節裡用到的形狀,但將引入新的變數,t
,並在從0.0
到1.0
的迴圈中做成動畫效果。使用這個變數來偏移小圓形的位置,以及最項部和最底部圓形的大小。這將建立一個動畫,看起來象圓圈出現在頂部並朝底部消失。
import QtQuick
import QtQuick.Shapes
Rectangle {
id: root
width: 600
height: 600
Shape {
anchors.centerIn: parent
ShapePath {
id: shapepath
property real t: 0.0
NumberAnimation on t { from: 0.0; to: 1.0; duration: 500; loops: Animation.Infinite; running: true }
strokeWidth: 3
strokeColor: "darkGray"
fillColor: "lightGray"
startX: -40; startY: 200
// The circle
PathArc { x: 40; y: 200; radiusX: 200; radiusY: 200; useLargeArc: true }
PathLine { x: 40; y: 120 }
PathArc { x: -40; y: 120; radiusX: 120; radiusY: 120; useLargeArc: true; direction: PathArc.Counterclockwise }
PathLine { x: -40; y: 200 }
// The dots
PathMove { x: -20+(1.0-shapepath.t)*20; y: 80 + shapepath.t*50 }
PathArc { x: 20-(1.0-shapepath.t)*20; y: 80 + shapepath.t*50; radiusX: 20*shapepath.t; radiusY: 20*shapepath.t; useLargeArc: true }
PathArc { x: -20+(1.0-shapepath.t)*20; y: 80 + shapepath.t*50; radiusX: 20*shapepath.t; radiusY: 20*shapepath.t; useLargeArc: true }
PathMove { x: -20; y: 130 + shapepath.t*50 }
PathArc { x: 20; y: 130 + shapepath.t*50; radiusX: 20; radiusY: 20; useLargeArc: true }
PathArc { x: -20; y: 130 + shapepath.t*50; radiusX: 20; radiusY: 20; useLargeArc: true }
PathMove { x: -20; y: 180 + shapepath.t*50 }
PathArc { x: 20; y: 180 + shapepath.t*50; radiusX: 20; radiusY: 20; useLargeArc: true }
PathArc { x: -20; y: 180 + shapepath.t*50; radiusX: 20; radiusY: 20; useLargeArc: true }
PathMove { x: -20+shapepath.t*20; y: 230 + shapepath.t*50 }
PathArc { x: 20-shapepath.t*20; y: 230 + shapepath.t*50; radiusX: 20*(1.0-shapepath.t); radiusY: 20*(1.0-shapepath.t); useLargeArc: true }
PathArc { x: -20+shapepath.t*20; y: 230 + shapepath.t*50; radiusX: 20*(1.0-shapepath.t); radiusY: 20*(1.0-shapepath.t); useLargeArc: true }
}
}
}
注意NumberAnimation on t
的用法,可以繫結其它任何屬性,比如,綁到滑塊、或外部狀態等。只有你想不到,沒有做不到。
總結
本章裡我們瞭解到Qt Quick形狀模組能提供什麼功能。使用它可以在QML建立任意形狀,並通過QML系統所帶有的property
建立動態形狀。我們也瞭解了可用於建立形狀的各種路徑段,如線、弧,以及各種曲線。最後,我們探索了填充選項,可用於為已有路徑建立令人振奮的視覺效果。