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