1. 程式人生 > 其它 >第十章:形狀

第十章:形狀

第十章:形狀

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的子元素組成的,比如,上例中的PathArcPathLinePathMove元素。下一節中,我們將仔細觀察路徑的構建部分。

建立路徑

正如在上一節看到的,形狀是由多個路徑構建的,即由一些路徑元素來構建。最通用的構建路徑的方式是閉合它,比如,確保開始及終止結點在同一位置。雖然可以建立非閉合路徑,比如,僅描畫線時。當填充非閉合路徑時,路徑是通過在直線上新增填充路徑所用的PathLine來關閉的,而非通過描邊。
如下面截圖所示,一些基本的形狀有助於構建複雜路徑。它們是:線,弧線和各種曲線。也可以使用PathMove元素僅移動不畫線。除了這些元素外,ShapePath元素也允許使用startXstartY屬性來指定起始點。

如下所示,線是通過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)
            ]
        }
    }
}

建立弧線,比如圓形或橢圓形上的一段線,可以使用PathArcPathAngleArc元素。它們提供了建立弧線的工具,當已知起始點與終止點時,使用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提供的三種漸變之一。

使用純色來填充形狀,要用到ShapePathfillColor屬性。將其設定為顏色的名稱或顏色程式碼,形狀就會被其填充。

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。這時,要通過centerXcenterY屬性來指定中間節點,通過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.01.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建立動態形狀。我們也瞭解了可用於建立形狀的各種路徑段,如線、弧,以及各種曲線。最後,我們探索了填充選項,可用於為已有路徑建立令人振奮的視覺效果。