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