Skip to content

Instantly share code, notes, and snippets.

@oKcerG
Last active June 14, 2023 15:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oKcerG/a3c57ea24d43c45e90263f31a3f912f6 to your computer and use it in GitHub Desktop.
Save oKcerG/a3c57ea24d43c45e90263f31a3f912f6 to your computer and use it in GitHub Desktop.
import QtQuick 2.15
import QtQml 2.15
Behavior {
id: root
property QtObject fadeTarget: targetProperty.object
property string fadeProperty: "opacity"
property var fadeProperties: [fadeProperty]
property var exitValue: 0
property var enterValue: 0
property int fadeDuration: 300
property string easingType: "Quad"
property bool delayWhile: false
onDelayWhileChanged: {
if (!delayWhile)
sequentialAnimation.finished();
}
readonly property Component shaderEffectSourceWrapperComponent: Item {
id: ses
property alias shaderEffectSource: shaderEffectSource
property alias sourceItem: shaderEffectSource.sourceItem
parent: sourceItem.parent
x: sourceItem.x
y: sourceItem.y
ShaderEffectSource {
id: shaderEffectSource
transformOrigin: sourceItem.transformOrigin
hideSource: true
live: false
width: sourceItem.width
height: sourceItem.height
}
}
readonly property Component defaultExitAnimation: NumberAnimation {
properties: root.fadeProperties.join(',')
duration: root.fadeDuration
to: root.exitValue
easing.type: root.easingType === "Linear" ? Easing.Linear : Easing["In"+root.easingType]
}
property Component exitAnimation: defaultExitAnimation
readonly property Component defaultEnterAnimation: NumberAnimation {
properties: root.fadeProperties.join(',')
duration: root.fadeDuration
from: root.enterValue
to: root.fadeTarget[root.fadeProperties[0]]
easing.type: root.easingType === "Linear" ? Easing.Linear : Easing["Out"+root.easingType]
}
property Component enterAnimation: defaultEnterAnimation
SequentialAnimation {
id: sequentialAnimation
ScriptAction {
script: {
const exitItem = shaderEffectSourceWrapperComponent.createObject(null, { sourceItem: root.fadeTarget });
const exitShaderEffectSource = exitItem.shaderEffectSource;
if (exitAnimation === root.defaultExitAnimation)
root.fadeProperties.forEach(p => exitShaderEffectSource[p] = root.fadeTarget[p]);
exitShaderEffectSource.width = root.fadeTarget.width;
exitShaderEffectSource.height = root.fadeTarget.height;
const exitAnimationInstance = exitAnimation.createObject(root, { target: exitItem.shaderEffectSource });
exitAnimationInstance.finished.connect(() => {
sequentialAnimation.finished.disconnect(exitAnimationInstance.start);
exitAnimationInstance.target = null;
exitItem.destroy();
exitAnimationInstance.destroy();
});
sequentialAnimation.finished.connect(exitAnimationInstance.start);
}
}
PauseAnimation {
duration: 5 // figure out how to wait on a signal in an animation (for ShaderEffectSource update)
}
PropertyAction {}
ScriptAction {
script: {
const enterItem = shaderEffectSourceWrapperComponent.createObject(null, { sourceItem: root.fadeTarget });
const enterShaderEffectSource = enterItem.shaderEffectSource;
if (enterAnimation === root.defaultEnterAnimation)
root.fadeProperties.forEach(p => enterShaderEffectSource[p] = root.enterValue);
enterShaderEffectSource.live = true;
const enterAnimationInstance = enterAnimation.createObject(root, { target: enterItem.shaderEffectSource });
enterAnimationInstance.finished.connect(() => {
sequentialAnimation.finished.disconnect(enterAnimationInstance.start);
enterAnimationInstance.target = null;
enterItem.destroy();
enterAnimationInstance.destroy();
})
sequentialAnimation.finished.connect(enterAnimationInstance.start);
if (!root.delayWhile)
sequentialAnimation.finished();
}
}
}
}
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.12
Window {
width: 500
height: columnLayout.implicitHeight + 40
visible: true
title: "CrossFadeBehavior"
component BigCenteredLabel: Label {
Layout.preferredWidth: 50
font.pixelSize: 30
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
component EmojiImage: Image {
Layout.preferredWidth: 50
fillMode: Image.PreserveAspectFit
cache: false
horizontalAlignment: Image.AlignHCenter
verticalAlignment: Image.AlignVCenter
readonly property url smilingFaceUrl: "https://deelay.me/20/https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/60/google/56/white-smiling-face_263a.png"
readonly property url winkingFaceUrl: "https://deelay.me/20/https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/60/google/56/winking-face_1f609.png"
}
component DescriptionRow: RowLayout {
Layout.fillWidth: true
property alias description: descriptionLabel.text
Label {
id: descriptionLabel
font.pixelSize: 12
Layout.fillWidth: true
}
}
component TitleSeparator: RowLayout {
spacing: 20
property alias title: titleLabel.text
Rectangle {
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
height: 1
color: "darkgrey"
}
Label {
id: titleLabel
font.pixelSize: 20
Layout.alignment: Qt.AlignCenter
}
Rectangle {
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
height: 1
color: "darkgrey"
}
}
id: root
property int counter: 0
Timer {
id: timer
interval: 1000
repeat: true
onTriggered: root.counter++
}
Timer {
running: true
interval: 2000
onTriggered: timer.start()
}
ColumnLayout {
id: columnLayout
anchors.fill: parent
anchors.margins: 20
spacing: 20
TitleSeparator { title: "Usage on text change" }
DescriptionRow {
description: "No Behavior"
BigCenteredLabel {
text: root.counter
}
}
DescriptionRow {
description: "CrossFadeBehavior (on opacity by default)"
BigCenteredLabel {
text: root.counter
CrossFadeBehavior on text {}
}
}
DescriptionRow {
description: "CrossFadeBehavior on scale"
BigCenteredLabel {
text: root.counter
CrossFadeBehavior on text {
fadeProperty: "scale"
}
}
}
DescriptionRow {
description: "CrossFadeBehavior on scale and opacity"
BigCenteredLabel {
text: root.counter
CrossFadeBehavior on text {
fadeProperties: ["scale", "opacity"]
}
}
}
DescriptionRow {
description: "CrossFadeBehavior with custom animations"
BigCenteredLabel {
text: root.counter
CrossFadeBehavior on text {
id: textCfb
exitAnimation: ParallelAnimation {
id: exit
property Item target
NumberAnimation {
target: exit.target
property: "opacity"
to: 0
easing.type: Easing.InQuad
}
NumberAnimation {
target: exit.target
property: "x"
to: textCfb.fadeTarget.width
easing.type: Easing.InQuad
}
}
enterAnimation: ParallelAnimation {
id: enter
property Item target
NumberAnimation {
target: enter.target
property: "opacity"
from: 0
to: 1
easing.type: Easing.InQuad
}
NumberAnimation {
target: enter.target
property: "x"
from: -textCfb.fadeTarget.width
to: 0
easing.type: Easing.OutQuad
}
}
}
}
}
TitleSeparator { title: "Usage on slow image source change" }
DescriptionRow {
description: "No Behavior"
EmojiImage {
source: root.counter % 2 === 0 ? smilingFaceUrl : winkingFaceUrl
}
}
DescriptionRow {
description: "CrossFadeBehavior"
EmojiImage {
source: root.counter % 2 === 0 ? smilingFaceUrl : winkingFaceUrl
CrossFadeBehavior on source { }
}
}
DescriptionRow {
description: "CrossFadeBehavior with delayWhile loading"
EmojiImage {
source: root.counter % 2 === 0 ? smilingFaceUrl : winkingFaceUrl
CrossFadeBehavior on source {
delayWhile: fadeTarget.status === Image.Loading
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment