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