动画被用于属性的改变。一个动画定义了属性值改变的曲线,将一个属性值变化从一个值过渡到另一个值。动画是由一连串的目标属性活动定义的,平缓的曲线算法能够引发一个定义时间内属性的持续变化。所有在QtQuick中的动画都由同一个计时器来控制,因此它们始终都保持同步,这也提高了动画的性能和显示效果。 先抛个砖头,简单的效果图如下:
这个效果图的代码我更书中有点不一样,书中只能移动一次,而且移动会有锯齿现象,书中代码效果:
对比一下就会发现一些问题了,书中对应的代码:
import QtQuick 2.5 Image { id: root source: "assets/background.png" property int padding: 40 property int duration: 4000 property bool running: false Image { id: box x: root.padding y: (root.height - height) / 2 source: "assets/box_green.png" NumberAnimation on x { to: root.width - box.width - root.padding duration: root.duration running: root.running } RotationAnimation on rotation { to: 360 duration: root.duration running: root.running } } MouseArea { anchors.fill: parent onClicked: root.running = true } }我在此基础上添加了重置的功能,与平滑的功能,效果就不一样了,大家对比一下吧:
import QtQuick 2.12 import QtQuick.Window 2.12 Image { id: root source: "assets/background.png" property int padding: 40 property int duration: 4000 property bool running: false MouseArea { anchors.fill: parent onClicked: root.running = true } Image { id: box x: root.padding y: (root.height - height) / 2 source: "assets/box_green.png" antialiasing: true //可以减少锯齿的样子 NumberAnimation on x { to: root.width - box.width - root.padding duration: root.duration //延时4000 running: root.running } RotationAnimation on rotation { to: 360 duration: root.duration running: root.running } } MouseArea { id: myArea anchors.fill: box onClicked: { root.running = false box.x = root.padding box.y = (root.height - height) / 2 box.rotation = 0 } } }有几种类型的动画,每一种都在特定情况下都有最佳的效果,下面列出了一些常用的动画:
PropertyAnimation(属性动画) - 使用属性值改变播放的动画NumberAnimation(数字动画) - 使用数字改变播放的动画ColorAnimation(颜色动画) - 使用颜色改变播放的动画RotationAnimation(旋转动画) - 使用旋转改变播放的动画PauseAnimation(停止动画) - 运行暂停一个动画SequentialAnimation(顺序动画) - 允许动画有序播放ParallelAnimation(并行动画) - 允许动画同时播放AnchorAnimation(锚定动画) - 使用锚定改变播放的动画ParentAnimation(父元素动画) - 使用父对象改变播放的动画SmotthedAnimation(平滑动画) - 跟踪一个平滑值播放的动画SpringAnimation(弹簧动画) - 跟踪一个弹簧变换的值播放的动画PathAnimation(路径动画) - 跟踪一个元素对象的路径的动画Vector3dAnimation(3D容器动画) - 使用QVector3d值改变播放的动
QtQuick提供了一个动作元素:
PropertyAction(属性动作) - 在播放动画时改变属性ScriptAction(脚本动作) - 在播放动画时运行脚本动画可以通过以下几种方式来应用:
属性动画 - 在元素完整加载后自动运行属性动作 - 当属性值改变时自动运行独立运行动画 - 使用start()函数明确指定运行或者running属性被设置为true(比如通过属性绑定)扩展可点击图像元素版本2(ClickableImage Version2) 创建ClickableImageV2.qml文件:
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: "#ececec" } } MouseArea { anchors.fill: parent onClicked: root.clicked() } }为了给图片下面的元素定位,我们使用了Column(列) 定位器,并且使用基于列的子矩形(childRect) 属性来计算它的宽度和高度(width and height) 。我们导出了文本(text) 和图形源(source) 属性,一个点击信号(clicked signal) 。我们使用文本元素的wrapMode属性来设置文本与图像一样宽并且可以自动换行。
创建应用文件:
import QtQuick 2.0 Item { id: root width: background.width height: background.height Image { id: background source: "assets/background_medium.png" } MouseArea { anchors.fill: parent onClicked: { greenBox.y = blueBox.y = redBox.y = 205 } } ClickableImageV2 { id: greenBox x: 40 y: root.height - height source: "assets/box_green.png" text: "animation on property" NumberAnimation on y { to: 40 duration: 4000 } } ClickableImageV2 { id: blueBox x: (root.width - width) / 2 y: root.height - height source: "assets/box_blue.png" text: "behavior on property" Behavior on y { NumberAnimation { duration: 4000 } } //onClicked: y = 40 onClicked: y = 40 + Math.random() * (205 - 40) } ClickableImageV2 { id: redBox x: root.width - width - 40 y: root.height - height source: "assets/box_red.png" onClicked: anim.start() // onClicked: anim.restart() text: "standalone animation" NumberAnimation { id: anim target: redBox properties: "y" to: 40 duration: 4000 } } }效果图:
第一个火箭使用了Animation on 属性变化的策略来完成。动画会在加载完成后立即播放。点击火箭可以重置它回到开始的位置。在动画播放时重置第一个火箭不会有任何影响。在动画开始前的几分之一秒设置一个新的y轴坐标让人感觉挺不安全 的,应当避免这样的属性值竞争的变化。
第二个火箭使用了behavior on 属性行为策略的动画。这个行为告诉属性值每时每刻都在变化,通过动画的方式来改变这个值。可以使用行为元素的enabled : false来设置行为失效。当你点击这个火箭时它将会开始运行(y轴坐标逐渐移至40) 。然后其它的点击对于位置的改变没有任何的影响。你可以试着使用一个随机值(例如 40+(Math.random()*(205-40)) 来设置y轴坐标。你可以发现动画始终会将移动到新位置的时间匹配在4秒内完成。
第三个火箭使用standalone animation独立动画策略。这个动画由一个私有的元素定义并且可以写在文档的任何地方。点击火箭调用动画函数start()来启动动画。每一个动画都有start(),stop(),resume(),restart()函数。这个动画自身可以比其他类型的动画更早的获取到更多的相关信息。我们只需要定义目标和目标元素的属性需要怎样改变的一个动画。我们定义一个to属性的值,在这个例子中我们也定义了一个from属性的值允许动画可以重复运行。
属性值的改变能够通过一个动画来控制,缓冲曲线属性影响了一个属性值改变的插值算法。我们现在已经定义的动画都使用了一种线性的插值算法,因为一个动画的默认缓冲类型是Easing.Linear。在一个小场景下的x轴与y轴坐标改变可以得到最好的视觉效果。一个线性插值算法将会在动画开始时使用from的值到动画结束时使用的to值绘制一条直线,所以缓冲类型定义了曲线的变化情况。精心为一个移动的对象挑选一个合适的缓冲类型将会使界面更加自然,例如一个页面的滑出,最初使用缓慢的速度滑出,然后在最后滑出时使用高速滑出,类似翻书一样的效果。 创建一个DarkSquare.qml
import QtQuick 2.5 Rectangle { width: 48 height: 48 color: "#3c3c3c" border.color: Qt.darker(color) }在创建一个GreenSquare.qml文件:
import QtQuick 2.5 Rectangle { width: 48 height: 48 color: "#67c111" border.color: Qt.lighter(color) }改变一下ClickableImageV2.qml文件
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 property bool framed: false property alias labelColor :label.color Rectangle { anchors.fill: parent color: "white" visible: root.framed } Column { x: 8 y: 8 id: container Image { id: image } Text { id: label width: image.width horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap //与图像一样宽并且可以自动换行 color: "#ececec" } } MouseArea { anchors.fill: parent onClicked: root.clicked() } }然后创建Easing.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 ClickableImageV2{ framed: true text: modelData labelColor: "black" 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) 来查看一个缓冲曲线的相关参数。
效果图:
每个缓冲的效果都在图中显示出来了,我把字体变成黑一点了,书中的字体显示出来太暗,看不清楚。