ViewTransition 提供了很多有趣的示例,说明如何为ListView 等操作设置动画,例如populate(组件创建时的初始项的转换)、add、remove(不言自明) ) 以及其他操作。
给定一个ListView,您可以为每个要设置动画的操作定义一个元素Transition。 The animation framework 可用于创建复合动画,只需简单地组合基本动画即可创建(或多或少)您感兴趣的复杂行为(另请参阅 here 以获取实际示例)。
这里是ListView 的定义(第一个链接文档提供了一些漂亮的图像):
ListView {
// data model, delegate, other usual stuff here...
// transitions for insertion/deletation of elements
add: Transition {
NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 500 }
NumberAnimation { property: "scale"; easing.type: Easing.OutBounce; from: 0; to: 1.0; duration: 750 }
}
addDisplaced: Transition {
NumberAnimation { properties: "y"; duration: 600; easing.type: Easing.InBack }
}
remove: Transition {
NumberAnimation { property: "scale"; from: 1.0; to: 0; duration: 200 }
NumberAnimation { property: "opacity"; from: 1.0; to: 0; duration: 200 }
}
removeDisplaced: Transition {
NumberAnimation { properties: "x,y"; duration: 500; easing.type: Easing.OutBack }
}
}
最后,请注意,可以通过使用着色器并结合元素上的动画和委托/委托的元素上的过渡来获得一些行为。一个很好的例子是Tweet Search,其中条形项目上的阴影效果(参见[ShaderEffect][5])与ListViewadd 上的简单Transition 相结合。
编辑
提供像示例中一样的自定义滚动,需要考虑Items 在ListView 中的位置。一个可行的解决方案的关键是找到一种方法来计算Item 在视图的可见部分 内的当前位置,并使用该值来计算适当的转换。 ListView 派生自 Flickable,它为此目的具有几个有用的属性。
但是,Item 的y 属性是指ListView 内内容的整体高度。有它的位置 w.r.t.可见区域的开头我们可以使用contentY 属性。在这种情况下,一张图片值一千字:
y 和contentY 之间的差异提供了一个值,可用于计算所需的转换因子(可能与height 的ListView 相关)。实际上,随着ListView 的轻弹,这两个值及其差异会发生变化,因此会更改特定Item 的转换因子。
这样的转换只涵盖了问题的部分。一旦轻弹/移动结束,Items 动画必须“完成”以使所有可见的items 可用。为此,我们可以利用Binding 及其when 属性仅在需要时激活结束动画,即当flicking 或dragging 结束时。
鉴于所有这些(无聊的)介绍,让我们考虑第二个动画(更简单的动画)。这里我们可以使用scale来获得想要的效果。 ListView 中的 delegate 代码如下所示:
ListView {
id: list
model: 100
spacing: 10
delegate: Rectangle {
id: itemDelegate
property int listY: y - list.contentY // stores the difference between the two values
width: parent.width
height: 50
border.color: "lightgray"
color: "red"
Binding {
target: itemDelegate
property: "scale"
value: 1 - listY / list.height / 2 // the "scale" property accepts values in the range [0, 1]
when: list.moving || list.flicking || list.dragging // ...when moved around
}
Binding {
target: itemDelegate
property: "scale"
value: 1 // flick finished --> scale to full size!
when: !(list.moving || list.dragging) // not moving or dragging any more
}
Behavior on scale {
NumberAnimation { duration: 100; to: 1}
enabled: !(list.flicking || list.dragging) // active only when flick or dragging ends!
}
}
}
第一个Binding 定义基于listY 的缩放因子,而第二个将缩放设置为1,但仅当ListView 不移动时。最后的Behavior 是平滑过渡到完全缩放的Item 所必需的。
第三个效果可以用Rotation类似的方式获得:
ListView {
anchors.fill: parent
id: list
spacing: 10
model: 100
delegate: Rectangle {
id: itemDelegate
property int listY: y - list.contentY
property real angleZ: (90 * listY) / list.height // 0 - 90 degrees
transform: Rotation { origin.x: width / 2; origin.y: 30; axis { x: 1; y: 0; z: 0 } angle: angleZ}
//transform: Rotation { origin.x: 0; origin.y: 30; axis { x: 1; y: 1; z: 0 } angle: angleZ} <--- I like this one more!
width: parent.width
height: 50
border.color: "lightgray"
color: "red"
Binding {
target: itemDelegate
property: "angleZ"
value: 0
when: !(list.moving || list.dragging)
}
Behavior on angleZ {
NumberAnimation {duration: 200; to: 0}
enabled: !(list.flicking || list.dragging)
}
}
}
这一次我选择(任意)只使用一个Binding。 同样可以在第一个示例中进行,即我们可以在第一个委托scale: 1 - listY / list.height / 2中编写。
按照类似的方法,您还可以创建第一个动画和其他动画。对于第一个动画,我认为将 Rotation 与 Translate 组合就足够了。