Qt Quick实现一个炫酷的折叠动画效果

这篇博客介绍了ShaderEffectSource和Flipable在QtQuick中的使用,通过一个动态折叠卡片的示例展示了它们的功能。ShaderEffectSource用于将项目渲染为纹理并应用于效果,可以作为缓存或不透明层。而Flipable则用于创建翻转效果,适用于卡片式的正面和背面切换。博客还提供了相关代码和效果展示。
摘要由CSDN通过智能技术生成

demo来自 https://github.com/cjmdaixi/FlipAnimation

代码上传到了 csdn

效果图

可以动态配置折叠次数
在这里插入图片描述

核心代码

FlipCards.qml

import QtQuick 2.9
import QtQuick.Controls 2.2

Item {
    id: root

    property int segments: 4
    property Item flipContent
    property Item coverItem

    property int expandSpeed: 300
    property int foldSpeed: 300

    property bool expanding: false
    property bool folding: false

    property bool folded: true

    width: flipCardWidth
    height: flipCardHeight

    property real flipCardWidth: flipContent.width
    property real flipCardHeight: flipContent.height / segments
    property int flipCardCount: segments - 1

    signal beginFold()
    signal beginExpand()

    ShaderEffectSource{
        id: bottomCard
        width: flipCardWidth
        height: flipCardHeight
        anchors{left: parent.left; top: parent.top}
        sourceItem: flipContent
        sourceRect: Qt.rect(0, 0, flipCardWidth, flipCardHeight)
        z: flipContent.z + 1
    }

    Component{
        id: flipCardComponent
        Flipable{
            id: flipable
            width: root.flipCardWidth
            height: root.flipCardHeight
            property alias expandAnimation: expandAnimation
            property alias foldAnimation: foldAnimation
            property alias backItem: backItem
            property alias frontItem: frontItem
            property int index: 0

            property bool isFirstCard: index === 0
            property bool isLastCard: index === root.flipCardCount - 1

            transform: Rotation {
                id: rotation
                origin.x: flipable.width / 2
                origin.y: flipable.height
                axis.x: -1; axis.y: 0; axis.z: 0
                angle: 0
            }
            front: Item{
                id: frontItem
                anchors.fill: parent
                Rectangle{
                    anchors.fill: parent
                    color: "white"
                }
            }
            back: ShaderEffectSource{
                id: backItem
                sourceItem: flipContent
                sourceRect: Qt.rect(0, flipCardHeight * (index + 1), flipCardWidth, flipCardHeight)
                anchors.fill: parent
            }

            NumberAnimation{
                id: expandAnimation
                duration: expandSpeed
                target: rotation
                property: "angle"
                to: 180
                onStarted: {
                    root.height += flipCardHeight;
                    if(isFirstCard){
                        root.expanding = true;
                    }
                }

                onStopped: {
                    if(isLastCard){
                        flipContent.layer.enabled = false;
                        flipContent.visible = true;
                        bottomCard.visible = false;
                        root.expanding = false;
                        root.folded = false;
                    }
                }
            }
            NumberAnimation{
                id: foldAnimation
                duration: foldSpeed
                target: rotation
                property: "angle"
                to: 0
                onStarted: {
                    if(isLastCard){
                        flipContent.layer.enabled = true;
                        flipContent.visible = false;
                        bottomCard.visible = true;
                        root.folding = true;
                    }
                }

                onStopped: {
                    root.height -= flipCardHeight;
                    if(isFirstCard){
                        root.folding = false;
                        root.folded = true;
                    }
                }
            }
        }
    }

    Loader{
        id: firstCardLoader
        sourceComponent: flipCardComponent
        onLoaded: {
            var prev = firstCardLoader.item;
            root.beginExpand.connect(prev.expandAnimation.start);

            flipContent.parent = root;
            flipContent.visible = false;
            coverItem.parent = prev.frontItem;
            prev.parent = bottomCard;
            for(var i = 1; i < root.flipCardCount; i++){
                //console.log("create flip card ", i);
                var card = flipCardComponent.createObject(prev.backItem, {"index": i});
                prev.expandAnimation.stopped.connect(card.expandAnimation.start);
                if(i === root.flipCardCount - 1){
                    root.beginFold.connect(card.foldAnimation.start);
                }
                card.foldAnimation.stopped.connect(prev.foldAnimation.start);
                prev = card;
            }
        }
    }

    function expand(){
        root.beginExpand();
    }

    function fold(){
        root.beginFold();
    }
}

