Qml Coveflow angle animation - qt

I want to implement a coverflow in QML but having a problem with the angle animation.
Scrolling to the left (from number 1→18→17→...) works great, but right scrolling is not behaving as expected. I'm not sure I got the angles right but they seem to animate from -40°→0° which looks odd (correct would be from 40°→0).
Is there a way to correct this behavior?
Here is a working example of my code:
import QtQuick 2.4
import QtQuick.Window 2.2
Window {
visible: true
width: 1280
height: 800
MouseArea {
anchors.fill: parent
onWheel: {
if (wheel.angleDelta.y < 0)
{
view.incrementCurrentIndex()
}
else if (wheel.angleDelta.y > 0)
{
view.decrementCurrentIndex()
}
}
}
PathView {
id: view
property int itemAngle: 40.0
property int itemSize: width/3.5
anchors.fill: parent
pathItemCount: 10
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
interactive: true
model: [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]
delegate: viewDelegate
path: Path {
startX: 0
startY: height / 2
PathPercent { value: 0.0 }
PathAttribute { name: "z"; value: 0 }
PathAttribute { name: "angle"; value: view.itemAngle }
PathAttribute { name: "origin"; value: 0 }
PathLine {x: view.width*0.4; y: view.height / 2}
PathPercent { value: 0.45 }
PathAttribute { name: "angle"; value: view.itemAngle }
PathAttribute { name: "origin"; value: 0 }
PathAttribute { name: "z"; value: 10 }
PathLine { relativeX: 0; relativeY: 0 }
PathAttribute { name: "angle"; value: 0.0 }
PathAttribute { name: "origin"; value: 0 }
PathAttribute { name: "z"; value: 10 }
PathLine {x: view.width*0.6; y: view.height / 2}
PathPercent { value: 0.55 }
PathAttribute { name: "angle"; value: 0.0 }
PathAttribute { name: "origin"; value: 0 }
PathAttribute { name: "z"; value: 10 }
PathLine { relativeX: 0; relativeY: 0 }
PathAttribute { name: "angle"; value: -view.itemAngle }
PathAttribute { name: "origin"; value: view.itemSize }
PathAttribute { name: "z"; value: 10 }
PathLine {x: view.width; y: view.height / 2}
PathPercent { value: 1 }
PathAttribute { name: "angle"; value: -view.itemAngle }
PathAttribute { name: "origin"; value: view.itemSize }
PathAttribute { name: "z"; value: 0 }
}
}
Component {
id: viewDelegate
Rectangle {
id: flipItem
width: view.itemSize
height: view.height
color: "white"
z: PathView.z
property var rotationAngle: PathView.angle
property var rotationOrigin: PathView.origin
transform:
Rotation {
id: rot
axis { x: 0; y: 1; z: 0 }
angle: rotationAngle
origin.x: rotationOrigin
origin.y: width
Behavior on angle { PropertyAnimation{} }
}
Rectangle {
border.color: "black"
border.width: 2
color: (index%2 === 0) ? "yellow" : "royalblue"
anchors.top: flipItem.top
anchors.topMargin: 100
anchors.left: flipItem.left
anchors.right: flipItem.right
width: flipItem.width
height: flipItem.height*0.55
smooth: true
antialiasing: true
Text {
text: model.modelData
color: "gray"
font.pixelSize: 30
font.bold: true
anchors.centerIn: parent
}
}
}
}
}

Related

QML change path in PathView with NumberAnimation of its items

