ソースを参照

添加第五章fluid elements

cwc 10 年 前
コミット
a1492b5342

+ 4 - 0
SUMMARY.md

@@ -28,4 +28,8 @@
    * [布局元素(Layout Items)](quick_starter/layout_items.md)
    * [布局元素(Layout Items)](quick_starter/layout_items.md)
    * [输入元素(Input Element)](quick_starter/input_element.md)
    * [输入元素(Input Element)](quick_starter/input_element.md)
    * [ 高级用法(Advanced Techniques)](quick_starter/advanced_techniques.md)
    * [ 高级用法(Advanced Techniques)](quick_starter/advanced_techniques.md)
+* [Fluid Elements](fluid_elements/README.md)
+   * [动画(Animations)](fluid_elements/animations.md)
+   * [状态与过渡(States and Transitions)](fluid_elements/states_and_transitions.md)
+   * [高级用法(Advanced Techniques)](fluid_elements/advanced_techniques.md)
 
 

+ 9 - 0
fluid_elements/README.md

@@ -0,0 +1,9 @@
+# Fluid Elements
+
+**注意**
+
+**最后一次构建:2014年1月20日下午18:00。**
+
+**这章的源代码能够在[assetts folder](http://qmlbook.org/assets)找到。**
+
+到目前为止,我们已经介绍了简单的图形元素和怎样布局,怎样操作它们。这一章介绍如何控制属性值的变化,通过动画的方式在一段时间内来改变属性值。这项技术是建立一个现代化的平滑界面的基础,通过使用状态和过渡来扩展你的用户界面。每一种状态定义了属性的改变,与动画联系起来的状态改变称作过渡。

+ 3 - 0
fluid_elements/advanced_techniques.md

@@ -0,0 +1,3 @@
+# 高级用法(Advanced Techniques)
+
+后续添加。

+ 602 - 0
fluid_elements/animations.md

@@ -0,0 +1,602 @@
+# 动画(Animations)
+
+动画被用于属性的改变。一个动画定义了属性值改变的曲线,将一个属性值变化从一个值过渡到另一个值。动画是由一连串的目标属性活动定义的,平缓的曲线算法能够引发一个定义时间内属性的持续变化。所有在QtQuick中的动画都由同一个计时器来控制,因此它们始终都保持同步,这也提高了动画的性能和显示效果。
+
+**注意**
+
+**动画控制了属性的改变,也就是值的插入。这是一个基本的概念,QML是基于元素,属性与脚本的。每一个元素都提供了许多的属性,每一个属性都在等待使用动画。在这本书中你将会看到这是一个壮阔的场景,你会发现你自己在看一些动画时欣赏它们的美丽并且肯定自己的创造性想法。然后请记住:动画控制了属性的改变,每个元素都有大量的属性供你任意使用。**
+
+![](http://qmlbook.org/_images/animation.png)
+
+```
+// animation.qml
+
+import QtQuick 2.0
+
+Image {
+    source: "assets/background.png"
+
+    Image {
+        x: 40; y: 80
+        source: "assets/rocket.png"
+
+        NumberAnimation on x {
+            to: 240
+            duration: 4000
+            loops: Animation.Infinite
+        }
+        RotationAnimation on rotation {
+            to: 360
+            duration: 4000
+            loops: Animation.Infinite
+        }
+    }
+}
+```
+
+上面这个例子在x坐标和旋转属性上应用了一个简单的动画。每一次动画持续4000毫秒并且永久循环。x轴坐标动画展示了火箭的x坐标逐渐移至240,旋转动画展示了当前角度到360度的旋转。两个动画同时运行,并且在加载用户界面完成后开始。
+
+现在你可以通过to属性和duration属性来实现动画效果。或者你可以在opacity或者scale上添加动画作为例子,集成这两个参数,你可以实现火箭逐渐消失在太空中,试试吧!
+
+## 5.1.1 动画元素(Animation Elements)
+
+有几种类型的动画,每一种都在特定情况下都有最佳的效果,下面列出了一些常用的动画:
+
+* PropertyAnimation(属性动画)- 使用属性值改变播放的动画
+
+* NumberAnimation(数字动画)- 使用数字改变播放的动画
+
+* ColorAnimation(颜色动画)- 使用颜色改变播放的动画
+
+* RotationAnimation(旋转动画)- 使用旋转改变播放的动画
+
+除了上面这些基本和通常使用的动画元素,QtQuick还提供了一切特殊场景下使用的动画:
+
+* PauseAnimation(停止动画)- 运行暂停一个动画
+
+* SequentialAnimation(继续动画)- 允许动画继续播放
+
+* ParallelAnimation(平行动画)- 允许动画平行播放
+
+* AnchorAnimation(锚定动画)- 使用锚定改变播放的动画
+
+* ParentAnimation(父元素动画)- 使用父对象改变播放的动画
+
+* SmotthedAnimation(平滑动画)- 跟踪一个平滑值播放的动画
+
+* SpringAnimation(弹簧动画)- 跟踪一个弹簧变换的值播放的动画
+
+* PathAnimation(路径动画)- 跟踪一个元素对象的路径的动画
+
+* Vector3dAnimation(3D容器动画)- 使用QVector3d值改变播放的动画
+
+我们将在后面学习怎样创建一连串的动画。当使用更加复杂的动画时,我们可能需要在播放一个动画时中改变一个属性或者运行一个脚本。对于这个问题,QtQuick提供了一个动作元素:
+
+* PropertyAction(属性动作)- 在播放动画时改变属性
+
+* ScriptAction(脚本动作)- 在播放动画时运行脚本
+
+在这一章中我们将会使用一些小的例子来讨论大多数类型的动画。
+
+## 5.1.2 应用动画(Applying Animations)
+
+动画可以通过以下几种方式来应用:
+
+* 属性动画 - 在元素完整加载后自动运行
+
+* 属性动作 - 当属性值改变时自动运行
+
+* 独立运行动画 - 使用start()函数明确指定运行或者running属性被设置为true(比如通过属性绑定)
+
+后面我们会谈论如何在状态变换时播放动画。
+
+**扩展可点击图像元素版本2(ClickableImage Version2)**
+
+为了演示动画的使用方法,我们重新实现了ClickableImage组件并且使用了一个文本元素(Text Element)来扩展它。
+
+```
+// ClickableImageV2.qml
+// Simple image which can be clicked
+
+import QtQuick 2.0
+
+Item {
+    id: root
+    width: container.childrenRect.width
+    height: container.childrenRect.height
+    property alias text: label.text
+    property alias source: image.source
+    signal clicked
+
+    Column {
+        id: container
+        Image {
+            id: image
+        }
+        Text {
+            id: label
+            width: image.width
+            horizontalAlignment: Text.AlignHCenter
+            wrapMode: Text.WordWrap
+            color: "#111111"
+        }
+    }
+
+    MouseArea {
+        anchors.fill: parent
+        onClicked: root.clicked()
+    }
+}
+```
+
+为了给图片下面的元素定位,我们使用了Column(列)定位器,并且使用基于列的子矩形(childRect)属性来计算它的宽度和高度(width and height)。我们导出了文本(text)和图形源(source)属性,一个点击信号(clicked signal)。我们使用文本元素的wrapMode属性来设置文本与图像一样宽并且可以自动换行。
+
+**注意**
+
+**由于几何依赖关系的反向(父几何对象依赖于子几何对象)我们不能对ClickableImageV2设置宽度/高度(width/height),因为这样将会破坏我们已经做好的属性绑定。这是我们内部设计的限制,作为一个设计组件的人你需要明白这一点。通常我们更喜欢内部几何图像依赖于父几何对象。**
+
+![](http://qmlbook.org/_images/animationtypes_start.png)
+
+三个火箭位于相同的y轴坐标(y = 200)。它们都需要移动到y = 40。每一个火箭都使用了一种的方法来完成这个功能。
+
+```
+    ClickableImageV3 {
+        id: rocket1
+        x: 40; y: 200
+        source: "assets/rocket2.png"
+        text: "animation on property"
+        NumberAnimation on y {
+            to: 40; duration: 4000
+        }
+    }
+```
+
+**第一个火箭**
+
+
+第一个火箭使用了Animation on <property>属性变化的策略来完成。动画会在加载完成后立即播放。点击火箭可以重置它回到开始的位置。在动画播放时重置第一个火箭不会有任何影响。在动画开始前的几分之一秒设置一个新的y轴坐标让人感觉挺不安全的,应当避免这样的属性值竞争的变化。
+
+```
+    ClickableImageV3 {
+        id: rocket2
+        x: 152; y: 200
+        source: "assets/rocket2.png"
+        text: "behavior on property"
+        Behavior on y {
+            NumberAnimation { duration: 4000 }
+        }
+
+        onClicked: y = 40
+        // random y on each click
+    //        onClicked: y = 40+Math.random()*(205-40)
+    }
+```
+
+**第二个火箭**
+
+第二个火箭使用了behavior on <property>属性行为策略的动画。这个行为告诉属性值每时每刻都在变化,通过动画的方式来改变这个值。可以使用行为元素的enabled : false来设置行为失效。当你点击这个火箭时它将会开始运行(y轴坐标逐渐移至40)。然后其它的点击对于位置的改变没有任何的影响。你可以试着使用一个随机值(例如 40+(Math.random()*(205-40))来设置y轴坐标。你可以发现动画始终会将移动到新位置的时间匹配在4秒内完成。
+
+```
+    ClickableImageV3 {
+        id: rocket3
+        x: 264; y: 200
+        source: "assets/rocket2.png"
+        onClicked: anim.start()
+    //        onClicked: anim.restart()
+
+        text: "standalone animation"
+
+        NumberAnimation {
+            id: anim
+            target: rocket3
+            properties: "y"
+            from: 205
+            to: 40
+            duration: 4000
+        }
+    }
+```
+
+**第三个火箭**
+
+第三个火箭使用standalone animation独立动画策略。这个动画由一个私有的元素定义并且可以写在文档的任何地方。点击火箭调用动画函数start()来启动动画。每一个动画都有start(),stop(),resume(),restart()函数。这个动画自身可以比其他类型的动画更早的获取到更多的相关信息。我们只需要定义目标和目标元素的属性需要怎样改变的一个动画。我们定义一个to属性的值,在这个例子中我们也定义了一个from属性的值允许动画可以重复运行。
+
+![](http://qmlbook.org/_images/animationtypes.png)
+
+点击背景能够重新设置所有的火箭回到它们的初始位置。第一个火箭无法被重置,只有重启程序重新加载元素才能重置它。
+
+**注意**
+
+**另一个启动/停止一个动画的方法是绑定一个动画的running属性。当需要用户输入控制属性时这种方法非常有用:**
+
+```
+    NumberAnimation {
+        ...
+        // animation runs when mouse is pressed
+        running: area.pressed
+    }
+    MouseArea {
+        id: area
+    }
+```
+
+## 5.1.3 缓冲曲线(Easing Curves)
+
+属性值的改变能够通过一个动画来控制,缓冲曲线属性影响了一个属性值改变的插值算法。我们现在已经定义的动画都使用了一种线性的插值算法,因为一个动画的默认缓冲类型是Easing.Linear。在一个小场景下的x轴与y轴坐标改变可以得到最好的视觉效果。一个线性插值算法将会在动画开始时使用from的值到动画结束时使用的to值绘制一条直线,所以缓冲类型定义了曲线的变化情况。精心为一个移动的对象挑选一个合适的缓冲类型将会使界面更加自然,例如一个页面的滑出,最初使用缓慢的速度滑出,然后在最后滑出时使用高速滑出,类似翻书一样的效果。
+
+**注意**
+
+**不要过度的使用动画。用户界面动画的设计应该尽量小心,动画是让界面更加生动而不是充满整个界面。眼睛对于移动的东西非常敏感,很容易干扰用户的使用。**
+
+在下面的例子中我们将会使用不同的缓冲曲线,每一种缓冲曲线都都使用了一个可点击图片来展示,点击将会在动画中设置一个新的缓冲类型并且使用这种曲线重新启动动画。
+
+![](http://qmlbook.org/_images/easingtypes.png)
+
+**扩展可点击图像V3(ClickableImage V3)**
+
+我们给图片和文本添加了一个小的外框来增强我们的ClickableImage。添加一个属性property bool framed: false来作为我们的API,基于framed的值我们能够设置这个框是否可见,并且不破坏之前用户的使用。下面是我们做的修改。
+
+```
+// ClickableImageV2.qml
+// Simple image which can be clicked
+
+import QtQuick 2.0
+
+Item {
+    id: root
+    width: container.childrenRect.width + 16
+    height: container.childrenRect.height + 16
+    property alias text: label.text
+    property alias source: image.source
+    signal clicked
+
+    // M1>>
+    // ... add a framed rectangle as container
+    property bool framed : false
+
+    Rectangle {
+        anchors.fill: parent
+        color: "white"
+        visible: root.framed
+    }
+```
+
+这个例子的代码非常简洁。我们使用了一连串的缓冲曲线的名称(property variant easings)并且在一个Repeater(重复元素)中将它们分配给一个ClickableImage。图片的源路径通过一个命名方案来定义,一个叫做“InQuad”的缓冲曲线在“curves/InQuad.png”中有一个对应的图片。如果你点击一个曲线图,这个点击将会分配一个缓冲类型给动画然后重新启动动画。动画自身是用来设置方块的x坐标属性在2秒内变化的独立动画。
+
+```
+// easingtypes.qml
+
+import QtQuick 2.0
+
+DarkSquare {
+    id: root
+    width: 600
+    height: 340
+
+    // A list of easing types
+    property variant easings : [
+        "Linear", "InQuad", "OutQuad", "InOutQuad",
+        "InCubic", "InSine", "InCirc", "InElastic",
+        "InBack", "InBounce" ]
+
+
+    Grid {
+        id: container
+        anchors.top: parent.top
+        anchors.horizontalCenter: parent.horizontalCenter
+        anchors.margins: 16
+        height: 200
+        columns: 5
+        spacing: 16
+        // iterates over the 'easings' list
+        Repeater {
+            model: easings
+            ClickableImageV3 {
+                framed: true
+                // the current data entry from 'easings' list
+                text: modelData
+                source: "curves/" + modelData + ".png"
+                onClicked: {
+                    // set the easing type on the animation
+                    anim.easing.type = modelData
+                    // restart the animation
+                    anim.restart()
+                }
+            }
+        }
+    }
+
+    // The square to be animated
+    GreenSquare {
+        id: square
+        x: 40; y: 260
+    }
+
+    // The animation to test the easing types
+    NumberAnimation {
+        id: anim
+        target: square
+        from: 40; to: root.width - 40 - square.width
+        properties: "x"
+        duration: 2000
+    }
+}
+```
+
+当你运行这个例子时,请注意观察动画的改变速度。一些动画对于这个对象看起来很自然,一些看起来非常恼火。
+
+除了duration属性与easing.type属性,你也可以对动画进行微调。例如PropertyAnimation属性,大多数动画都支持附加的easing.amplitude(缓冲振幅),easing.overshoot(缓冲溢出),easing.period(缓冲周期),这些属性允许你对个别的缓冲曲线进行微调。不是所有的缓冲曲线都支持这些参数。可以查看Qt PropertyAnimation文档中的缓冲列表(easing table)来查看一个缓冲曲线的相关参数。
+
+**注意**
+
+**对于用户界面正确的动画非常重要。请记住动画是帮助用户界面更加生动而不是刺激用户的眼睛。**
+
+## 5.1.4 动画分组(Grouped Animations)
+
+通常使用的动画比一个属性的动画更加复杂。例如你想同时运行几个动画并把他们连接起来,或者在一个一个的运行,或者在两个动画之间执行一个脚本。动画分组提供了很好的帮助,作为命名建议可以叫做一组动画。有两种方法来分组:平行与连续。你可以使用SequentialAnimation(连续动画)和ParallelAnimation(平行动画)来实现它们,它们作为动画的容器来包含其它的动画元素。
+
+![](http://qmlbook.org/_images/groupedanimation.png)
+
+当开始时,平行元素的所有子动画都会平行运行,它允许你在同一时间使用不同的属性来播放动画。
+
+```
+// parallelanimation.qml
+import QtQuick 2.0
+
+BrightSquare {
+    id: root
+    width: 300
+    height: 200
+    property int duration: 3000
+
+    ClickableImageV3 {
+        id: rocket
+        x: 20; y: 120
+        source: "assets/rocket2.png"
+        onClicked: anim.restart()
+    }
+
+    ParallelAnimation {
+        id: anim
+        NumberAnimation {
+            target: rocket
+            properties: "y"
+            to: 20
+            duration: root.duration
+        }
+        NumberAnimation {
+            target: rocket
+            properties: "x"
+            to: 160
+            duration: root.duration
+        }
+    }
+}
+```
+
+![](http://qmlbook.org/_images/parallelanimation_sequence.png)
+
+一个连续的动画将会一个一个的运行子动画。
+
+```
+// sequentialanimation.qml
+import QtQuick 2.0
+
+BrightSquare {
+    id: root
+    width: 300
+    height: 200
+    property int duration: 3000
+
+    ClickableImageV3 {
+        id: rocket
+        x: 20; y: 120
+        source: "assets/rocket2.png"
+        onClicked: anim.restart()
+    }
+
+    SequentialAnimation {
+        id: anim
+        NumberAnimation {
+            target: rocket
+            properties: "y"
+            to: 20
+            // 60% of time to travel up
+            duration: root.duration*0.6
+        }
+        NumberAnimation {
+            target: rocket
+            properties: "x"
+            to: 160
+            // 40% of time to travel sideways
+            duration: root.duration*0.4
+        }
+    }
+}
+```
+
+![](http://qmlbook.org/_images/sequentialanimation_sequence.png)
+
+分组动画也可以被嵌套,例如一个连续动画可以拥有两个平行动画作为子动画。我们来看看这个足球的例子。这个动画描述了一个从左向右扔一个球的行为:
+
+![](http://qmlbook.org/_images/soccer_init.png)
+
+要弄明白这个动画我们需要剖析这个目标的运动过程。我们需要记住这个动画是通过属性变化来实现的动画,下面是不同部分的转换:
+
+* 从左向右的x坐标转换(X1)。
+
+* 从下往上的y坐标转换(Y1)然后跟着一个从上往下的Y坐标转换(Y2)。
+
+* 整个动画过程中360度旋转。
+
+这个动画将会花掉3秒钟的时间。
+
+![](http://qmlbook.org/_images/soccer_plan.png)
+
+我们使用一个空的基本元素对象(Item)作为根元素,它的宽度为480,高度为300。
+
+```
+import QtQuick 1.1
+
+Item {
+    id: root
+    width: 480
+    height: 300
+    property int duration: 3000
+
+    ...
+}
+```
+
+我们定义动画的总持续时间作为参考,以便更好的同步各部分的动画。
+
+下一步我们需需要添加一个背景,在我们这个例子中有两个矩形框分别使用了绿色渐变和蓝色渐变填充。
+
+```
+    Rectangle {
+        id: sky
+        width: parent.width
+        height: 200
+        gradient: Gradient {
+            GradientStop { position: 0.0; color: "#0080FF" }
+            GradientStop { position: 1.0; color: "#66CCFF" }
+        }
+    }
+    Rectangle {
+        id: ground
+        anchors.top: sky.bottom
+        anchors.bottom: root.bottom
+        width: parent.width
+        gradient: Gradient {
+            GradientStop { position: 0.0; color: "#00FF00" }
+            GradientStop { position: 1.0; color: "#00803F" }
+        }
+    }
+```
+
+![](http://qmlbook.org/_images/soccer_stage1.png)
+
+上面部分的蓝色区域高度为200像素,下面部分的区域使用上面的蓝色区域的底作为锚定的顶,使用根元素的底作为底。
+
+让我们将足球加入到屏幕上,足球是一个图片,位于路径“assets/soccer_ball.png”。首先我们需要将它放置在左下角接近边界处。
+
+```
+    Image {
+        id: ball
+        x: 20; y: 240
+        source: "assets/soccer_ball.png"
+
+        MouseArea {
+            anchors.fill: parent
+            onClicked: {
+                ball.x = 20; ball.y = 240
+                anim.restart()
+            }
+        }
+    }
+```
+
+![](http://qmlbook.org/_images/soccer_stage2.png)
+
+图片与鼠标区域连接,点击球将会重置球的状态,并且动画重新开始。
+
+首先使用一个连续的动画来播放两次的y轴变换。
+
+```
+    SequentialAnimation {
+        id: anim
+        NumberAnimation {
+            target: ball
+            properties: "y"
+            to: 20
+            duration: root.duration * 0.4
+        }
+        NumberAnimation {
+            target: ball
+            properties: "y"
+            to: 240
+            duration: root.duration * 0.6
+        }
+    }
+```
+
+![](http://qmlbook.org/_images/soccer_stage3.png)
+
+在动画总时间的40%的时间里完成上升部分,在动画总时间的60%的时间里完成下降部分,一个动画完成后播放下一个动画。目前还没有使用任何缓冲曲线。缓冲曲线将在后面使用easing curves来添加,现在我们只关心如何使用动画来完成过渡。
+
+现在我们需要添加x轴坐标转换。x轴坐标转换需要与y轴坐标转换同时进行,所以我们需要将y轴坐标转换的连续动画和x轴坐标转换一起压缩进一个平行动画中。
+
+```
+    ParallelAnimation {
+        id: anim
+        SequentialAnimation {
+            // ... our Y1, Y2 animation
+        }
+        NumberAnimation { // X1 animation
+            target: ball
+            properties: "x"
+            to: 400
+            duration: root.duration
+        }
+    }
+```
+
+![](http://qmlbook.org/_images/soccer_stage4.png)
+
+最后我们想要旋转这个球,我们需要向平行动画中添加一个新的动画,我们选择RotationAnimation来实现旋转。
+
+```
+    ParallelAnimation {
+        id: anim
+        SequentialAnimation {
+            // ... our Y1, Y2 animation
+        }
+        NumberAnimation { // X1 animation
+            // X1 animation
+        }
+        RotationAnimation {
+            target: ball
+            properties: "rotation"
+            to: 720
+            duration: root.duration
+        }
+    }
+```
+
+我们已经完成了整个动画链表,然后我们需要给动画提供一个正确的缓冲曲线来描述一个移动的球。对于Y1动画我们使用Easing.OutCirc缓冲曲线,它看起来更像是一个圆周运动。Y2使用了Easing.OutBounce缓冲曲线,因为在最后球会发生反弹。(试试使用Easing.InBounce,你会发现反弹将会立刻开始。)。X1和ROT1动画都使用线性曲线。
+
+下面是这个动画最后的代码,提供给你作为参考:
+
+```
+    ParallelAnimation {
+        id: anim
+        SequentialAnimation {
+            NumberAnimation {
+                target: ball
+                properties: "y"
+                to: 20
+                duration: root.duration * 0.4
+                easing.type: Easing.OutCirc
+            }
+            NumberAnimation {
+                target: ball
+                properties: "y"
+                to: 240
+                duration: root.duration * 0.6
+                easing.type: Easing.OutBounce
+            }
+        }
+        NumberAnimation {
+            target: ball
+            properties: "x"
+            to: 400
+            duration: root.duration
+        }
+        RotationAnimation {
+            target: ball
+            properties: "rotation"
+            to: 720
+            duration: root.duration * 1.1
+        }
+    }
+```

+ 136 - 0
fluid_elements/states_and_transitions.md

@@ -0,0 +1,136 @@
+# 状态与过渡(States and Transitions)
+
+通常我们将用户界面描述为一种状态。一个状态定义了一组属性的改变,并且会在一定的条件下被触发。另外在这些状态转化的过程中可以有一个过渡,定义了这些属性的动画或者一些附加的动作。当进入一个新的状态时,动作也可以被执行。
+
+## 5.2.1 状态(States)
+
+在QML中,使用State元素来定义状态,需要与基础元素对象(Item)的states序列属性连接。状态通过它的状态名来鉴别,由组成它的一系列简单的属性来改变元素。默认的状态在初始化元素属性时定义,并命名为“”(一个空的字符串)。
+
+```
+    Item {
+        id: root
+        states: [
+            State {
+                name: "go"
+                PropertyChanges { ... }
+            },
+            State {
+                name: "stop"
+                PropertyChanges { ... }
+            }
+        ]
+    }
+```
+
+状态的改变由分配一个元素新的状态属性名来完成。
+
+**注意**
+
+**另一种切换属性的方法是使用状态元素的when属性。when属性能够被设置为一个表达式的结果,当结果为true时,状态被使用**。
+
+```
+    Item {
+        id: root
+        states: [
+            ...
+        ]
+
+        Button {
+            id: goButton
+            ...
+            onClicked: root.state = "go"
+        }
+    }
+```
+
+![](http://qmlbook.org/_images/trafficlight_sketch.png)
+
+例如一个交通信号灯有两个信号灯。上面的一个信号灯使用红色,下面的信号灯使用绿色。在这个例子中,两个信号灯不会同时发光。让我们看看状态图。
+
+![](http://qmlbook.org/_images/trafficlight_states.png)
+
+当系统启动时,它会自动切换到停止模式作为默认状态。停止状态改变了light1为红色并且light2为黑色(关闭)。一个外部的事件能够触发现在的状态变换为“go”状态。在go状态下,我们改变颜色属性,light1变为黑色(关闭),light2变为绿色。
+
+为了实现这个方案,我们给这两个灯绘制一个用户界面的草图,为了简单起见,我们使用两个包含园边的矩形框,设置园半径为宽度的一半(宽度与高度相同)。
+
+```
+    Rectangle {
+        id: light1
+        x: 25; y: 15
+        width: 100; height: width
+        radius: width/2
+        color: "black"
+    }
+
+    Rectangle {
+        id: light2
+        x: 25; y: 135
+        width: 100; height: width
+        radius: width/2
+        color: "black"
+    }
+```
+
+就像在状态图中定义的一样,我们有一个“go”状态和一个“stop”状态,它们将会分别将交通灯改变为红色和绿色。我们设置state属性到stop来确保初始化状态为stop状态。
+
+**注意**
+
+**我们可以只使用“go”状态来达到同样的效果,设置颜色light1为红色,颜色light2为黑色。初始化状态“”(空字符串)定义初始化属性,并且扮演类似“stop”状态的角色。**
+
+```
+    state: "stop"
+
+    states: [
+        State {
+            name: "stop"
+            PropertyChanges { target: light1; color: "red" }
+            PropertyChanges { target: light2; color: "black" }
+        },
+        State {
+            name: "go"
+            PropertyChanges { target: light1; color: "black" }
+            PropertyChanges { target: light2; color: "green" }
+        }
+    ]
+```
+
+PropertyChanges{ target: light2; color: "black" }在这个例子中不是必要的,因为light2初始化颜色已经是黑色了。在一个状态中,只需要描述属性如何从它们的默认状态改变(而不是前一个状态的改变)。
+
+使用鼠标区域覆盖整个交通灯,并且绑定在点击时切换go和stop状态。
+
+```
+    MouseArea {
+        anchors.fill: parent
+        onClicked: parent.state = (parent.state == "stop"? "go" : "stop")
+    }
+```
+
+![](http://qmlbook.org/_images/trafficlight_ui.png)
+
+我们现在已经成功实现了交通灯的状态切换。为了让用户界面看起来更加自然,我们需要使用动画效果来增加一些过渡。一个过渡能够被状态的改变触发。
+
+**注意**
+
+**可以使用一个简单逻辑的脚本来替换QML状态。开发人员很容易落入这种陷阱,写的代码更像一个JavaScript程序而不是一个QML程序。**
+
+## 5.2.2 过渡(Transitions)
+
+一系列的过渡能够被加入任何元素,一个过渡由状态的改变触发执行。你可以使用属性的from:和to:来定义状态改变的指定过渡。这两个属性就像一个过滤器,当过滤器为true时,过渡生效。你也可以使用“*”来表示任何状态。例如from:"*"; to:"*"表示从任一状态到另一个任一状态的默认值,这意味着过渡用于每个状态的切换。
+
+在这个例子中,我们期望从状态“go”到“stop”转换时实现一个颜色改变的动画。对于从“stop”到“go”状态的改变,我们期望保持颜色的直接改变,不使用过渡。我们使用from和to来限制过渡只在从“go”到“stop”时生效。在过渡中我们给每个灯添加两个颜色的动画,这个动画将按照状态的描述来改变属性。
+
+```
+    transitions: [
+        Transition {
+            from: "stop"; to: "go"
+            ColorAnimation { target: light1; properties: "color"; duration: 2000 }
+            ColorAnimation { target: light2; properties: "color"; duration: 2000 }
+        }
+    ]
+```
+
+你可以点击用户界面来改变状态。试试点击用户界面,当状态从“stop”到“go”时,你将会发现改变立刻发生了。
+
+![](http://qmlbook.org/_images/trafficlight_transition.png)
+
+接下来,你可以修改下这个例子,例如缩小未点亮的等来突出点亮的等。为此,你需要在状态中添加一个属性用来缩放,并且操作一个动画来播放缩放属性的过渡。另一个选择是可以添加一个“attention”状态,灯会出现黄色闪烁,为此你需要添加为这个过渡添加一个一秒连续的动画来显示黄色(使用“to”属性来实现,一秒后变为黑色)。也许你也可以改变缓冲曲线来使这个例子更加生动。

+ 0 - 43
quick_starter/README.md

@@ -8,46 +8,3 @@ Quick Starter
 **这章的源代码能够在[assetts folder](http://qmlbook.org/assets)找到。**
 **这章的源代码能够在[assetts folder](http://qmlbook.org/assets)找到。**
 
 
 这章概述了QML语言,Qt5中大量使用了这种声明用户界面的语言。我们将会讨论QML语言,一个树形结构的元素,跟着是一些最基本的元素概述。然后我们会简短的介绍怎样创建我们自己的元素,这些元素被叫做组件,并如何使用属性操作来转换元素。最后我们会介绍如何对元素进行布局,如何向用户提供输入。
 这章概述了QML语言,Qt5中大量使用了这种声明用户界面的语言。我们将会讨论QML语言,一个树形结构的元素,跟着是一些最基本的元素概述。然后我们会简短的介绍怎样创建我们自己的元素,这些元素被叫做组件,并如何使用属性操作来转换元素。最后我们会介绍如何对元素进行布局,如何向用户提供输入。
-
-4.7 输入元素(Input Element)
-
-我们已经使用过MouseArea(鼠标区域)作为鼠标输入元素。这里我们将更多的介绍关于键盘输入的一些东西。我们开始介绍文本编辑的元素:TextInput(文本输入)和TextEdit(文本编辑)。
-4.7.1 文本输入(TextInput)
-
-文本输入允许用户输入一行文本。这个元素支持使用正则表达式验证器来限制输入和输入掩码的模式设置。
-
-
-用户可以通过点击TextInput来改变焦点。为了支持键盘改变焦点,我们可以使用KeyNavigation(按键向导)这个附加属性。
-
-KeyNavigation(按键向导)附加属性可以预先设置一个元素id绑定切换焦点的按键。
-一个文本输入元素(text input element)只显示一个闪烁符和已经输入的文本。用户需要一些可见的修饰来鉴别这是一个输入元素,例如一个简单的矩形框。当你放置一个TextInput(文本输入)在一个元素中时,你需要确保其它的元素能够访问它导出的大多数属性。
-我们提取这一段代码作为我们自己的组件,称作TLineEditV1用来重复使用。
-
-注意
-如果你想要完整的导出TextInput元素,你可以使用property alias input: input来导出这个元素。第一个input是属性名字,第二个input是元素id。
-我们使用TLineEditV1组件重写了我们的KeyNavigation(按键向导)的例子。
-
-
-尝试使用Tab按键来导航,你会发现焦点无法切换到input2上。这个例子中使用focus:true的方法不正确,这个问题是因为焦点被转移到input2元素时,包含TlineEditV1的顶部元素接收了这个焦点并且没有将焦点转发给TextInput(文本输入)。为了防止这个问题,QML提供了FocusScope(焦点区域)。
-4.7.2 焦点区域(FocusScope)
-
-一个焦点区域(focus scope)定义了如果焦点区域接收到焦点,它的最后一个使用focus:true的子元素接收焦点,它将会把焦点传递给最后申请焦点的子元素。我们创建了第二个版本的TLineEdit组件,称作TLineEditV2,使用焦点区域(focus scope)作为根元素。
-
-现在我们的例子将像下面这样:
-
-按下Tab按键可以成功的在两个组件之间切换焦点,并且能够正确的将焦点锁定在组件内部的子元素中。
-4.7.3 文本编辑(TextEdit)
-
-文本编辑(TextEdit)元素与文本输入(TextInput)非常类似,它支持多行文本编辑。它不再支持文本输入的限制,但是提供了已绘制文本的大小查询(paintedHeight,paintedWidth)。我们也创建了一个我们自己的组件TTextEdit,可以编辑它的背景,使用focus scope(焦点区域)来更好的切换焦点。
-
-你可以像下面这样使用这个组件:
-
-
-4.7.4 按键元素(Key Element)
-
-附加属性key允许你基于某个按键的点击来执行代码。例如使用up,down按键来移动一个方块,left,right按键来旋转一个元素,plus,minus按键来缩放一个元素。
-
-
-4.8 高级用法
-
-后续添加。

+ 18 - 18
quick_starter/input_element.md

@@ -101,17 +101,17 @@ Rectangle {
 我们使用TLineEditV1组件重写了我们的KeyNavigation(按键向导)的例子。
 我们使用TLineEditV1组件重写了我们的KeyNavigation(按键向导)的例子。
 
 
 ```
 ```
-Rectangle {
-    ...
-    TLineEditV1 {
-        id: input1
-        ...
-    }
-    TLineEditV1 {
-        id: input2
+    Rectangle {
         ...
         ...
+        TLineEditV1 {
+            id: input1
+            ...
+        }
+        TLineEditV1 {
+            id: input2
+            ...
+        }
     }
     }
-}
 ```
 ```
 
 
 ![](http://qmlbook.org/_images/textinput3.png)
 ![](http://qmlbook.org/_images/textinput3.png)
@@ -151,17 +151,17 @@ FocusScope {
 现在我们的例子将像下面这样:
 现在我们的例子将像下面这样:
 
 
 ```
 ```
-Rectangle {
-    ...
-    TLineEditV2 {
-        id: input1
-        ...
-    }
-    TLineEditV2 {
-        id: input2
+    Rectangle {
         ...
         ...
+        TLineEditV2 {
+            id: input1
+            ...
+        }
+        TLineEditV2 {
+            id: input2
+            ...
+        }
     }
     }
-}
 ```
 ```
 
 
 按下Tab按键可以成功的在两个组件之间切换焦点,并且能够正确的将焦点锁定在组件内部的子元素中。
 按下Tab按键可以成功的在两个组件之间切换焦点,并且能够正确的将焦点锁定在组件内部的子元素中。