web-dev-qa-db-ja.com

ListViewスクロールアニメーション

QML ListViewのスクロールアニメーションを実装したい。これがサンプル画像です:
ListView scrolling animation
これを実装するために誰かが私に助言できますか?

ありがとうございました。

8
S.M.Mousavi

何時間もの作業、調査、そして@BaCaRoZzoの多大な支援(@BaCaRoZzoに感謝)を経て、ようやく適切な解決策を見つけました。各デリゲートに関連付けられたアニメーションを実行するには、Component.onCompleted()イベントハンドラーを使用するだけです。

ここに例があります、楽しんでください!

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" }
        }
    }
}
11
S.M.Mousavi

ViewTransition は、ListView(コンポーネント作成時の最初の項目の遷移)、populateadd(自明)などの操作のremoveをアニメーション化する方法に関する多くの興味深い例と他の操作を提供します。

ListViewを指定して、アニメーション化する操作ごとに要素Transitionを定義します。 アニメーションフレームワーク は、基本的なアニメーションを組み合わせて、興味のある(多かれ少なかれ)複雑な動作を作成するだけで、複合アニメーションを作成するために利用できます( here for実際の例)。

ここで、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 }
    }
}

最後に、いくつかの動作は、シェーダーを使用し、要素のアニメーションとデリゲートの1つまたは複数のデリゲートの遷移を組み合わせることによって取得できることに注意してください。良い例は Tweet Search で、バーアイテムのシェーディング効果([ShaderEffect][5]を参照)がTransitionListViewの単純なaddと組み合わされています。

編集

例のようにカスタマイズされたスクロールを提供するには、Item内のListViewsの位置を考慮する必要があります。実用的なソリューションの鍵は、ビューの可視部分内のItemの現在の位置を計算し、その値を使用して計算する方法を見つけることです適切な変換。 ListViewは、この目的に役立ついくつかの有用なプロパティを持つFlickableから派生しています。

ただし、yItemプロパティは、ListView内のコンテンツのoverall高さを参照します。その位置をw.r.tにするには可視領域の始まり contentY プロパティを使用できます。この場合、画像は1000語に相当します。

enter image description here

ycontentYの違いは、必要な変換係数を計算するために使用できる値を提供します(heightListViewに関連している可能性があります)。実際、ListViewがフリックされると、2つの値とそれらの差が変化し、特定のItemの変換係数が変化します。

そのような変換は、問題のpartのみをカバーします。フリック/移動が終了したら、Itemsアニメーションを「終了」して、表示されているすべてのitemsを使用できるようにする必要があります。この目的のために、 Binding とそのwhenプロパティを利用して、必要な場合のみ、つまり flicking または dragging が終了したときにのみ、終了アニメーションをアクティブ化できます。

この(退屈な)導入をすべて踏まえて、2番目のアニメーション(より単純なもの)を考慮に入れましょう。ここでは scale を使用して目的の効果を得ることができます。 delegate内のListViewコードは次のようになります。

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に基づいてスケーリング係数を定義しますが、2番目はListViewが移動していない場合にのみ、スケーリングを1に設定します。最後のBehaviorは、完全にスケーリングされたItemへの移行をスムーズするために必要です。

3番目の効果は、同様の方法で 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を1つだけ使用することを選択しました。 最初の例でも同じを作成できます。つまり、最初のデリゲートscale: 1 - listY / list.height / 2に書き込むことができます。

同様のアプローチに従って、最初のアニメーションなどを作成することもできます。最初のアニメーションでは、RotationTranslate を組み合わせれば十分だと思います。

16
BaCaRoZzo