从中学习到了什么

ShaderEffectSource

ShaderEffectSource类型将sourceItem渲染为纹理并在场景中显示。sourceItem被绘制到纹理中,就像它是一个完全不透明的根项目一样。因此,sourceItem本身可以不可见,但仍会出现在纹理中。

ShaderEffectSource可以用作:

  • ShaderEffect中的纹理源。这允许您将自定义着色器效果应用于任何Qt Quick项目。
  • 用于复杂项目的缓存。复杂项目可以在纹理中渲染一次,然后可以自由设置动画,而无需每帧再次渲染复杂项目。
  • 不透明层。ShaderEffectSource允许将不透明度作为一个组应用于项目,而不是单独应用于每个项目。

用法如下:

  import QtQuick 2.0

  Rectangle {
      width: 200
      height: 100
      gradient: Gradient {
          GradientStop { position: 0; color: "white" }
          GradientStop { position: 1; color: "black" }
      }
      Row {
          opacity: 0.5
          Item {
              id: foo
              width: 100; height: 100
              Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" }
              Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" }
              Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" }
          }
          ShaderEffectSource {
              width: 100; height: 100
              sourceItem: foo
          }
      }
  }

效果:在这里插入图片描述
即使把foo隐藏掉,ShaderEffectSource 仍然可以显示。

Flipable

Flipable是一种可以在正面和背面之间明显“翻转”的物品,就像一张卡片。它可以与旋转、状态和过渡类型一起使用,以产生翻转效果。

front和back属性用于保存分别显示在可翻转项目正面和背面的项目。

用法如下,纸牌反转demo,效果如图:

在这里插入图片描述

flipable.qmlproject

import QmlProject 1.1

Project {
    mainFile: "flipable.qml"

    /* Include .qml, .js, and image files from current directory and subdirectories */
    QmlFiles {
        directory: "."
    }
    JavaScriptFiles {
        directory: "."
    }
    ImageFiles {
        directory: "."
    }
}

Card.qml

import QtQuick 2.0

Flipable {
    id: container

    property alias source: frontImage.source
    property bool flipped: true
    property int xAxis: 0
    property int yAxis: 0
    property int angle: 0

    width: front.width; height: front.height

    front: Image { id: frontImage }
    back: Image { source: "back.png" }

    state: "back"

    MouseArea { anchors.fill: parent; onClicked: container.flipped = !container.flipped }

    transform: Rotation {
        id: rotation; origin.x: container.width / 2; origin.y: container.height / 2
        axis.x: container.xAxis; axis.y: container.yAxis; axis.z: 0
    }

    states: State {
        name: "back"; when: container.flipped
        PropertyChanges { target: rotation; angle: container.angle }
    }

    transitions: Transition {
        ParallelAnimation {
            NumberAnimation { target: rotation; properties: "angle"; duration: 600 }
            SequentialAnimation {
                NumberAnimation { target: container; property: "scale"; to: 0.75; duration: 300 }
                NumberAnimation { target: container; property: "scale"; to: 1.0; duration: 300 }
            }
        }
    }
}

flipable.qml

import QtQuick 2.0
import "content"

Rectangle {
    id: window

    width: 480; height: 320
    color: "darkgreen"

    Row {
        anchors.centerIn: parent; spacing: 30
        Card { source: "content/9_club.png"; angle: 180; yAxis: 1 }
        Card { source: "content/5_heart.png"; angle: 540; xAxis: 1 }
    }
}

感谢无私奉献的人

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值