Unity 8
 All Classes Functions
Infographics.qml
1 /*
2  * Copyright (C) 2013 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 "../Components"
18 import "Gradient.js" as Gradient
19 import QtQuick 2.0
20 import Ubuntu.Components 0.1
21 
22 Item {
23  id: infographic
24 
25  property var model
26 
27  property int animDuration: 10
28 
29  QtObject {
30  id: d
31  objectName: "infographicPrivate"
32  property bool useDotAnimation: true
33  property int circleModifier: useDotAnimation ? 1 : 2
34  property bool animating: dotHideAnimTimer.running
35  || dotShowAnimTimer.running
36  || circleChangeAnimTimer.running
37  }
38 
39  Connections {
40  target: model
41 
42  onDataAboutToAppear: startHideAnimation() // hide "no data" label
43  onDataAppeared: startShowAnimation()
44 
45  onDataAboutToChange: startHideAnimation()
46  onDataChanged: startShowAnimation()
47 
48  onDataAboutToDisappear: startHideAnimation()
49  onDataDisappeared: startShowAnimation() // show "no data" label
50  }
51 
52  function startShowAnimation() {
53  dotHideAnimTimer.stop()
54  notification.hideAnim.stop()
55 
56  if (d.useDotAnimation) {
57  dotShowAnimTimer.startFromBeginning()
58  }
59  notification.showAnim.start()
60  }
61 
62  function startHideAnimation() {
63  dotShowAnimTimer.stop()
64  circleChangeAnimTimer.stop()
65  notification.showAnim.stop()
66 
67  if (d.useDotAnimation) {
68  dotHideAnimTimer.startFromBeginning()
69  } else {
70  circleChangeAnimTimer.startFromBeginning()
71  }
72  notification.hideAnim.start()
73  }
74 
75  visible: model.label !== ""
76 
77  Component.onCompleted: startShowAnimation()
78 
79  Item {
80  id: dataCircle
81  objectName: "dataCircle"
82 
83  property real divisor: 1.5
84 
85  width: Math.min(parent.height, parent.width) / divisor
86  height: width
87 
88  anchors.centerIn: parent
89 
90  Timer {
91  id: circleChangeAnimTimer
92 
93  property int pastCircleCounter
94  property int presentCircleCounter
95 
96  interval: notification.duration
97  running: false
98  repeat: true
99  onTriggered: {
100  if (pastCircleCounter < pastCircles.count) {
101  var nextCircle = pastCircles.itemAt(pastCircleCounter++)
102  if (nextCircle !== null) nextCircle.pastCircleChangeAnim.start()
103  }
104  if (pastCircleCounter > pastCircles.count / 2) {
105  var nextCircle = presentCircles.itemAt(presentCircleCounter++)
106  if (nextCircle !== null) nextCircle.presentCircleChangeAnim.start()
107  }
108  if (presentCircleCounter > infographic.model.currentDay && pastCircleCounter >= pastCircles.count) {
109  stop()
110  }
111  }
112 
113  function startFromBeginning() {
114  circleChangeAnimTimer.pastCircleCounter = 0
115  circleChangeAnimTimer.presentCircleCounter = 0
116  start()
117  }
118  }
119 
120  Repeater {
121  id: pastCircles
122  objectName: "pastCircles"
123  model: infographic.model.secondMonth
124 
125  delegate: ObjectPositioner {
126  property alias pastCircleChangeAnim: pastCircleChangeAnim
127 
128  index: model.index
129  count: pastCircles.count
130  radius: parent.width / 2
131  halfSize: pastCircle.width / 2
132  posOffset: 0.0
133 
134  Circle {
135  id: pastCircle
136  objectName: "pastCircle" + index
137 
138  property real divisor: 1.8
139  property real circleOpacity: 0.1
140 
141  width: dataCircle.width / divisor
142  height: dataCircle.height / divisor
143  opacity: 0.0
144  scale: 0.0
145  visible: modelData !== undefined
146  color: "transparent"
147 
148  SequentialAnimation {
149  id: pastCircleChangeAnim
150 
151  loops: 1
152  ParallelAnimation {
153  PropertyAnimation {
154  target: pastCircle
155  property: "opacity"
156  to: pastCircle.circleOpacity
157  easing.type: Easing.OutCurve
158  duration: circleChangeAnimTimer.interval * d.circleModifier
159  }
160  PropertyAnimation {
161  target: pastCircle
162  property: "scale"
163  to: modelData
164  easing.type: Easing.OutCurve
165  duration: circleChangeAnimTimer.interval * d.circleModifier
166  }
167  ColorAnimation {
168  target: pastCircle
169  property: "color"
170  to: Gradient.threeColorByIndex(index, count, infographic.model.secondColor)
171  easing.type: Easing.OutCurve
172  duration: circleChangeAnimTimer.interval * d.circleModifier
173  }
174  }
175  }
176  }
177  }
178  }
179 
180  Repeater {
181  id: presentCircles
182  objectName: "presentCircles"
183  model: infographic.model.firstMonth
184 
185  delegate: ObjectPositioner {
186  property alias presentCircleChangeAnim: presentCircleChangeAnim
187 
188  index: model.index
189  count: presentCircles.count
190  radius: parent.width / 2
191  halfSize: presentCircle.width / 2
192  posOffset: 0.0
193 
194  Circle {
195  id: presentCircle
196  objectName: "presentCircle" + index
197 
198  property real divisor: 1.8
199  property real circleOpacity: 0.3
200 
201  width: dataCircle.width / divisor
202  height: dataCircle.height / divisor
203  opacity: 0.0
204  scale: 0.0
205  visible: modelData !== undefined
206  color: "transparent"
207 
208  SequentialAnimation {
209  id: presentCircleChangeAnim
210 
211  loops: 1
212 
213  ParallelAnimation {
214  PropertyAnimation {
215  target: presentCircle
216  property: "opacity"
217  to: presentCircle.circleOpacity
218  easing.type: Easing.OutCurve
219  duration: circleChangeAnimTimer.interval * d.circleModifier
220  }
221  PropertyAnimation {
222  target: presentCircle
223  property: "scale"
224  to: modelData
225  easing.type: Easing.OutCurve
226  duration: circleChangeAnimTimer.interval * d.circleModifier
227  }
228  ColorAnimation {
229  target: presentCircle
230  property: "color"
231  to: Gradient.threeColorByIndex(index, infographic.model.currentDay, infographic.model.firstColor)
232  easing.type: Easing.OutCurve
233  duration: circleChangeAnimTimer.interval * d.circleModifier
234  }
235  }
236  }
237  }
238  }
239  }
240 
241  Image {
242  id: backgroundCircle
243  objectName: "backgroundCircle"
244 
245  anchors.fill: parent
246 
247  source: "graphics/infographic_circle_back.png"
248  }
249 
250  Timer {
251  id: dotShowAnimTimer
252 
253  property int dotCounter: 0
254 
255  interval: animDuration * 0.5; running: false; repeat: true
256  onTriggered: {
257  if (dotCounter < dots.count) {
258  var nextDot = dots.itemAt(dotCounter++)
259  nextDot.unlockAnimation.start()
260  } else {
261  stop()
262  }
263  if (dotCounter == Math.round(dots.count / 2)) {
264  circleChangeAnimTimer.startFromBeginning()
265  }
266  }
267 
268  function startFromBeginning() {
269  if (!dotShowAnimTimer.running)
270  dotCounter = 0
271 
272  start()
273  }
274  }
275 
276  Timer {
277  id: dotHideAnimTimer
278 
279  property int dotCounter
280 
281  interval: animDuration * 0.5
282  running: false
283  repeat: true
284  onTriggered: {
285  if (dotCounter >= 0) {
286  var nextDot = dots.itemAt(dotCounter--)
287  nextDot.changeAnimation.start()
288  } else {
289  stop()
290  }
291  if (dotCounter == 0) {
292  infographic.model.readyForDataChange()
293  }
294  }
295 
296  function startFromBeginning() {
297  if (!dotHideAnimTimer.running)
298  dotCounter = dots.count - 1
299 
300  start()
301  }
302  }
303 
304  Repeater {
305  id: dots
306  objectName: "dots"
307 
308  model: infographic.model.firstMonth
309 
310  delegate: ObjectPositioner {
311  property alias unlockAnimation: dotUnlockAnim
312  property alias changeAnimation: dotChangeAnim
313 
314  property int currentDay: infographic.model.currentDay
315 
316  index: model.index
317  count: dots.count
318  radius: backgroundCircle.width / 2
319  halfSize: dot.width / 2
320  posOffset: radius / dot.width / 3
321  state: dot.state
322 
323  Dot {
324  id: dot
325  objectName: "dot" + index
326 
327  property real baseOpacity: 0.4
328 
329  width: units.dp(5) * parent.radius / 200
330  height: units.dp(5) * parent.radius / 200
331  opacity: 0.0
332  smooth: true
333  state: index < currentDay ? "filled" : index == currentDay ? "pointer" : "unfilled"
334 
335  PropertyAnimation {
336  id: dotUnlockAnim
337 
338  target: dot
339  property: "opacity"
340  to: dot.baseOpacity
341  duration: dotShowAnimTimer.interval
342  }
343 
344  PropertyAnimation {
345  id: dotChangeAnim
346 
347  target: dot
348  property: "opacity"
349  to: 0.0
350  duration: dotHideAnimTimer.interval
351  }
352  }
353  }
354  }
355 
356  Label {
357  id: notification
358  objectName: "label"
359 
360  property alias hideAnim: decreaseOpacity
361  property alias showAnim: increaseOpacity
362 
363  property real baseOpacity: 0.6
364  property real duration: dotShowAnimTimer.interval * 5
365 
366  height: 0.7 * backgroundCircle.width
367  width: notification.height
368  anchors.centerIn: parent
369 
370  text: infographic.model.label
371 
372  wrapMode: Text.WordWrap
373  horizontalAlignment: Text.AlignHCenter
374  verticalAlignment: Text.AlignVCenter
375  color: "white"
376 
377  PropertyAnimation {
378  id: increaseOpacity
379 
380  target: notification
381  property: "opacity"
382  from: 0.0
383  to: notification.baseOpacity
384  duration: notification.duration * dots.count
385  }
386 
387  PropertyAnimation {
388  id: decreaseOpacity
389 
390  target: notification
391  property: "opacity"
392  from: notification.baseOpacity
393  to: 0.0
394  duration: notification.duration * dots.count
395  onStopped: if (!d.useDotAnimation) infographic.model.readyForDataChange()
396  }
397  }
398  }
399 
400  MouseArea {
401  anchors.fill: dataCircle
402 
403  onDoubleClicked: {
404  if (!d.animating) {
405  d.useDotAnimation = false
406  infographic.model.nextDataSource()
407  }
408  }
409 
410  onClicked: mouse.accepted = false
411  onPressed: mouse.accepted = false
412  }
413 }