ListView 滚动动画

10

我希望实现一个针对QML ListView 的滚动动画。以下是一个示例图片:
ListView scrolling animation
请问有谁能为我提供实现方法?

谢谢。


只需使用 PathView…… 它有点复杂,但足以满足用例。 不建议在 ListView 中执行此操作,因为您需要在委托中保存状态(如BaCaRoZzo所建议的),这是不良做法。 - ניר
@TOHO PathView 是一个循环列表。在这种情况下它是无用的。 - S.M.Mousavi
3个回答

19
ViewTransition提供了许多有趣的示例,演示了如何为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,它具有几个可用于此目的的有用属性。

但是,Itemy属性是相对于ListView内部内容的总高度。为了获得相对于可见区域开头的位置,我们可以使用contentY属性。在这种情况下,图片胜过千言万语:

enter image description here

ycontentY之间的差异提供了一个值,可用于计算所需的转换因子(可能与ListViewheight相关)。实际上,随着ListView的快速滑动,两个值及其差异会发生变化,因此特定Item的转换因子也会发生变化。

这种转换仅涵盖了问题的部分。一旦滑动/移动结束,Item的动画必须“完成”,以使所有可见的item可用。为此,我们可以利用Binding及其when属性,仅在需要时激活完成动画,即当flickingdragging结束时。

在考虑第二个(较简单)动画时,我们可以使用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

通过类似的方法,您还可以创建第一个动画和其他动画。对于第一个动画,我认为将RotationTranslate组合应该就足够了。


我知道ListView和ViewTransition。请注意,我想要滚动动画,而不仅仅是添加/删除/...位移动画。你能给我建议吗? - S.M.Mousavi
抱歉,看来我完全误解了。关于问题的主要点,我已经调查了相同的功能,并记得一个有趣的例子。我一找到它就会发布链接(并重新编辑答案)。再次抱歉。 - BaCaRoZzo
1
@S.M.Mousavi 对于延迟我表示抱歉。我所提供的示例仅用于所选项目,因此没有逐渐过渡的效果。我不在办公室,最后但并非最不重要的是,我在“y”上遇到了一些问题(当时已经很晚了……详见此处,最终非常感谢Jens)。无论如何,我希望这种方法能够满足您的需求。 - BaCaRoZzo
1
非常感谢您,您让我对这个问题有了更好的理解。我已经分享了我的最终解决方案给您和其他人。我很想给您一些分数,但似乎没有任何方法:( 再次感谢。 - S.M.Mousavi
很好。写答案很有趣,你的解决方案给了我一些好的想法。正确答案加1分。 :) - BaCaRoZzo

12

经过数小时的工作,研究和 @BaCaRoZzo 的巨大帮助(感谢 @BaCaRoZzo),我终于找到了正确的解决方案。只需使用 Component.onCompleted() 事件处理程序来运行与每个委托相关联的动画。

这是一个例子,享受吧!

QML上的滚动动画

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" }
        }
    }
}

1
是的,很棒的解决方案!我仍然没有完全满足您的要求,但至少我有所帮助,我为此感到高兴。 :) 此外,我一定会尝试使用您的解决方案。喜欢它。 :) - BaCaRoZzo

-1

PathView 显示从内置 QML 类型(如 ListModelXmlListModel)或从继承自 QAbstractListModel 的 C++ 自定义模型类创建的模型数据。 视图具有一个模型,该模型定义要显示的数据和一个委托,该委托定义数据应如何显示。对于路径上的每个项目,都会实例化委托。可以轻弹项目以沿路径移动它们。


谢谢。我已经检查过了。它似乎适用于分辨率问题。但是滚动动画有不同的行为。在滚动时,不可见的项目必须变为可见,并以动画方式填充列表的可见区域。PathView没有足够的行为并且无法解决这个问题:\。请看一下示例视频http://codecanyon.net/item/android-listview-animations/4179731 - S.M.Mousavi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接