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.
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 want to create, in QML, a TV-schedule where the vertical axis is a list of Channels and the horizontal axis is time-based. For example something like
(source: zappware.com)
Initially, I created
a vertical ListView with
model = the list of Channels
delegate = a horizontal ListView
every horizontal ListView has
model = the list of Events
delegate = an Item where the width is proportional to the duration of the Event
So far so good. Only drawback is that the horizontal ListViews scroll one by one while they should scroll together.
So somehow, the contentX property of every horizontal ListView should be bound to the contentX property of the moving/flicking horizontal ListView. Note that this binding is dynamic: when flicking in the first row, all other rows should bind to the contentX of the first row. But this should be changed when flicking in the second row.
Any advice on how this can be done?
I tried a somewhat different approach by
creating a Flickable Item on top of the vertical ListView (with contentWidth the complete time-window).
binding every horizontal ListView to the contentX of this Flickable (this is a static binding)
This resulted in nice synchronous scrolling but I still have some issues
I had to do some tricks to ensure that flicking is only horizontal or vertical but not both
I'm not able anymore to click on individual Events; I guess events are intercepted by the Flickable
I'm also not sure about the memory impact of such a Flickable with a huge contentWidth?
Feedback appreciated!
I'd say have only one vertical list view for the channels. But the channel names only, not the actual programs. Instead of a horizontal view for the programs, you can cram them all together in a single flickable, using the begin time and duration to layout the programs in the flickable by binding their x and width properties to the former.
Then you can bind the channel list view together with the vertical scrolling of the program items, so that you have the programs corresponding to their appropriate channels. This way you can scroll vertically from both, and only scroll horizontally the programs.
Here is a quick example:
ApplicationWindow {
id: main
width: 500
height: 100
visible: true
color: "white"
ListModel {
id: modC
ListElement { name: "Ch1" }
ListElement { name: "Ch2" }
ListElement { name: "Ch3" }
}
ListModel {
id: modP1
ListElement { name: "p1"; start: 0; duration: 6 }
ListElement { name: "p2"; start: 6; duration: 6 }
ListElement { name: "p3"; start: 12; duration: 6 }
ListElement { name: "p4"; start: 18; duration: 6 }
}
ListModel {
id: modP2
ListElement { name: "p1"; start: 0; duration: 12 }
ListElement { name: "p2"; start: 12; duration: 12 }
}
ListModel {
id: modP3
ListElement { name: "p1"; start: 0; duration: 8 }
ListElement { name: "p2"; start: 8; duration: 8 }
ListElement { name: "p3"; start: 16; duration: 8 }
}
property var subMod : [ modP1, modP2, modP3 ]
Component {
id: progDelegate
Rectangle {
property var source
x: source.start * 50
width: source.duration * 50
height: 50
color: "lightblue"
border.color: "black"
Text {
text: source.name
}
}
}
Row {
anchors.fill: parent
ListView {
id: list
height: parent.height
width: 100
model: modC
delegate: Item {
width: 100
height: 50
Rectangle {
anchors.fill: parent
color: "red"
border.color: "black"
Text {
anchors.centerIn: parent
text: name
}
}
Component.onCompleted: {
var mod = subMod[index]
for (var i = 0; i < mod.count; ++i) progDelegate.createObject(flick.contentItem, {"source": mod.get(i), "y": index * 50})
}
}
}
Flickable {
id: flick
height: parent.height
width: parent.width - list.width
contentWidth: 1200
contentHeight: contentItem.childrenRect.height
clip: true
flickableDirection: Flickable.HorizontalFlick
contentY: list.contentY
}
}
}
I have gone through the Qt docs :Qt StackView yet still I can't figure out what am I doing wrong, because my code should produce fade in/out animation but what I got is just an instant blink between content. I hope my description is sufficient and clear.
StackView {
id: stackView
anchors.fill: parent
delegate: StackViewDelegate {
function transitionFinished(properties)
{
properties.exitItem.opacity = 1
}
property Component pushTransition: StackViewTransition {
PropertyAnimation {
target: enterItem
property: "opacity"
from: 0
to: 1
duration: 10
}
PropertyAnimation {
target: exitItem
property: "opacity"
from: 1
to: 0
duration: 10
}
}
}
initialItem: Item {
width: parent.width
height: parent.height
ListView {
model: pageModel
anchors.fill: parent
delegate: AndroidDelegate {
text: title
onClicked: stackView.push(Qt.resolvedUrl(page))
}
}
}
}
I hit the same problem and asked their support - their example is wrong.
replace
property Component pushTransition: StackViewTransition {
with
pushTransition: StackViewTransition {
and it should work. pushTransition is actually a property on StackViewDelegate
I'm still trying to get the transitionFinished function to work becuase their example of that doesn't work either.
The duration of your animation is 10 milliseconds, or 1/100th of a second.
This is working for me:
Window {
id: mainWindowId
width: 1280
height: 800
StackView {
id: stackViewId
anchors.fill: parent
replaceEnter: Transition {
PropertyAnimation{
property: "opacity"
from: 0
to: 1
duration: 300
}
}
replaceExit: Transition {
PropertyAnimation{
property: "opacity"
from: 1
to: 0
duration: 250
}
}
initialItem: "yourStartingPage.qml"
}
}
And for changing views:
stackViewId.replace("yourOtherPage.qml")
import QtQuick 1.0
Rectangle
{
width: 100; height: 100
color: "red"
MouseArea
{
anchors.fill: parent
onPressed:
{
NumberAnimation
{
target: parent.x
to: 50;
duration: 1000
}
}
}
}
I expect this code to shift the x position of the rectangle on the button press event, but this does nothing.
Where am I going wrong?
You are defining an NumberAnimation in a signal handler, it's not going to work properly. Furthermore, NumberAnimation target should be an item, and here you are targeting a property of an item. Here is your code corrected :
import QtQuick 1.0
Rectangle
{
id: rect
width: 100; height: 100
color: "red"
MouseArea
{
anchors.fill: parent
onPressed:
{
animation.start()
}
NumberAnimation
{
id: animation
target: rect
property: "x"
to: 50;
duration: 1000
}
}
}
If your rectangle animation should revert when mouse is released, you would like to take benefit of a proper state definition, and animate property "x" at each state change (between default and "pressed" states. Here is a self contained example :
import QtQuick 1.0
Rectangle {
id: root
width: 360
height: 200
Rectangle
{
id: rect
width: 100; height: 100
color: "red"
MouseArea
{
id: mouse
anchors.fill: parent
}
states: [
State {
name: "pressed"
when: mouse.pressed
PropertyChanges {
target: rect
x: 50
}
}
]
Behavior on x {
NumberAnimation { duration: 1000 }
}
}
}
If you need more complex animation, define a proper Transition. A simple Behavior, here, is more readable I find.
Try this code:
import QtQuick 1.0
Rectangle
{
width: 100; height: 100
color: "red"
MouseArea {
anchors.fill: parent
onPressed: {
animation.start();
}
}
NumberAnimation on x {
id: animation
running: false
to: 50
duration: 1000
}
}
From docs:
NumberAnimation is a specialized PropertyAnimation that defines an animation to be applied when a numerical value changes.
So, you want not to animate on click, but you want to assign animation to x property of rectangle and start it on click.