I have a PathView with Rectangle as the delegate. I change the PathView.path to another path and I want to play animation of moving PathView items to their new positions. Something like this:
PathView {
id: pathView
delegate: Rectangle {
Behavior on x {
NumberAnimation {
duration: 5000
}
}
}
path: path1
}
But it doesn't work. Is it possible to do somehow?
Unfortunately, changing the "PathView.path" to another "Path" will destroy and recreate delegates just like changing a model.
A workaround can be done with "states". You create multiple blank PathLine and set its x and y values from states. You give the animation from "transitions"
This Sample code will initially have 3 data items in the model. In between the animation, it reloads the model with 5 data and still works in a continuous fashion without any glitch to the animation.
PathView {
id: pathView
anchors.fill: parent
anchors.margins: 100
model: 3
path: Path {
id: pathLines
PathLine { id: pl1 }
PathLine { id: pl2 }
PathLine { id: pl3 }
PathLine { id: pl4 }
PathLine { id: pl5 }
}
state: 'zigzag'
states: [
State {
name: "zigzag"
PropertyChanges { target: pathLines; startX: 0; startY: 100; }
PropertyChanges { target: pl1; x: 200; y: 300; }
PropertyChanges { target: pl2; x: 500; y: 100; }
PropertyChanges { target: pl3; x: 600; y: 300; }
PropertyChanges { target: pl4; x: 800; y: 100; }
PropertyChanges { target: pl5; x: 1000; y: 300; }
},
State {
name: "close"
PropertyChanges { target: pathLines; startX: pathView.width/2; startY: pathView.height/2; }
PropertyChanges { target: pl1; x: pathView.width/2; y: pathView.height/2; }
PropertyChanges { target: pl2; x: pathView.width/2; y: pathView.height/2; }
PropertyChanges { target: pl3; x: pathView.width/2; y: pathView.height/2; }
PropertyChanges { target: pl4; x: pathView.width/2; y: pathView.height/2; }
PropertyChanges { target: pl5; x: (pathView.width/2) + 1; y: pathView.height/2; } // Note: "+1" to fix disappearance bug
},
State {
name: "open"
PropertyChanges { target: pathLines; startX: pathView.width/2; startY: pathView.height/4; }
PropertyChanges { target: pl1; x: pathView.width/2; y: pathView.height/4; }
PropertyChanges { target: pl2; x: pathView.width/2; y: pathView.height/4; }
PropertyChanges { target: pl3; x: pathView.width/2; y: pathView.height/4; }
PropertyChanges { target: pl4; x: pathView.width/2; y: pathView.height/4; }
PropertyChanges { target: pl5; x: pathView.width/2 + 1; y: pathView.height/4; } // Note: "+1" to fix disappearance bug
},
State {
name: "triangle"
PropertyChanges { target: pathLines; startX: 200; startY: 500; }
PropertyChanges { target: pl1; x: 400; y: 700; }
PropertyChanges { target: pl2; x: 600; y: 500; }
PropertyChanges { target: pl3; x: 350; y: 500; }
PropertyChanges { target: pl4; x: 300; y: 500; }
PropertyChanges { target: pl5; x: 250; y: 500; }
}
]
transitions: [
Transition {
to: 'zigzag'
SmoothedAnimation { properties: "x,y,startX,startY"; easing.type: Easing.InOutQuad; duration: 2000; onFinished: pathView.state = 'triangle' }
},
Transition {
to: 'triangle'
SmoothedAnimation { properties: "x,y,startX,startY"; easing.type: Easing.InOutQuad; duration: 2000; }
},
Transition {
to: 'close'
SmoothedAnimation { properties: "x,y,startX,startY"; easing.type: Easing.InOutQuad; duration: 2000; }
onRunningChanged: {
if (!running) {
pathView.model = 5
pathView.state = 'open'
}
}
},
Transition {
from: "close"
to: 'open'
SmoothedAnimation { properties: "x,y,startX,startY"; easing.type: Easing.InOutQuad; duration: 2000; }
onRunningChanged: !running ? pathView.state = 'triangle' : ''
}
]
delegate: Rectangle {
width: 20
height: 20
radius: 20
color: 'green'
}
}
Controls.Button {
anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: 'Start Animation'
onClicked: pathView.state = 'close'
}
The state names like "zigzag" and "triangle" does not resemble its real shape, just some random shape for test purposes.

Simple PathView with centered Current Item

