玩转Qml(13)-动画特效-飞入
本文于
1032
天之前发表,文中内容可能已经过时。
简介
这次涛哥将会教大家一些Qml动画相关的知识。
源码
《玩转Qml》系列文章,配套了一个优秀的开源项目:TaoQuick
github https://github.com/jaredtao/TaoQuick
访问不了或者速度太慢,可以用国内的镜像网站gitee
https://gitee.com/jaredtao/TaoQuick
飞入效果预览
第一篇文章,就放一个简单的动画效果
实现原理
进场动画,使用了QtQuick的动画系统,以及ShaderEffect特效。
Qml中有一个模块QtGraphicalEffects,提供了部分特效,就是使用ShaderEffect实现的。
使用ShaderEffect实现特效,需要有一些OpenGL/DirectX知识,了解GPU渲染管线,同时也需要一些数学知识。
QtQuick动画系统
动画组件
Qt动画系统,在帮助文档有详细的介绍,搜索关键词”Animation”,涛哥在这里说一些重点。
涛哥用思维导图列出了Qml中所有的动画组件:
右边带虚线框的部分比较常用,是做动画必须要掌握的,尤其是属性动画PropertyAnimation和数值动画NumberAinmation。
常见的各种坐标动画、宽高动画、透明度动画、颜色动画等等,都可以用这些组件来实现。
底下的States、Behavior 和 Traisitions,也是比较常用的和动画相关的组件。可在帮助文档搜索
关键词”Qt Quick States”、”Behavior”、”Animation and Transitions”。后续的文章,涛哥会专门讲解。
左边的Animator系列,属于Scene Graph渲染层面的优化,其属性Change信号只在最终值时发出,不发出中间值,使用的时候需要注意。
顶上的AnimationController,属于高端玩家,用来控制整个动画的进度。
动画的使用
用例一 直接声明动画
直接声明动画,指定target和property,之后可以在槽函数/js脚本中通过id控制动画的运行。
也可以通过设定loops 和 running属性来控制动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| Rectangle { id: flashingblob width: 75; height: 75 color: "blue" opacity: 1.0
MouseArea { anchors.fill: parent onClicked: { animateColor.start() animateOpacity.start() } }
PropertyAnimation {id: animateColor; target: flashingblob; properties: "color"; to: "green"; duration: 100}
NumberAnimation { id: animateOpacity target: flashingblob properties: "opacity" from: 0.99 to: 1.0 loops: Animation.Infinite easing {type: Easing.OutBack; overshoot: 500} } }
|
用例二 on语法
on语法可以使用动画组件,也可以用Behavior,直接on某个特定的属性即可。效果一样。
on动画中,如果直接指定了running属性,默认就会执行这个动画。
也可以不指定running属性,其它地方修改这个属性时,会自动按照动画来执行。
示例代码 on动画
1 2 3 4 5 6 7 8 9
| Rectangle { width: 100; height: 100; color: "green" RotationAnimation on rotation { loops: Animation.Infinite from: 0 to: 360 running: true } }
|
示例代码 Behavior 动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import QtQuick 2.0
Rectangle { id: rect width: 100; height: 100 color: "red"
Behavior on width { NumberAnimation { duration: 1000 } }
MouseArea { anchors.fill: parent onClicked: rect.width = 50 } }
|
用例三 Transitions或状态机
过渡动画和状态机动画,本质还是直接使用动画组件。只不过是把动画声明并存储起来,以在状态切换时使用。
这里先不细说了,后面会有系列文章<Qml特效-页面切换动画>,会专门讲解。
ShaderEffect
动画只能控制组件的属性整体的变化,做特效需要精确到像素。
Qml中提供了ShaderEffect这个组件,就能实现像素级别的操作。
大名鼎鼎的ShaderToy网站,就是使用Shader实现各种像素级别的酷炫特效。
ShaderToy
作者iq大神
ShaderToy上面的特效都是可以移植到Qml中的。
使用Shader开发,需要一定的图形学知识。其中使用GLSL需要熟悉OpenGL, 使用HLSL需要熟悉DirectX。
飞入效果源码
封装了一个平移进入的动画组件,能够支持从四个方向进场。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import QtQuick 2.12 import QtQuick.Controls 2.12 import "../.." Item { id: r property int targetX: 0 property int targetY: 0 property alias animation: animation enum Direct { FromLeft = 0, FromRight = 1, FromTop = 2, FromBottom = 3 } property int dir: ASlowEnter.Direct.FromBottom property int duration: 2000 property int extDistance: 10 property var __propList: ["x", "x", "y", "y"] property var __fromList: [ -r.parent.width - r.width - extDistance, r.parent.width + r.width + extDistance, -r.parent.height - r.height - extDistance, r.parent.height + r.height + extDistance] property var __toList: [targetX, targetX, targetY, targetY] NumberAnimation { id: animation target: r property: __propList[dir] from: __fromList[dir] to: __toList[dir] duration: r.duration loops: 1 alwaysRunToEnd: true } }
|
进场组件的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
|
import QtQuick 2.12 import QtQuick.Controls 2.12 import "../Animation/Enter" Item { anchors.fill: parent ASlowEnter { id: a1 width: 160 height: 108 x: (parent.width - width) / 2 targetY: parent.height / 2 dir: ASlowEnter.Direct.FromBottom Image { anchors.fill: parent source: "qrc:/EffectImage/Img/baby.jpg" } } ASlowEnter { id: a2 width: 160 height: 108 x: (parent.width - width) / 2 targetY: parent.height / 2 - height dir: ASlowEnter.Direct.FromTop Image { anchors.fill: parent source: "qrc:/EffectImage/Img/baby.jpg" } } ASlowEnter { id: a3 width: 160 height: 108 targetX: parent.width / 2 - width * 1.5 y: (parent.height - height) / 2 dir: ASlowEnter.Direct.FromLeft Image { anchors.fill: parent source: "qrc:/EffectImage/Img/baby.jpg" } } ASlowEnter { id: a4 width: 160 height: 108 targetX: parent.width / 2 + width / 2 y: (parent.height - height) / 2 dir: ASlowEnter.Direct.FromRight Image { anchors.fill: parent source: "qrc:/EffectImage/Img/baby.jpg" } } ParallelAnimation { id: ani ScriptAction{ script: {a1.animation.restart()} } ScriptAction{ script: {a2.animation.restart()} } ScriptAction{ script: {a3.animation.restart()} } ScriptAction{ script: {a4.animation.restart()} } } Component.onCompleted: { ani.restart() } Button { anchors.right: parent.right anchors.bottom: parent.bottom text: "replay" onClicked: { ani.restart() } } }
|