Example of Qml - Flipable:
import QtQuick 2.0
Flipable {
id: flipable
width: 240
height: 240
property bool flipped: false
front: Rectangle { width: 200; height: 200; color: 'red'; anchors.centerIn: parent }
back: Rectangle { width: 200; height: 200; color: 'blue'; anchors.centerIn: parent }
transform: Rotation {
id: rotation
origin.x: flipable.width/2
origin.y: flipable.height/2
axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
angle: 0 // the default angle
}
states: State {
name: "back"
PropertyChanges { target: rotation; angle: 180 }
when: flipable.flipped
}
transitions: Transition {
NumberAnimation { target: rotation; property: "angle"; duration: 4000 }
}
MouseArea {
anchors.fill: parent
onClicked: flipable.flipped = !flipable.flipped
}
}
This example is running good but, if I use this code, Flipable doesn't run:
MouseArea {
anchors.fill: parent
onClicked: {
flipable.flipped = true;
flipable.flipped = false;
}
}
I think the animation conflicts when I first make the flipped property is true then false. Whereas I want flipable open and then close.
The problem is that you set the property flipped back to false before the flip animation even started.
If you want the full open/close animation, you have to wait for the "open" animation to finish before starting the "close" animation:
transitions: Transition {
id: transition
onRunningChanged: {
if (!running && flipable.flipped) {
flipable.flipped = false;
}
}
NumberAnimation { target: rotation; property: "angle"; duration: 4000 }
}
MouseArea {
anchors.fill: parent
onClicked: {
if (!transition.running) {
flipable.flipped = true;
}
}
}
Related
I am trying to call a reset function once after the animation is completed. But in the below given code , the animation starts after resetting the values
On Long press of the button I need to start an animation which basically acts as a progress bar, and once the animation is completed I need to call the reset() function.
I have tried the below given code , But here the animation starts once after the resetting of values are done.
Button {
id: button1
onPressAndHold: {
rectangle.visible = true
timer.restart()
}
}
Item {
id: rectangle
Behavior on width {
NumberAnimation {
duration: 1000
easing.type: Easing.InOutCubic
}
}
Image {
id: img
source: "somesource"
fillMode: Image.PreserveAspectCrop
}
}
Timer {
id: timer
repeat: true
interval: 50
onTriggered: {
rectangle.width = img.sourceSize.width * img.progress
if (rectangle.width <= img.sourceSize.width) {
timer.stop()
reset(values)
}
}
}
can you please let me know on how modify it such that the animation completes first and then the reset is done. Thank you in advance!
Ok, that really works for standalone Animation only that sounds a bit strange for me since the common usecase is using animations inside Behavior or State.
So you can use NumberAnimation.onRunningChanged or ScriptAction as #iam_peter said or use Transition.onRunningChanged as well:
Window {
height: 200
width: 600
visible: true
title: qsTr("Animation test")
RowLayout {
width: parent.width
height: 100
anchors.centerIn: parent
Button {
text: "start"
onClicked: {
testRect.state = "state2"
}
}
Rectangle {
id: testContainer
Layout.fillWidth: true
Layout.fillHeight: true
Rectangle {
id: testRect
height: 100
width: 0
color: "orange"
states: [
State {
name: "state1"
PropertyChanges {
target: testRect
width: 0
}
},
State {
name: "state2"
PropertyChanges {
target: testRect
width: testContainer.width
}
}
]
transitions: Transition {
NumberAnimation {
property: "width"
duration: 2000
easing.type: Easing.InQuad
}
onRunningChanged: {
if(running == false)
{
finishRect.color = "red";
}
}
}
}
}
Rectangle {
id: finishRect
Layout.preferredWidth: 100
color: "yellow"
width: 100
height: width
radius: width / 2
}
}
}
According to this post you have two options.
You can us the onRunningChanged handler and check if the animation is still running. If not call anything you want at that point.
Rectangle {
id: rect
width: 100; height: 100
color: "red"
Behavior on width {
NumberAnimation {
duration: 1000
onRunningChanged: {
if (!running)
console.log("Animation finished")
}
}
}
MouseArea {
anchors.fill: parent
onClicked: rect.width = 50
}
}
The other option would be to create a SequentialAnimation and add a SrcriptAction that runs after the NumberAnimation is completed.
Rectangle {
id: rect
width: 100; height: 100
color: "red"
Behavior on width {
SequentialAnimation {
NumberAnimation { duration: 1000 }
ScriptAction {
script: console.log("Animation finished")
}
}
}
MouseArea {
anchors.fill: parent
onClicked: rect.width = 50
}
}
I have a simple ListView here with RotationAnimation when the listview is loaded. Basically the listview I have has pre defined model or list elements. I want to load each element with the RotationAnimation that I have one by one. For example, first element is displayed with animation and after few milliseconds (0.5ms) the next element will be displayed with animation. What I currently have right now is the whole listview will be displayed with animation including all the elements already. Is there anyway to do what I want?
This is what I currently have where the whole listview is displayed with animation
ListView {
width: 240; height: 320
model: ListModel {
ListElement{
name:"One"
}
ListElement{
name:"Two"
}
ListElement{
name:"Three"
}
}
delegate: Rectangle {
width: 100; height: 30
border.width: 1
color: "Transparent"
Text {
anchors.centerIn: parent
text: name
}
Component.onCompleted: seqAnim.start();
transform: Rotation { id:rotate; origin.x: width; origin.y: height; axis { x: 0.3; y: 1; z: 0 } angle: 0}
SequentialAnimation {
id: seqAnim
running: false
RotationAnimation { target: rotate; from: 180; to: 0; duration: 3000; easing.type: Easing.OutBack; property: "angle" }
}
}
}
What I want is the ListElements are displayed one by one entering from left to right
With a static model like you used, you will get all elements animated since that happens in the delegate.
On the other hand, with a dynamic model (where elements are added along the way), will let you get animation for each element individually.
You need to manage (suitably) how model elements are added to the model based on your practical model.
An example to demonstrate the concept is by adding model elements with a timer, sine you mentioned you want a period between the animations ...
ListModel {
id:lModel
ListElement{
name:"One"
}
}
Timer {
property int indexer: 0
id:timer
interval: 1000 ;running: true; repeat: true
onTriggered: {
if(indexer === 0)
lModel.append({name:"Two"})
else if (indexer === 1)
lModel.append({name:"Three"})
else timer.stop()
indexer++
}
}
ListView {
width: 240; height: 320
model: lModel
delegate: Rectangle {
width: 100; height: 30
border.width: 1
color: "Transparent"
Text {
anchors.centerIn: parent
text: name
}
Component.onCompleted: seqAnim.start();
transform: Rotation { id:rotate; origin.x: width; origin.y: height; axis { x: 0.3; y: 1; z: 0 } angle: 0}
SequentialAnimation {
id: seqAnim
running: false
RotationAnimation { target: rotate; from: 180; to: 0; duration: 3000; easing.type: Easing.OutBack; property: "angle" }
}
}
}
I need sequentially to load one by one components from array.
Before loading next component to Loader it should be fade-out , loaded and then fade-in.
The code above blinks not correctly and show this message:
"QML State: Binding loop detected for property "when"
What have I done wrong?
Thank you
import QtQuick 2.0
Rectangle {
id: screen
width: 400
height: 400
color: "black"
Rectangle {
id: view
anchors.fill: parent
Loader {
id: loader
onLoaded: { view.state="fadeIn"; }
}
states: [
State {
name: "fadeOut";
PropertyChanges { target: view; opacity: 0.1; }
},
State {
name: "fadeIn";
PropertyChanges { target: view; opacity: 1; }
},
State {
name: "load"; when: view.opacity == 0;
StateChangeScript { script: { loader.sourceComponent=com_array[index]; } }
}
]
transitions: [
Transition {
to: "fadeIn"
NumberAnimation { properties: "opacity"; from: 0.0; to: 0.99; duration: 2000 }
},
Transition {
to: "fadeOut"
NumberAnimation { properties: "opacity"; from: 0.99; to: 0; duration: 2000 }
}
]
}
Timer {
id: timer
interval: 3000; running: true; repeat: true
onTriggered: {
++index;
if( index >= com_array.length ) {
index = 0
}
view.state="fadeOut"
}
}
// -------------------------------------------------------------------
// Components
Item {
id: list
property Component red_rect_com : Component {
Rectangle {
width: screen.width; height: screen.height
Rectangle {
anchors.fill: parent; color: "red";
}
}
}
property Component blue_rect_com : Component {
Rectangle {
width: screen.width; height: screen.height
Rectangle {
anchors.fill: parent; color: "blue";
}
}
}
}
property int index: 0
property var com_array: [ list.red_rect_com, list.blue_rect_com ]
Component.onCompleted: { loader.sourceComponent = com_array[index]; }
}
UPD
Possible this could be useful for other, full example with correction ( thanks to answer author ):
import QtQuick 2.0
Rectangle {
id: screen
width: 400
height: 400
color: "black"
Rectangle {
id: view
anchors.fill: parent
property var sourceComponent
opacity: 0
function loadComponent( component, is_first_start ) {
sourceComponent = component;
if( is_first_start ) {
fadeOut.stop(); fadeIn.start();
}
else {
fadeIn.stop(); fadeOut.start();
}
}
Loader {
id: loader
onLoaded: {
fadeOut.stop();
fadeIn.start();
}
}
NumberAnimation on opacity {
id: fadeOut;
to: 0.0;
duration: 2000;
onStopped: { loader.sourceComponent=view.sourceComponent; }
}
NumberAnimation on opacity {
id: fadeIn;
to: 1.0;
duration: 2000
}
}
Timer {
id: timer
interval: 4000; running: true; repeat: true
onTriggered: {
++index;
if( index >= com_array.length ) {
index = 0
}
view.loadComponent( com_array[index], false );
}
}
// -------------------------------------------------------------------
// Components
Item {
id: list
property Component red_rect_com : Component {
Rectangle {
width: screen.width; height: screen.height
Rectangle {
anchors.fill: parent; color: "red";
}
}
}
property Component blue_rect_com : Component {
Rectangle {
width: screen.width; height: screen.height
Rectangle {
anchors.fill: parent; color: "blue";
}
}
}
}
property int index: 0
property var com_array: [ list.red_rect_com, list.blue_rect_com ]
Component.onCompleted: { view.loadComponent( com_array[index], true ); }
}
It gives you the error because QML make connections to communicate between changes:
onStateChange is connected by PropertyChanges that changes opacity to a view's opacity property.
The onOpacityChanged signal will be connected to State's when property.
The onWhenChanged signal will be connected to the state property, thus making binding Loop.
Another thing is that when you make NumberAnimation within Transition, you do not need to specify the from and to properties. The PropertyChanges sets the correct value.
Anyway, your states does not make sense, you using them wrong.
You should also wait for fadeOut, because it will load so fast that you will not see the effect.
Simply make two animations:
Rectangle {
id: view
anchors.fill: parent
property var sourceComponent
function loadComponent(component){
fadeIn.stop(); fadeOut.start();
sourceComponent = component;
}
Loader {
id: loader
onLoaded: { fadeOut.stop(); fadeIn.start(); }
}
NumberAnimation on opacity {
id: fadeOut
to: 0.0
onStopped: { loader.sourceComponent= sourceComponent; }
}
NumberAnimation on opacity {
id: fadeIn
to: 1.0
}
}
You should also change the loading to:
property int index: 0
onIndexChanged: { view.loadComponent(com_array[index]); }
But there is better solution: using ShaderEffectSource to take a frame of Loader before changing the source, and using it to fadeOut meanwhile loading the component.
I am trying to make a flippable clock in QML.
But am not able to get the flippable effect as desired, I have referred the documentation of flip method, took that as base for further development.
Tried various approaches but didn't succeed. Any idea how to get that effect.
Below is the referred documentation code snippet
import QtQuick 1.0
Flipable {
id: flipable
width: 240
height: 240
property bool flipped: false
front: Image { source: "front.png"; anchors.centerIn: parent }
back: Image { source: "back.png"; anchors.centerIn: parent }
transform: Rotation {
id: rotation
origin.x: flipable.width/2
origin.y: flipable.height/2
axis.x: 1; axis.y: 0; axis.z: 0 // set axis.x to 1 to rotate around y-axis
angle: 0 // the default angle
}
states: State {
name: "back"
PropertyChanges { target: rotation; angle: 180 }
when: flipable.flipped
}
transitions: Transition {
NumberAnimation { target: rotation; property: "angle"; duration: 4000 }
}
MouseArea {
anchors.fill: parent
onClicked: flipable.flipped = !flipable.flipped
}
}
You can't flip just half of an Item. To simulate a flip clock you have to use two clones of an image.
Try to add this at the end of the code above:
Item {
anchors {top: flipable.top; horizontalCenter: flipable.horizontalCenter}
width: flipable.width
height: flipable.height / 2
z: flipable.z + 1
clip: true
Image {
source: "front.png";
anchors.centerIn: parent
}
}
Obviously this code is not the solution, it's just a hint on what you have to do for the complete solution.
So I try:
Rectangle {
x: 617
y: 450
Image {
id: rect
source: "buttons/GO/GO!-norm.png"
smooth: true
opacity: 1
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true //this line will enable mouseArea.containsMouse
onClicked: Qt.quit()
}
states: [
State {
name: "mouse-over"; when: mouseArea.containsMouse
PropertyChanges { target: rect; scale: 0.95; opacity: 0; }
PropertyChanges { target: rect2; scale: 0.95; opacity: 1}
PropertyChanges { target: rect3; scale: 0.95; opacity: 0}
},
State {
name: "mouse-down"; when: mouseArea.pressedButtons
PropertyChanges { target: rect; scale: 0.95; opacity: 0; }
PropertyChanges { target: rect2; scale: 0.95; opacity: 0}
PropertyChanges { target: rect3; scale: 0.95; opacity: 1}
}
]
transitions: Transition {
NumberAnimation { properties: "scale, opacity"; easing.type: Easing.InOutQuad; duration: 500 }
}
}
Image {
id: rect2
source: "buttons/GO/GO!-over.png"
smooth: true
opacity: 0
anchors.fill: rect
}
Image {
id: rect3
source: "buttons/GO/GO!-down.png"
smooth: true
opacity: 0
anchors.fill: rect
}
}
but it works only in over\out states... How to make my button have 3 states?
I'm not entirely sure if this is what happens but it's possible: When you mouseover the image, its opacity gets set to 0. The documentation says that:
Changing opacity affects also the child items of the Item.
If an item's opacity is set to 0, the item will no longer receive mouse events.
Thus, when you mouseover, the rect.opacity is set to 0, mouseArea stops receiving mouse events and the mouseArea.pressedButtons condition is never fulfilled. You can probably avoid this by making mouseArea a sibling of the Image and not its child item. Use an Item or Rectangle as the parent item.