I've just started programming in QML and I need to make a simple Carousel with some images. I've found that the simplest way to do that is to use a PathView. According to that I've tried to make my current item on the center of the view,failing. Here's the code I've done.
Rectangle {
id: rectangle
height: 200
color: "#00000000"
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.fillWidth: true
PathView {
id: carouselView
anchors.fill: parent
model: listModel
delegate: Image {
width: PathView.isCurrentItem ? 256 : 128
height: PathView.isCurrentItem ? 256 : 128
source: iconSource
}
path: Path {
startX: 0
PathLine {
x: 800
y: 0
}
}
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
}
}
ListModel {
id: listModel
ListElement {
iconSource: "qrc:///images/chair.svg"
}
ListElement {
iconSource: "qrc:///images/chair.svg"
}
ListElement {
iconSource: "qrc:///images/chair.svg"
}
ListElement {
iconSource: "qrc:///images/chair.svg"
}
}
The desired effect is a simple horizontal carousel with a centered current item.
Current version used : 5.6
Here is a simple carousel example using PathView:
import QtQuick 2.9
import QtQuick.Controls 2.2
ApplicationWindow {
visible: true
width: 640
height: 640
Component {
id: delegate
Item {
width: 200; height: 200
scale: PathView.iconScale
opacity: PathView.iconOpacity
z: PathView.iconOrder
Image { anchors.fill: parent; source: modelData }
}
}
PathView {
id: view
anchors.fill: parent
anchors.bottomMargin: 150
anchors.topMargin: 50
pathItemCount: 7
preferredHighlightBegin: 0.5 //
preferredHighlightEnd: 0.5 // That should center the current item
highlightRangeMode: PathView.StrictlyEnforceRange //
model:
[
"http://placeimg.com/200/200/any?rand=" + Math.random(),
"http://placeimg.com/200/200/any?rand=" + Math.random(),
"http://placeimg.com/200/200/any?rand=" + Math.random(),
"http://placeimg.com/200/200/any?rand=" + Math.random(),
"http://placeimg.com/200/200/any?rand=" + Math.random(),
"http://placeimg.com/200/200/any?rand=" + Math.random(),
"http://placeimg.com/200/200/any?rand=" + Math.random(),
"http://placeimg.com/200/200/any?rand=" + Math.random(),
]
delegate: delegate
path: Path {
startX: 0; startY: view.height/2
PathAttribute { name: "iconScale"; value: 0.2 }
PathAttribute { name: "iconOpacity"; value: 10.2 }
PathAttribute { name: "iconOrder"; value: 0 }
PathLine {x: view.width / 2; y: view.height/2 }
PathAttribute { name: "iconScale"; value: 1.2 }
PathAttribute { name: "iconOpacity"; value: 1 }
PathAttribute { name: "iconOrder"; value: 8 }
PathLine {x: view.width; y: view.height/2 }
}
}
}
Sure, if you really want to place current item in the center of the view you just have to change the path, i.e. move start point to the center etc.

Animate `positionViewAtIndex`

