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