10

I want to implement a scrolling animation for QML ListView. Here is a sample image:
ListView scrolling animation
Can anybody advise me for implementing this?

Thank you.

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
S.M.Mousavi
  • 5,013
  • 7
  • 44
  • 59
  • Just use a `PathView`... it is a little more complicated but will suffice the use case. doing it in a `ListView` IS NOT recommended because you will need to save state in the delegate (as suggested by BaCaRoZzo) which is bad practice. – ניר May 08 '22 at 12:36
  • @TOHO The `PathView` is a looping list. It is useless for this scenario. – S.M.Mousavi May 09 '22 at 06:42

3 Answers3

19

The ViewTransition provides a lot of interesting examples on how to animate a ListView for operations like populate (the transition for the initial items at component creation), add, remove (self-explanatory) as well as other operations.

Given a ListView you define an element Transition for each operation you want to animate. The animation framework can be exploited to create compound animations, by simply combining the basic animations to create the (more or less) complex behaviour you are interested in (see also here for an actual example).

Here a definition for a ListView (the first linked document provides some nice images):

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

Finally, note that some behaviours can be obtained by using Shaders and combining animation on the elements and transitions on the delegate/elements of the delegate. A nice example is Tweet Search, in which a shading effect (see [ShaderEffect][5]) on the bar item is combined with a simple Transition on ListView add.

EDIT

Provide a customized scrolling like in the examples requires to take in account the position of the Items inside the ListView. A key to a working solution is to find a way to calculate the current position of the Item inside the visible part of the view and use that value to calculate the appropriate transformation. ListView derives from Flickable which has several useful properties for this purpose.

However, the y property of the Item is referred to the overall height of the content inside the ListView. To have its position w.r.t. the beginning of the visible area we can use the contentY property. A picture is worth a thousand words in this case:

enter image description here

The difference between y and contentY provides a value which can be used to calculate the required transformation factor (maybe in relation to the height of the ListView). Indeed, as the ListView is flicked, the two values and their difference change and so changes the transformation factor for a specific Item.

Such transformation covers only part of the problem. Once the flicking/movement ends the Items animation must be "finished" to make all the visible items usable. For this purpose we can exploit Binding and its when property to activate the finishing animation only when required, i.e. when flicking or dragging ends.

Given all this (boring) introduction, let's take in account the second animation (the simpler one). Here we can use scale to obtain the desired effect. The delegate code inside the ListView looks like the following:

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

The first Binding define the scaling factor on the basis of listY whereas the second one set the scaling to 1 but only when the ListView is not moving. The final Behavior is necessary to smooth the transition to the fully scaled Item.

The third effect can be obtained in a similar fashion with a 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)
        }
    }
}

This time I've choosen to (arbitrarily) use only one Binding. The same could have been made for the first example, i.e. we could have written in the first delegate scale: 1 - listY / list.height / 2.

Following a similar approach you can also create the first animation and others. For the first animation I think that combining a Rotation with a Translate should suffice.

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
  • I know about ListView and ViewTransition. Please note that I want SCROLLING ANIMATION not just add/remove/... displace animation. Can you advise me? – S.M.Mousavi Nov 22 '14 at 20:26
  • Sorry, It seems I've totally miss the point then. For what concerns the main point of the question, I've investigated the same functionaly and I remember an interesting example. I'm going to post a link (and reedit the answer) as soon as I'll find it. Sorry again. – BaCaRoZzo Nov 23 '14 at 09:14
  • 1
    @S.M.Mousavi Sorry for the delay. The example I had was only for the selected item so no gradual transition, I was out of office and, last but not least, I had some problems with `y` (was just late at night...longer story [here](http://lists.qt-project.org/pipermail/interest/2014-November/014231.html) and many thanks to Jens in the end). Anyhow, I hope this approach satisfies your needs. – BaCaRoZzo Nov 27 '14 at 21:03
  • 1
    Thank you very much. You gave me a better vision about this problem. I shared my final solution for you and anybody. I like to give you some score but it seems there is not any way :( Thanks again – S.M.Mousavi Nov 29 '14 at 11:08
  • Fine like this. It was fun to write the answer and your solution give me some nice ideas. +1 for the correct answer. :) – BaCaRoZzo Nov 29 '14 at 14:58
12

After many hours of work, research and @BaCaRoZzo's great help (Thanks @BaCaRoZzo), I finally found the right solution. Just use Component.onCompleted() event handler to run the animation associated with each delegate.

Here is an example, enjoy!

scroll animation on 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" }
        }
    }
}
BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
S.M.Mousavi
  • 5,013
  • 7
  • 44
  • 59
  • 1
    Yeah, great solution! I still didn't match your requirements but at least I was helpful and I'm glad of that. :) Also, I'm going to play around with your solution for sure. Love it. :) – BaCaRoZzo Nov 29 '14 at 12:04
-1

A PathView displays data from models created from built-in QML types like ListModel and XmlListModel, or custom model classes defined in C++ that inherit from QAbstractListModel. The view has a model, which defines the data to be displayed, and a delegate, which defines how the data should be displayed. The delegate is instantiated for each item on the path. The items may be flicked to move them along the path.

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
zSilas
  • 1
  • Thank you. I checked it. It seems to be applicable for resolution. But Scrolling Animation has different behaviour. Invisible items, must becomes visible and fills list's visible area with an animation on scrolling. The PathView does not sufficient behaviour and not satisfies this problem :\ . Take a look at sample video http://codecanyon.net/item/android-listview-animations/4179731 – S.M.Mousavi Nov 26 '14 at 08:24