Unity 8
SpreadMaths.qml
1 /*
2  * Copyright (C) 2015 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 3.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 import QtQuick 2.4
18 import Ubuntu.Components 1.3
19 import Utils 0.1
20 
21 Item {
22  id: root
23  anchors { left: parent.left; top: parent.top; margins: units.gu(1) }
24 
25  // Information about the transformed item
26  property int itemIndex: 0
27  property real itemHeight: units.gu(10)
28 
29  // Information about the environment
30  property int totalItems: 0
31  property Item flickable: null
32  property int sceneHeight: units.gu(20)
33 
34  // Spread properties
35  property real spreadHeight: sceneHeight * 0.4
36  property int spreadBottomOffset: sceneHeight * 0.18
37  property int foldingAreaWidth: flickableWidth * 0.2
38  property int maxVisibleItems: 7
39  property int margins: flickableWidth * 0.05
40  property real stackScale: 0.1
41  property int leftEndFoldedAngle: 70
42  property int rightEndFoldedAngle: 65
43  property int unfoldedAngle: 30
44  property int stackWidth: flickableWidth * 0.01
45 
46 
47  // Internal
48  readonly property int flickableWidth: flickable ? flickable.width : 0
49  readonly property int flickableContentWidth: flickable ? flickable.contentWidth: 0
50  readonly property real flickableProgress: flickable ? flickable.contentX / (flickable.contentWidth - flickableWidth) : 0
51 
52  readonly property int contentWidth: flickableWidth - root.margins * 2
53 
54  readonly property int distance: (flickableContentWidth - (margins * 2) - (foldingAreaWidth * 2)) / (totalItems - 2)
55  readonly property int startPos: margins + foldingAreaWidth + (itemIndex - 1) * distance
56  readonly property int linearX: startPos - flickableProgress * (flickableContentWidth - flickableWidth)
57 
58  readonly property int leftFoldingAreaX: margins + foldingAreaWidth
59  readonly property int rightFoldingAreaX: flickableWidth - foldingAreaWidth - margins
60 
61  readonly property real leftFoldingAreaProgress: linearAnimation(leftFoldingAreaX, margins, 0, 1, linearX)
62  readonly property real rightFoldingAreaProgress: linearAnimation(rightFoldingAreaX, flickableWidth - margins, 0, 1, linearX)
63 
64  readonly property real limitedLeftProgress: Math.min(2, leftFoldingAreaProgress)
65  readonly property real limitedRightProgress: Math.min(2, rightFoldingAreaProgress)
66 
67  readonly property real middleSectionProgress: (linearX - margins - foldingAreaWidth) / (flickableWidth - (margins + foldingAreaWidth) * 2)
68 
69  // Output
70  readonly property int animatedX: {
71  if (leftFoldingAreaProgress > 4) { // Stop it at the edge
72  return margins;
73  }
74  if (leftFoldingAreaProgress > 2) { // move it slowly through the stack
75  return linearAnimation(2, 4, margins + stackWidth, margins, leftFoldingAreaProgress)
76  }
77  if (leftFoldingAreaProgress > 1 && itemIndex == 0) {
78  // The leftmost runs faster... make it stop before the stack and wait for others
79  return margins + stackWidth;
80  }
81 
82  if (leftFoldingAreaProgress > 0) { // slow it down in a curve
83  if (itemIndex == 0) { // except if it's the leftmost. that one goes straigt
84  return linearAnimation(0, 1, leftFoldingAreaX, margins + stackWidth, leftFoldingAreaProgress)
85  }
86  return linearAnimation(0, 1, leftFoldingAreaX, margins + stackWidth, leftEasing.value)
87  }
88  // same for the right side stack... mostly... don't need to treat the rightmost special...
89  if (rightFoldingAreaProgress > 4) {
90  return flickableWidth - margins
91  }
92  if (rightFoldingAreaProgress > 2) {
93  return linearAnimation(2, 4, flickableWidth - margins - stackWidth, flickableWidth - margins, rightFoldingAreaProgress)
94  }
95 
96  if (rightFoldingAreaProgress > 0) {
97  return linearAnimation(0, 1, rightFoldingAreaX, flickableWidth - margins - stackWidth, rightEasing.value);
98  }
99 
100  return linearX
101  }
102 
103  readonly property int animatedY: sceneHeight - itemHeight - spreadBottomOffset
104 
105  readonly property real animatedAngle: {
106  if (limitedLeftProgress > 0) {
107  // Leftmost is special...
108  if (index == 0) {
109  if (limitedLeftProgress < 1) {
110  return unfoldedAngle;
111  } else {
112  return linearAnimation(1, 2, unfoldedAngle, leftEndFoldedAngle, limitedLeftProgress)
113  }
114  }
115  return linearAnimation(0, 2, unfoldedAngle, leftEndFoldedAngle, limitedLeftProgress)
116  } else if (limitedRightProgress > 0) {
117  return linearAnimation(0, 1, unfoldedAngle, rightEndFoldedAngle, rightEasing.value)
118  } else {
119  return unfoldedAngle
120  }
121  }
122 
123  readonly property real scale: limitedLeftProgress > 0 ?
124  linearAnimation(0, 1, 1, 1 + stackScale, leftEasing.value)
125  : limitedRightProgress > 0 ?
126  linearAnimation(0, 1, 1, 1 + stackScale, rightEasing.value)
127  : 0.95 + Math.abs(middleSectionProgress - 0.5) * 0.1
128 
129  readonly property real closeIconOffset: (scale - 1) * (-root.spreadHeight / 2)
130 
131  readonly property real tileInfoOpacity: leftFoldingAreaProgress > 0 ?
132  linearAnimation(1, 1.5, 1, 0, leftFoldingAreaProgress)
133  : rightFoldingAreaProgress > 0 ?
134  linearAnimation(1, 1.5, 1, 0, rightFoldingAreaProgress)
135  : 1
136 
137  readonly property bool itemVisible: itemIndex == totalItems - 1 ? true : leftFoldingAreaProgress < 5 && rightFoldingAreaProgress < 5
138  readonly property real shadowOpacity: itemIndex == totalItems -1 ?
139  1
140  : leftFoldingAreaProgress > 3 ?
141  linearAnimation(3, 3.5, 1, 0, leftFoldingAreaProgress)
142  : rightFoldingAreaProgress > 3 ?
143  linearAnimation(3, 3.5, 1, 0, rightFoldingAreaProgress)
144  : 1
145 
146 
147  // Helpers
148  function linearAnimation(startProgress, endProgress, startValue, endValue, progress) {
149  // progress : progressDiff = value : valueDiff => value = progress * valueDiff / progressDiff
150  return (progress - startProgress) * (endValue - startValue) / (endProgress - startProgress) + startValue;
151  }
152 
153  EasingCurve {
154  id: leftEasing
155  type: EasingCurve.OutSine
156  progress: limitedLeftProgress / 2 // OutSine starts with twice the speed. slow it down.
157  }
158 
159  EasingCurve {
160  id: rightEasing
161  type: EasingCurve.OutSine
162  progress: limitedRightProgress / 2 // OutSine starts with twice the speed. slow it down.
163  }
164 }