作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
在使用 QML 进行界面开发时,我们的目标是创建一套流体界面,所谓流体界面指的是UI组件是动态变化的。举个例子,当界面上的组件需要变化时,如果视觉画布突然变化会导致用户的体验感比较差。而如果在状态的变化过程中,我们添加一些引导,把状态从初始慢慢变化到目标状态,让用户可以感受到这个变化的过程,那么用户的感官体验这一块就会大大提升了,而这也就是所谓的动态变化
这里需要引出几个重要的概念:State(状态)、Transition(过度)、Animation(动画)
具体请参考官方文档:Important Concepts in Qt Quick - States, Transitions and Animations
通常动画中的变化是匀速的,如果开发者觉得太单调了,可以调节动画的变化速度曲线,即缓和曲线。我们通过动画的 easing 属性组来改变缓和曲线
缓和曲线的类型如下所示:
Rectangle {
id: myRect
width: 100; height: 100
color: "red"
property int type: 1
states: [
State {
name: "right"
AnchorChanges { target: myRect; anchors.right: parent.right }
},
State {
name: "bottom"
AnchorChanges { target: myRect; anchors.bottom: parent.bottom }
},
State {
name: "left"
AnchorChanges { target: myRect; anchors.left: parent.left }
},
State {
name: "top"
AnchorChanges { target: myRect; anchors.top: parent.top }
}
]
transitions: Transition {
AnchorAnimation { duration: 500 }
}
MouseArea {
anchors.fill: parent
onClicked: {
let state = {
1: "right",
2: "bottom",
3: "left",
4: "top"
}
myRect.state = state[myRect.type]
myRect.type++
if ( myRect.type > 4 ) {
myRect.type = 1
}
}
}
}
Rectangle {
id: myRect
width: 100; height: 100
color: "black"
property int type: 1
states: [
State {
name: "red"
PropertyChanges { target: myRect; color: "red" }
},
State {
name: "yellow"
PropertyChanges { target: myRect; color: "yellow" }
},
State {
name: "pink"
PropertyChanges { target: myRect; color: "pink" }
},
State {
name: "blue"
PropertyChanges { target: myRect; color: "blue" }
},
State {
name: "black"
PropertyChanges { target: myRect; color: "black" }
}
]
transitions: Transition {
ColorAnimation { duration: 500 }
}
MouseArea {
anchors.fill: parent
onClicked: {
let state = {
1: "red",
2: "yellow",
3: "pink",
4: "blue",
5: "black",
}
myRect.state = state[myRect.type]
myRect.type++
if ( myRect.type > 5 ) {
myRect.type = 1
}
}
}
}
Rectangle {
id: myRect
width: 100; height: 100
color: "red"
property int type: 1
states: [
State {
name: "step1"
PropertyChanges { target: myRect; x: 60; y: 60; width: 120; height: 120; rotation: 30 }
},
State {
name: "step2"
PropertyChanges { target: myRect; x: 60; y: 60; width: 120; height: 120; rotation: 60 }
},
State {
name: "step3"
PropertyChanges { target: myRect; x: 40; y: 40; width: 180; height: 180; rotation: 90 }
},
State {
name: "step4"
PropertyChanges { target: myRect; x: 100; y: 100; width: 50; height: 50; rotation: 45 }
}
]
transitions: Transition {
NumberAnimation { duration: 500 }
}
MouseArea {
anchors.fill: parent
onClicked: {
let state = {
1: "step1",
2: "step2",
3: "step3",
4: "step4"
}
myRect.state = state[myRect.type]
myRect.type++
if ( myRect.type > 4 ) {
myRect.type = 1
}
}
}
}
Rectangle {
id: myRect
width: 300; height: 100
color: "black"
property int type: 1
Rectangle {
id: redRect
width: 100; height: 100
color: "red"
}
Rectangle {
id: blueRect
x: 110
width: 100; height: 100
color: "blue"
}
Rectangle {
id: yellowRect
x: 220; y: 10
width: 50; height: 50
color: "yellow"
states: [
State {
name: "red"
ParentChange { target: yellowRect; parent: redRect; x: 10; y: 10 }
},
State {
name: "blue"
ParentChange { target: yellowRect; parent: blueRect; x: 10; y: 10 }
},
State {
name: "black"
ParentChange { target: yellowRect; parent: myRect; x: 220; y: 10 }
}
]
transitions: Transition {
ParentAnimation {
NumberAnimation { properties: "x,y"; duration: 500 }
}
}
MouseArea {
anchors.fill: parent
onClicked: {
let state = {
1: "red",
2: "blue",
3: "black"
}
yellowRect.state = state[myRect.type]
myRect.type++
if ( myRect.type > 3 ) {
myRect.type = 1
}
}
}
}
}
Rectangle {
width: 400; height: 400
PathInterpolator {
id: motionPath
path: Path {
startX: 0; startY: 0
PathCubic {
x: 350; y: 350
control1X: 350; control1Y: 0
control2X: 0; control2Y: 350
}
}
NumberAnimation on progress { id: animation; from: 0; to: 1; duration: 2000 }
}
Rectangle {
x: motionPath.x; y: motionPath.y
width: 50; height: 50
rotation: motionPath.angle
color: "green"
}
MouseArea {
anchors.fill: parent
onClicked: animation.start()
}
}
Rectangle {
width: 100; height: 100
color: "red"
property bool location: true
Behavior on x { PropertyAnimation {} }
MouseArea {
anchors.fill: parent
onClicked: {
var x = location ? 50 : 0
parent.x = x
location = !location
}
}
}
Item {
width: 300; height: 300
Rectangle {
id: myRect
width: 150; height: 100
anchors.centerIn: parent
color: "red"
antialiasing: true
property int type: 1
states: [
State {
name: "state1"
PropertyChanges { target: myRect; rotation: 45 }
},
State {
name: "state2"
PropertyChanges { target: myRect; rotation: 90 }
},
State {
name: "state3"
PropertyChanges { target: myRect; rotation: 180 }
}
]
transitions: Transition {
RotationAnimation { duration: 1000; direction: RotationAnimation.Counterclockwise }
}
}
MouseArea {
anchors.fill: parent
onClicked: {
let state = {
1: "state1",
2: "state2",
3: "state3"
}
myRect.state = state[myRect.type]
myRect.type++
if ( myRect.type > 3 ) {
myRect.type = 1
}
}
}
}
Rectangle {
id: myRect
x: 50; y: 50
width: 100; height: 100
color: "red"
transform: Rotation {
angle: 45
origin.x: 50; origin.y: 50
axis: Qt.vector3d(0, 1, 0)
SequentialAnimation on axis {
id: animation
running: false
Vector3dAnimation { from: "1, 0, 0"; to: "0, 1, 0"; duration: 1000 }
Vector3dAnimation { from: "0, 1, 0"; to: "0, 0, 1"; duration: 1000 }
Vector3dAnimation { from: "0, 0, 1"; to: "1, 0, 1"; duration: 1000 }
Vector3dAnimation { from: "1, 0, 1"; to: "1, 1, 0"; duration: 1000 }
Vector3dAnimation { from: "1, 1, 0"; to: "1, 1, 1"; duration: 1000 }
Vector3dAnimation { from: "1, 1, 1"; to: "1, 0, 1"; duration: 1000 }
Vector3dAnimation { from: "1, 0, 1"; to: "0, 0, 1"; duration: 1000 }
Vector3dAnimation { from: "0, 0, 1"; to: "0, 1, 0"; duration: 1000 }
Vector3dAnimation { from: "0, 1, 0"; to: "1, 0, 0"; duration: 1000 }
}
}
MouseArea {
anchors.fill: parent
onClicked: animation.start()
}
}
Rectangle {
id: rect
width: 100; height: 100
color: "red"
SequentialAnimation {
id: animation
running: false
NumberAnimation { target: rect; property: "x"; to: 100; duration: 500 }
NumberAnimation { target: rect; property: "y"; to: 100; duration: 500 }
NumberAnimation { target: rect; property: "x"; to: 0; duration: 500 }
NumberAnimation { target: rect; property: "y"; to: 0; duration: 500 }
}
MouseArea {
anchors.fill: parent
onClicked: animation.start()
}
}
Rectangle {
id: rect
width: 100; height: 100
color: "red"
ParallelAnimation {
id: animation
running: false
NumberAnimation { target: rect; property: "x"; to: 100; duration: 1000 }
NumberAnimation { target: rect; property: "y"; to: 100; duration: 1000 }
NumberAnimation { target: rect; property: "width"; to: 200; duration: 1000 }
NumberAnimation { target: rect; property: "height"; to: 200; duration: 1000 }
NumberAnimation { target: rect; property: "rotation"; to: 90; duration: 1000 }
}
MouseArea {
anchors.fill: parent
onClicked: animation.start()
}
}
Rectangle {
id: coloredRect
width: 100; height: 100
anchors.centerIn: parent
color: "red"
states: State {
name: "GreenState"
when: mouser.containsMouse
PropertyChanges {
target: coloredRect
color: "green"
}
}
Behavior on color { ColorAnimation {} }
MouseArea {
id: mouser
anchors.fill: parent
hoverEnabled: true
}
}
Rectangle {
id: rect
width: 100; height: 100
color: "red"
SequentialAnimation {
id: animation
running: false
NumberAnimation { target: rect; property: "x"; to: 100; duration: 500 }
NumberAnimation { target: rect; property: "y"; to: 100; duration: 500 }
PauseAnimation { duration: 1000 }
NumberAnimation { target: rect; property: "x"; to: 0; duration: 500 }
NumberAnimation { target: rect; property: "y"; to: 0; duration: 500 }
}
MouseArea {
anchors.fill: parent
onClicked: animation.start()
}
}
Rectangle {
id: rect
width: 50; height: 50
color: "red"
Behavior on x { SpringAnimation { spring: 2; damping: 0.2 } }
Behavior on y { SpringAnimation { spring: 2; damping: 0.2 } }
MouseArea {
anchors.fill: parent
onClicked: {
rect.x = mouse.x - rect.width/2
rect.y = mouse.y - rect.height/2
}
}
}
Item {
Rectangle {
width: 60; height: 60
x: rect1.x - 5; y: rect1.y - 5
color: "green"
Behavior on x { SmoothedAnimation { velocity: 200; duration: 500 } }
Behavior on y { SmoothedAnimation { velocity: 200; duration: 500 } }
}
Rectangle {
id: rect1
x: 5; y: 5
width: 50; height: 50
color: "red"
}
focus: true
Keys.onRightPressed: rect1.x = rect1.x + 100
Keys.onLeftPressed: rect1.x = rect1.x - 100
Keys.onUpPressed: rect1.y = rect1.y - 100
Keys.onDownPressed: rect1.y = rect1.y + 100
}
Rectangle {
id: rect
width: 100; height: 100
color: "red"
SequentialAnimation {
id: animation
running: false
NumberAnimation { target: rect; property: "x"; to: 100; duration: 500 }
NumberAnimation { target: rect; property: "y"; to: 100; duration: 500 }
ScriptAction { script: doSomething() }
NumberAnimation { target: rect; property: "x"; to: 0; duration: 500 }
NumberAnimation { target: rect; property: "y"; to: 0; duration: 500 }
ScriptAction { script: doStateStuff() }
}
MouseArea {
anchors.fill: parent
onClicked: animation.start()
}
function doSomething() {
rect.color = "blue"
rect.width = 120
rect.height = 120
}
function doStateStuff() {
rect.color = "red"
rect.width = 100
rect.height = 100
}
}