Reputation: 3666
Using QtQuick, I have a row of 5 images in a repeater. I'd like to implement an animation on hover that's similar to the MacOS dock animation. Here's a picture for reference:
To further break it down, here's what I'm trying to accomplish. These images, on hover, should act as follows:
Here is the code I have so far
Row {
spacing: 2
anchors.bottom: parent.bottom
anchors.bottomMargin: 30
anchors.horizontalCenter: parent.horizontalCenter
Repeater {
id: iconRepeater
model: iconColors()
Image {
source: "icons/" + modelData + ".png"
scale: mouseArea.containsMouse ? 1.5 : 1.0
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: endTimer()
}
Behavior on scale {
PropertyAnimation {
duration: 75
}
}
}
}
}
This expands the image you hover over, but I cannot seem to also effect the neighbors. Any advice is appreciated!
Upvotes: 1
Views: 631
Reputation: 49279
I'd suggest a little more robust solution, where you have control over the zoom factor and the spread and decay of influence:
Column {
Slider {
id: foff
from: 1
to: 5
stepSize: 1
value: 2
snapMode: Slider.SnapAlways
}
Slider {
id: sf
from: 0.5
to: 2.5
stepSize: 0.5
value: 0.5
snapMode: Slider.SnapAlways
}
Slider {
id: dmp
from: 1
to: 5
stepSize: 1
value: 1
snapMode: Slider.SnapAlways
}
}
Row {
id: row
anchors.bottom: parent.bottom
anchors.bottomMargin: 30
anchors.horizontalCenter: parent.horizontalCenter
property int falloff: foff.value // how many adjacent elements are affected
property int current: -1
property real scaleFactor: sf.value // that's how much extra it scales
property real damp: dmp.value // decay of influence
Repeater {
id: iconRepeater
model: 10
Rectangle {
width: 50 * pseudoScale
height: width
anchors.bottom: parent.bottom
color: "red"
border.color: "black"
property real pseudoScale: {
if (row.current == -1) return 1
else {
var diff = Math.abs(index - row.current)
diff = Math.max(0, row.falloff - diff)
var damp = row.falloff - Math.max(1, diff)
var sc = row.scaleFactor
if (damp) sc /= damp * row.damp
diff = diff / row.falloff * sc + 1
return diff
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onContainsMouseChanged: row.current = containsMouse ? index : -1
}
Behavior on pseudoScale {
PropertyAnimation {
duration: 150
}
}
}
}
}
Upvotes: 7
Reputation: 13691
It could be something along the lines of this:
Row {
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
Repeater {
id: rep
model: ['red', 'yellow', 'pink', 'green', 'teal', 'orchid', 'blue', 'orange']
property int currentIndex: -10
delegate: Rectangle {
anchors.bottom: parent.bottom
// Calculate the width depending on the currently hovered element
width: (rep.currentIndex === index ? 100 : ((rep.currentIndex - index) === 1 || (rep.currentIndex - index) === -1 ? 80 : 50))
height: width
radius: width / 2
color: modelData
MouseArea {
anchors.fill: parent
hoverEnabled: true
// onEntered/Exited did not react. This will work.
onContainsMouseChanged: {
if (containsMouse) rep.currentIndex = index
else rep.currentIndex = -10 // -10 is safe
}
}
// Makes the movement smooth
Behavior on width {
NumberAnimation {}
}
}
}
}
I tried to put in the necessary explainations as comment in the code. The only thing that would need some tweeking is, that the dots will be initially shifted when the first resizing happens. Putting it on a flickable and some manual labour for the right position handeling could deal with that. Basically you need to shift the flickable by half the width change (in my case ~ 55 or so) to the left, when the mouse enters, and to the right when it leaves again.
You could also do so with a ListView, most likely, but due to the ever changing estimated size of the background, it might be more challanging to get the positioning right.
Upvotes: 1