I have ListViewshowing a Model, in which, at random positions I might add a entry.
Now I want to see my new entry, and try to move the ListView to it, with the
positionViewAtIndex(newIndex, ListView.Visible)
But this change somtimes feeles very hard. Is there a posibility to smooth it with som animation?
A somewhat lengthy example to play with:
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow
{
width: 1024
height: 800
visible: true
property int lastAddedIndex: -1
onLastAddedIndexChanged: lv.positionViewAtIndex(lastAddedIndex, ListView.Contain)
Button {
property int num: 10
text: 'Add element: ' + num
onClicked: {
lastAddedIndex = Math.floor(Math.random(num) * lm.count)
lm.insert(lastAddedIndex, { num : this.num })
num++
}
}
ListModel {
id: lm
ListElement { num: 0 }
ListElement { num: 1 }
ListElement { num: 2 }
ListElement { num: 3 }
ListElement { num: 4 }
ListElement { num: 5 }
ListElement { num: 6 }
ListElement { num: 7 }
ListElement { num: 8 }
ListElement { num: 9 }
}
ListView {
id: lv
model: lm
y: 150
width: 800
height: 100
spacing: 2
clip: true
orientation: ListView.Horizontal
delegate: Rectangle {
width: 150
height: 100
border.color: 'black'
color: lastAddedIndex === index ? 'purple' : 'white'
Text {
anchors.centerIn: parent
text: index + ': ' + num
}
}
add: Transition { NumberAnimation { property: 'width'; from: 0; to: 150; duration: 600 } }
}
}
You can do it by adding addition animation element for contentX (as you have your element horizontally placed), and running it in order to animate changing view position at a given index, it will give you smooth animation:
source
import QtQuick 2.7
import QtQuick.Controls 2.0
ApplicationWindow
{
width: 1024
height: 800
visible: true
property int lastAddedIndex: -1
onLastAddedIndexChanged: gotoIndex(lastAddedIndex)
function gotoIndex(idx) {
anim.running = false;
var pos = lv.contentX;
var destPos;
lv.positionViewAtIndex(idx, ListView.Contain);
destPos = lv.contentX;
anim.from = pos;
anim.to = destPos;
anim.running = true;
}
NumberAnimation { id: anim; target: lv; property: "contentX"; duration: 500 }
Button {
property int num: 10
text: 'Add element: ' + num
onClicked: {
lastAddedIndex = Math.floor(Math.random(num) * lm.count)
lm.insert(lastAddedIndex, { num : this.num })
num++
}
}
ListModel {
id: lm
ListElement { num: 0 }
ListElement { num: 1 }
ListElement { num: 2 }
ListElement { num: 3 }
ListElement { num: 4 }
ListElement { num: 5 }
ListElement { num: 6 }
ListElement { num: 7 }
ListElement { num: 8 }
ListElement { num: 9 }
}
ListView {
id: lv
model: lm
y: 150
width: 800
height: 100
spacing: 2
clip: true
orientation: ListView.Horizontal
delegate: Rectangle {
width: 150
height: 100
border.color: 'black'
color: lastAddedIndex === index ? 'purple' : 'white'
Text {
anchors.centerIn: parent
text: index + ': ' + num
}
}
add: Transition { NumberAnimation { property: 'width'; from: 0; to: 150; duration: 600 } }
}
}

QML PathView: change current index of pathview via mouse wheel

