ListView Load element with animation one by one - qt

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

Related

How to trigger An Action Once After NumberAnimation Is Completed In QML

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

Displaced ViewTransition not triggered in QML

So I have a ListView and ListModel to which I'm adding objects dynamically. I want a displaced animation to happen to when a new object is added, but from what I can see, only the add transition is triggered and the displaced transition is never triggered. Based on the tutorial, this works:
Rectangle {
anchors.fill: parent
ListView {
width: 240; height: 320
model: ListModel {
id: model
}
Component {
id: del
Row {
height: 20
spacing: 20
Text { text: name }
Text { text: age }
}
}
delegate: del
add: Transition {
NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 1000 }
}
displaced: Transition {
NumberAnimation { properties: "x,y"; duration: 1000; easing.type: Easing.OutBounce }
}
focus: true
Keys.onSpacePressed: model.insert(0, { "name": "Item " + model.count, "age":21})
}
}
But this only triggers the add transition.
Rectangle {
height: 500
width: 0.90 * parent.width
anchors {
top: parent
topMargin: 30
left: parent.left
leftMargin: 45
}
ListView {
anchors.fill: parent
model: notificationModel
delegate: notificationDelegate
spacing: 30
add: Transition {
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 1000 }
}
displaced: Transition {
NumberAnimation { properties: "x,y"; duration: 1000; easing.type: Easing.OutBounce }
}
}
}
ListModel {
id: notificationModel
Component.onCompleted: {
notificationModel.append({"name": "Tony"})
}
}
Timer {
interval: 5000; running: true; repeat: true
onTriggered: notificationModel.append({"name": "Stark"})
}
Component {
id: notificationDelegate
Row {
spacing: 20
Text { text: name; color: "white" }
}
}
Any idea why this happens? Thanks!
The displaced transition is triggered when an item in the list is forced to move because another item is added/removed/etc. The working example works because a single item gets inserted at the beginning of the list causing the remaining items to be displaced.
With the current code you are showing, the problem is that you are using the append function instead of insert. Append adds items to the bottom of the list, so no items are getting displaced.
Before you edited your question, you had a different problem. You were using insert to add items to the beginning of the list, but every time you added one, you were clearing the entire list and rebuilding it. So again, nothing was getting displaced because the entire list was getting recreated every time.

QML - Why do animations conflict?

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

Display variuos size of ListElements with GridView

Iam relatively new to qml and maybe my attempt is wrong, so please, if you have another idea for solving the issue, please do not hesitate to write it down.
Problem:
I need to Display varoius sizes of ListElemts (which can be dragged and dropped). The main Container is 200px x 180px. The smalles List element is 100px x 60px, the secound size should be 200px x 60px and the biggest should be 200px x 120 px. So I found an Qt example and modified it to my needs, but I have problems regarding the automatic placement of the ListElements.
It works perfect for only small ListElements like here:
Now, if I need a big one, GridView is placing them for Expample like this one:
In this Example, the View is much larger in width like 200px. (gragging the ListElements around may sort them other way, but not proper.)
In the last test, I removed CellWidth and Cell height:
The traget is to display all cells in a 200px x 180px Grid.
As said, maybe my attempt is wrong. Do anyone of you guys have a solution for me?
Here is the modified code. For testing, I commented out things and altered the size property:
Window {
visible: true
width: 480
height: 272
title: qsTr("Hello World")
GridView {
id: root
width: 200; height: 180
cellWidth: 100; cellHeight: 60
displaced: Transition {
NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad }
}
//! [0]
model: DelegateModel {
//! [0]
id: visualModel
model: ListModel {
id: colorModel
ListElement { color: "blue"; size:1}
ListElement { color: "green"; size:1 }
ListElement { color: "red"; size: 1 }
ListElement { color: "yellow"; size: 1 }
ListElement { color: "orange"; size: 1 }
ListElement { color: "purple"; size: 1 }
}
//! [1]
delegate: MouseArea {
id: delegateRoot
property int visualIndex: DelegateModel.itemsIndex
width:{
switch (model.size) {
case 1:
return 100;
case 2:
return 200;
case 3:
return 200;
}
}
height: {
switch (model.size) {
case 1:
return 60;
case 2:
return 60;
case 3:
return 120;
}
}
drag.target: icon
Rectangle {
id: icon
width:{
switch (model.size) {
case 1:
return 100;
case 2:
return 200;
case 3:
return 200;
}
}
height: {
switch (model.size) {
case 1:
return 60;
case 2:
return 60;
case 3:
return 120;
}
}
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter
}
color: model.color
radius: 3
Drag.active: delegateRoot.drag.active
Drag.source: delegateRoot
Drag.hotSpot.x: icon.width/2
Drag.hotSpot.y: icon.height/2
states: [
State {
when: icon.Drag.active
ParentChange {
target: icon
parent: root
}
AnchorChanges {
target: icon;
anchors.horizontalCenter: undefined;
anchors.verticalCenter: undefined
}
}
]
}
DropArea {
anchors { fill: parent; margins: 0 }
onEntered: visualModel.items.move(drag.source.visualIndex, delegateRoot.visualIndex)
}
}
//! [1]
}
}
}
Thank you in advance for all your thoughts/suggestions!
Edit:
To clear up the questions, here a gif with showd correct rearrangement when drag and drop items:
This Picture shows the the problems I decribed. Some items are overlapping:
This picture is showing the problem with not setted cellWidth and cellHeight of GridView.
As far as I understand, GridView donĀ“t care about the size of its elements. Positioning Elements depends on the cellWith and cellHeight Values. But is it possible to configure GridView the way I need (do not overlap, let items not come outside bonds)?
Or is GradView maybe the wrong attempt to realize this?
If anyone need more information, I will be happy to provide!

QML : Flippable issue

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.

Resources