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