I have created a path view with a delegate and model. Is it possible to change current index of the PathView via mouse wheel instead of mouse click drag?
Following is my PathView code:
PathView {
id: pathView;
anchors.fill: parent;
model: sampleModel;
delegate: delegate;
path: Path {
startX: 45; startY: 100
PathAttribute { name: "iconScale"; value: 0.3 }
PathAttribute { name: "iconOpacity"; value: 0.1 }
PathQuad { x: 45; y: 300; controlX: 45; controlY: 200 }
PathAttribute { name: "iconScale"; value: 1 }
PathAttribute { name: "iconOpacity"; value: 1 }
PathQuad { x: 45; y: 500; controlX: 45; controlY: 400 }
}
}
I had the the same problem. Here is my solution:
Every time a wheel event occurs, I in/decrement a "wheelIndex" and set the view.currentIndex to this value.
To reset the wheelIndex after the wheel usage, I use a timer:
PathView {
id: view
anchors.fill: parent
focus: true
model: monthModel
property int wheelIndex:- 1;
currentIndex: 0;
MouseArea {
anchors.fill: parent
preventStealing: true;
propagateComposedEvents: true
Timer {
id:wheelTimer
interval: 200;
running: false;
repeat: false
onTriggered: {
view.wheelIndex = -1;
}
}
onWheel: {
wheelTimer.start();
if (view.wheelIndex === -1) {
view.wheelIndex = view.currentIndex;
}
if (wheel.angleDelta.y > 0) {
view.wheelIndex++
if (view.wheelIndex > view.model.count) {
view.wheelIndex = 0;
}
view.currentIndex = view.wheelIndex;
} else {
view.wheelIndex--;
if (view.wheelIndex < 0) {
view.wheelIndex = view.model.count - 1;
}
view.currentIndex = view.wheelIndex;
}
}
}
}
by adding a MouseArea as child to your PathView with anchors.fill:parent and implementing the onWheel() event as follow :
PathView{
id:view
......
MouseArea{
id:mouse
anchors.fill: parent
onWheel:
{
if( wheel.angleDelta.y > 0 ) view.incrementCurrentIndex();
else view.decrementCurrentIndex();
}
}
you'll get it working with both ,mouse drag and mouse scroll (wheel).For more info look at QtQuick 5.0: WheelEvent

reducing the distance between Qml pathview members

I have been trying to implement a scroll kind of thing using the Pathview element of QML.Below image shows the progress
I want the reduce the distance between the Calendar and the Messaging element. Please help.
Below is my code of the scroll.
import QtQuick 2.0
Rectangle {
// property real rotationOrigin: PathView.origin
id: scroll
width: 800; height: 480
color: "white"
// property real rotationAngle
ListModel {
id: appModel
ListElement { name: "Music"; icon: "pics/AudioPlayer_48.png" }
ListElement { name: "Movies"; icon: "pics/VideoPlayer_48.png" }
ListElement { name: "Camera"; icon: "pics/Camera_48.png" }
ListElement { name: "Calendar"; icon: "pics/DateBook_48.png" }
ListElement { name: "Messaging"; icon: "pics/EMail_48.png" }
/*ListElement { name: "Todo List"; icon: "pics/TodoList_48.png" }
ListElement { name: "Contacts"; icon: "pics/AddressBook_48.png" }*/
}
Component {
id: appDelegate
Item {
id: item
width: 240; height: 80
scale: PathView.iconScale
property real rotationAngle: PathView.angle
transform: Rotation {
id: rotation
origin.x: item.width/2
origin.y: item.height/2
axis.x: 1; axis.y:0 ; axis.z: 0
angle: rotationAngle
}
Image {
id: myIcon
y: 20; anchors.verticalCenter: parent.verticalCenter
source: icon
smooth: true
}
Text {
anchors {
leftMargin: 10
left: myIcon.right; verticalCenter: parent.verticalCenter }
text: name
font.pointSize: 25
smooth: true
}
MouseArea {
anchors.fill: parent
onClicked: {
view.currentIndex = index
console.log("onClicked" + index);
}
}
}
}
Component {
id: appHighlight
Rectangle { width: 240; height: 80; color: "lightsteelblue" }
}
PathView {
Keys.onRightPressed: if (!moving) { incrementCurrentIndex(); console.log(moving) }
Keys.onLeftPressed: if (!moving) decrementCurrentIndex()
id: view
anchors.fill: parent
highlight: appHighlight
preferredHighlightBegin: 0.5
preferredHighlightEnd: 0.5
focus: true
model: appModel
delegate: appDelegate
path: Path {
startX: scroll.width/2
startY: 0
PathAttribute { name: "z"; value: 0 }
PathAttribute { name: "angle"; value: 75 }
PathAttribute { name: "iconScale"; value: 0.85 }
PathLine { x: scroll.width / 2; y: scroll.height / 4; }
PathAttribute { name: "z"; value: 50 }
PathAttribute { name: "angle"; value: 70 }
PathAttribute { name: "iconScale"; value: 0.85 }
PathLine { x: scroll.width /2; y: scroll.height/2; }
PathAttribute { name: "z"; value: 100 }
PathAttribute { name: "angle"; value: 0 }
PathAttribute { name: "iconScale"; value: 1.0 }
PathLine { x: scroll.width /2; y: 3*(scroll.height/4); }
PathAttribute { name: "z"; value: 50 }
PathAttribute { name: "angle"; value: -70 }
PathAttribute { name: "iconScale"; value: 0.85 }
PathLine { x: scroll.width /2; y: scroll.height; }
PathAttribute { name: "z"; value: 0 }
PathAttribute { name: "angle"; value: -75 }
PathAttribute { name: "iconScale"; value: 0.85 }
}
}
}
Did you try with PathPercent? Take a look here:
http://qmlbook.github.io/en/ch06/index.html#the-pathview

Resources