我希望实现一个针对QML ListView
的滚动动画。以下是一个示例图片:
请问有谁能为我提供实现方法?
谢谢。
ListView
操作制作动画效果,例如populate
(组件创建时初始项的过渡), add
, remove
(不言自明)以及其他操作。
针对每个要制作动画的操作,在给定ListView
中定义一个Transition
元素。可以利用动画框架来创建复合动画,只需将基本动画组合起来即可创建出(更或者不那么)复杂的行为效果(请参见此处的实际示例)。
这里是一个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]
)和ListView add
上的简单Transition
。
提供定制的滚动效果,就需要考虑到ListView
内部Item
的位置。实现可行解决方案的关键是找到一种方法来计算Item
在视图的可见部分中的当前位置,并使用该值计算适当的转换。 ListView
继承自Flickable
,它具有几个可用于此目的的有用属性。
但是,Item
的y
属性是相对于ListView
内部内容的总高度。为了获得相对于可见区域开头的位置,我们可以使用contentY
属性。在这种情况下,图片胜过千言万语:
y
和contentY
之间的差异提供了一个值,可用于计算所需的转换因子(可能与ListView
的height
相关)。实际上,随着ListView
的快速滑动,两个值及其差异会发生变化,因此特定Item
的转换因子也会发生变化。
这种转换仅涵盖了问题的部分。一旦滑动/移动结束,Item
的动画必须“完成”,以使所有可见的item
可用。为此,我们可以利用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
的缩放因子,而第二个则仅在ListView
未移动时将缩放设置为1
。最后的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
组合应该就足够了。
经过数小时的工作,研究和 @BaCaRoZzo 的巨大帮助(感谢 @BaCaRoZzo),我终于找到了正确的解决方案。只需使用 Component.onCompleted()
事件处理程序来运行与每个委托相关联的动画。
这是一个例子,享受吧!
import QtQuick 2.3
ListView {
anchors.fill: parent
id: list
model: 100
cacheBuffer: 50
delegate: Rectangle {
id: itemDelegate
Component.onCompleted: showAnim.start();
transform: Rotation { id:rt; origin.x: width; origin.y: height; axis { x: 0.3; y: 1; z: 0 } angle: 0}// <--- I like this one more!
width: parent.width
height: 50
color: index % 2 === 0 ? "#EEE" : "#DDD"
SequentialAnimation {
id: showAnim
running: false
RotationAnimation { target: rt; from: 180; to: 0; duration: 800; easing.type: Easing.OutBack; property: "angle" }
}
}
}
PathView
显示从内置 QML 类型(如 ListModel
和 XmlListModel
)或从继承自 QAbstractListModel
的 C++ 自定义模型类创建的模型数据。
视图具有一个模型,该模型定义要显示的数据和一个委托,该委托定义数据应如何显示。对于路径上的每个项目,都会实例化委托。可以轻弹项目以沿路径移动它们。
PathView
…… 它有点复杂,但足以满足用例。 不建议在ListView
中执行此操作,因为您需要在委托中保存状态(如BaCaRoZzo所建议的),这是不良做法。 - נירPathView
是一个循环列表。在这种情况下它是无用的。 - S.M.Mousavi