PDA

View Full Version : Animating JS-bound anchors with behavior ?



Data42
22nd January 2014, 18:42
Hi everyone :)

Here is the thing - I'm planning to release a cards game soon (regular patience games like Solitaire, Freecell etc.) developped with QtQuick and C++ on SailfishOS. I have a small problem when I want to animate nicely my cards. I use dynamical binding of anchors.top and anchors.left properties on my QML Card objects; indeed, the top anchor of a given card is the top anchor of its parent card on the stack (or the top anchor of the stack if the card is alone on a tableau), same goes for the left anchor. When dragging a card begins, the JS binding in Card sets these anchors to "undefined" to be able to freely drag the card wherever you want. Also, in y Card properties, I put a behavior on anchors.left and anchors.right to animate the anchor changes with AnchorAnimation.

Here is the part of Card.qml that is interesting:

Flipable {
id: card
property int cid
property QtObject parentCard
property QtObject parentStack
property bool isValueShown
property bool isHint
property bool canMove
property bool dragged: false


Drag.active: dragArea.drag.active
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2

Behavior on anchors.top { AnchorAnimation { duration: 1000 } }
Behavior on anchors.left { AnchorAnimation { duration: 1000 } }

function aParentIsDragged() {
// returns true if any ancestor is being dragged, false otherwise
}

height: 150
width: height * 0.7333

back: Rectangle {
anchors.fill: parent

// card back graphics...
}

front: DropArea {
enabled: !(!isValueShown || dragged || aParentIsDragged())

anchors.fill: parent
onDropped: {
gameInterface.move(drag.source.cid, parentStack.id)
}

// only card graphics here...
}

transform: Rotation {
id: rotation
origin.x: card.width / 2.0
origin.y: card.height / 2.0
axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
angle: -180 // the default angle
}

states: [
State {
name: "visible"
when: card.isValueShown

PropertyChanges {
target: rotation
angle: 0
}
}
]

transitions: Transition {
NumberAnimation { target: rotation; property: "angle"; duration: 75 }
}

MouseArea {
enabled: canMove
id: dragArea
anchors.fill: parent
drag.target: parent
onPressed: dragged = true
onCanceled: dragged = false
onReleased: {
parent.Drag.drop()
dragged = false
}

}
}

And here the part of Solitaire.qml generating the cards:

Repeater {
id: cards
property bool ready: false

model: gameInterface.cards
delegate: Card {
cid: modelData.id
parentCard: modelData.parent
parentStack: modelData.cardStack

value: modelData.value
color: modelData.color
isValueShown: modelData.isVisible
isHint: modelData.isHint
canMove: modelData.canMove

z: {
if (cards.ready)
((parentCard) ? cards.itemAt(parentCard.id).z + 1 : modelData.depth) + 100 * dragged
else
modelData.depth
}

anchors.top: (dragged || !cards.ready) ? undefined : ((parentCard) ? cards.itemAt(parentCard.id).top : stacks[parentStack.id].top) // stacks are components defined just above
anchors.left: (dragged || !cards.ready) ? undefined : ((parentCard) ? cards.itemAt(parentCard.id).left : stacks[parentStack.id].left)

anchors.topMargin: {
if (!parentCard)
0
else if (stacks[parentStack.id].type === CCardStack.FANNED_DOWN)
parentCard.isVisible * 23 + !parentCard.isVisible * 10
else if (stacks[parentStack.id].type === CCardStack.FANNED_UP)
parentCard.isVisible * -23 + !parentCard.isVisible * -10
else
0
}
anchors.leftMargin: {
if (!parentCard)
0
else if (stacks[parentStack.id].type === CCardStack.FANNED_RIGHT)
parentCard.isVisible * 23 + !parentCard.isVisible * 10
else if (stacks[parentStack.id].type === CCardStack.FANNED_LEFT)
parentCard.isVisible * -23 + !parentCard.isVisible * -10
else
0
}
}

onItemAdded: if (index + 1 == count) ready = true // I find this a little dirty, but it seems that Component.onCompleted is fired BEFORE the repeater has instanciated all its Card's
}

Sadly, the behavior is somehow not triggered... When I begin to drag, "dragged" is set to true so anchors are modified to "undefined", the card should smoothly detach from its parent and join the cursor position. And more importantly, when I drop a card on another one, I it should attach itself to its new stack smoothly too. But none of these are happening for now, and I don't know why :/ I even tried in using transitions and states, and not behavior, but I had no luck neither (moreover, latency was WAY more present with this approach, so this is not a solution, would it have worked). Notice that the margins and anchors are correctly handled (ie. the cards have always their anchors correctly updated).

Would you have some ideas on how I could make these animations possible ? Or maybe did I do something wrong ?
Thank you very much in advance! :)