Animate snap in ListView

2019-08-03 05:17发布

问题:

I would like to animate the "snap" effect when a ListView snaps to a specific item.

I enable "snapping" with the snapMode: ListView.SnapOneItem property. Currently it just de-accelerates to the current item and stops, but it would be nice if I could get it to make a "bounce" effect when it stops.

Any ideas about how I can do this?

Flickable has a rebound property, but this seems to not work for the snapping on elements inside a ListView.

回答1:

Since you used SnapOneItem, you can insert a bounce effect once the movement is finished, i.e. once movementEnded signal is emitted. IMO applying an animation on each item would be overkill in this case. Better would be to animate the contentY property of the ListView.

The following is a possible approach which produces a bounce (don't know if this is the exact effect you are searching for):

ListView {
    id: list
    anchors.fill: parent
    spacing: 10
    snapMode: ListView.SnapOneItem
    model: 100

    delegate: Rectangle {
        width: parent.width
        height: 50
        Text {                // added text to distinguish items
            text: index
            font.pixelSize: 20
            anchors.centerIn: parent
        }
        color: index % 2 ? "lightGray" : "darkGrey"
    }

    onMovementEnded: {
        bounce.start()        // starts the animation when the movement ends
    }

    onCurrentIndexChanged: { 
        if(!list.moving)      // animation started only if the list is not moving
            bounce.start()
    }

    Timer {
        repeat: true
        interval: 2000     // enough time to let the animation play
        running: true
        onTriggered: {
            list.incrementCurrentIndex()
        }
    }

    SequentialAnimation {
        id: bounce
        running: false

        NumberAnimation {
            target: list
            property: "contentY"
            to: list.contentY - 10
            easing {
                type: Easing.InOutBack
                amplitude: 2.0
                period: 0.5
            }
            duration: 250
        }

        NumberAnimation {
            target: list
            property: "contentY"
            to: list.contentY
            duration: 800
            easing {
                type: Easing.OutBounce
                amplitude: 3.0
                period: 0.7
            }
        }
    }
}   

When you release the items from a drag or movement the bounce is produced. The amplitude and period properties can be adjusted to obtain a stronger or lighter effect (the same applies for the value of to properties).

EDIT

As you've seen, if the list is moved via incrementCurrentIndex() no real movement occurs, i.e. the movementEnded signal is not emitted. In such a case you can exploit the change of value that occurs for the currentIndex. I've modified the example to combine this approach with the previous one and to show the usage I've inserted a Timer to call incrementCurrentIndex() function.

The !list.moving check is added to avoid double animations when the list is moved and to guarantee consistency in the example, since the Timer can generate inconsistent jumps while the list is dragged. Clearly, other more specific constraints can be added, depending on your requirements.