1
+ <script setup>
2
+ import { ref , computed , nextTick , onMounted } from " vue" ;
3
+ import pdf from " ../pdf" ;
4
+ import img from " ../img" ;
5
+ import mainConfig from " ../default_configs.json" ;
6
+ import { useNestedProp } from " ../useNestedProp" ;
7
+ import Title from " ../atoms/Title.vue" ;
8
+ import UserOptions from " ../atoms/UserOptions.vue" ;
9
+ import { shiftHue } from " ../lib" ;
10
+
11
+ const props = defineProps ({
12
+ config: {
13
+ type: Object ,
14
+ default () {
15
+ return {}
16
+ }
17
+ },
18
+ dataset: {
19
+ type: Object ,
20
+ default () {
21
+ return {}
22
+ }
23
+ },
24
+ });
25
+
26
+ const uid = ref (` vue-ui-wheel-${ Math .random ()} ` );
27
+ const defaultConfig = ref (mainConfig .vue_ui_wheel );
28
+
29
+ const isImaging = ref (false );
30
+ const isPrinting = ref (false );
31
+ const wheelChart = ref (null );
32
+ const details = ref (null );
33
+
34
+
35
+ const wheelConfig = computed (() => {
36
+ return useNestedProp ({
37
+ userConfig: props .config ,
38
+ defaultConfig: defaultConfig .value
39
+ });
40
+ });
41
+
42
+ const svg = ref ({
43
+ size: 360
44
+ })
45
+
46
+ const wheel = computed (() => {
47
+ return {
48
+ radius: (svg .value .size * 0.9 ) / 2 ,
49
+ centerX: svg .value .size / 2 ,
50
+ centerY: svg .value .size / 2 ,
51
+ }
52
+ })
53
+
54
+ function calcTickStart (angle , distance = 1 ) {
55
+ const angleStart = 29.85 ;
56
+ return {
57
+ x: wheel .value .centerX + wheel .value .radius * Math .cos (angleStart + angle * Math .PI / 180 ) * distance,
58
+ y: wheel .value .centerY + wheel .value .radius * Math .sin (angleStart + angle * Math .PI / 180 ) * distance
59
+ }
60
+ }
61
+
62
+ const activeValue = ref (wheelConfig .value .style .chart .animation .use ? 0 : props .dataset .percentage );
63
+
64
+ onMounted (() => {
65
+ let acceleration = 0 ;
66
+ let speed = wheelConfig .value .style .chart .animation .speed ;
67
+ let incr = (0.005 ) * wheelConfig .value .style .chart .animation .acceleration ;
68
+ function animate () {
69
+ activeValue .value += speed + acceleration;
70
+ acceleration += incr;
71
+ if (activeValue .value < props .dataset .percentage ) {
72
+ requestAnimationFrame (animate)
73
+ } else {
74
+ activeValue .value = props .dataset .percentage
75
+ }
76
+ }
77
+
78
+ if (wheelConfig .value .style .chart .animation .use ) {
79
+ activeValue .value = 0 ;
80
+ animate ()
81
+ }
82
+ })
83
+
84
+ const ticks = computed (() => {
85
+ const tickArray = [];
86
+ const tickAmount = 100 ;
87
+ for (let i = 0 ; i < tickAmount; i += 1 ) {
88
+ const color = activeValue .value > i ? wheelConfig .value .style .chart .layout .wheel .ticks .activeColor : wheelConfig .value .style .chart .layout .wheel .ticks .inactiveColor ;
89
+ tickArray .push ({
90
+ x1: calcTickStart ((360 / tickAmount) * i).x ,
91
+ y1: calcTickStart ((360 / tickAmount) * i).y ,
92
+ x2: calcTickStart ((360 / tickAmount) * i, 0.9 ).x ,
93
+ y2: calcTickStart ((360 / tickAmount) * i, 0.9 ).y ,
94
+ color: wheelConfig .value .style .chart .layout .wheel .ticks .gradient .show ? shiftHue (color, i / tickAmount * (wheelConfig .value .style .chart .layout .wheel .ticks .gradient .shiftHueIntensity / 100 )) : color
95
+ })
96
+ }
97
+ return tickArray
98
+ })
99
+
100
+ const __to__ = ref (null );
101
+
102
+ function showSpinnerPdf () {
103
+ isPrinting .value = true ;
104
+ }
105
+
106
+ function generatePdf (){
107
+ showSpinnerPdf ();
108
+ clearTimeout (__to__ .value );
109
+ __to__ .value = setTimeout (() => {
110
+ pdf ({
111
+ domElement: document .getElementById (` ${ uid .value } ` ),
112
+ fileName: wheelConfig .value .style .chart .title .text || ' vue-ui-wheel'
113
+ }).finally (() => {
114
+ isPrinting .value = false ;
115
+ })
116
+ }, 100 )
117
+ }
118
+
119
+ function showSpinnerImage () {
120
+ isImaging .value = true ;
121
+ }
122
+
123
+ function generateImage () {
124
+ showSpinnerImage ();
125
+ clearTimeout (__to__ .value );
126
+ __to__ .value = setTimeout (() => {
127
+ img ({
128
+ domElement: document .getElementById (` ${ uid .value } ` ),
129
+ fileName: wheelConfig .value .style .chart .title .text || ' vue-ui-wheel' ,
130
+ format: ' png'
131
+ }).finally (() => {
132
+ isImaging .value = false ;
133
+ })
134
+ }, 100 )
135
+ }
136
+
137
+ </script >
138
+
139
+ <template >
140
+ <div
141
+ class =" vue-ui-wheel"
142
+ ref =" wheelChart"
143
+ :id =" uid"
144
+ :style =" `font-family:${wheelConfig.style.fontFamily};width:100%; text-align:center;${wheelConfig.userOptions.show ? 'padding-top:36px' : ''}`"
145
+ >
146
+
147
+ <div v-if =" wheelConfig.style.chart.title.text" :style =" `width:100%;background:${wheelConfig.style.chart.backgroundColor};padding-bottom:12px`" >
148
+ <Title
149
+ :config =" {
150
+ title: {
151
+ cy: 'wheel-title',
152
+ text: wheelConfig.style.chart.title.text,
153
+ color: wheelConfig.style.chart.title.color,
154
+ fontSize: wheelConfig.style.chart.title.fontSize,
155
+ bold: wheelConfig.style.chart.title.bold
156
+ },
157
+ subtitle: {
158
+ cy: 'wheel-subtitle',
159
+ text: wheelConfig.style.chart.title.subtitle.text,
160
+ color: wheelConfig.style.chart.title.subtitle.color,
161
+ fontSize: wheelConfig.style.chart.title.subtitle.fontSize,
162
+ bold: wheelConfig.style.chart.title.subtitle.bold
163
+ },
164
+ }"
165
+ />
166
+ </div >
167
+
168
+ <UserOptions
169
+ ref =" details"
170
+ v-if =" wheelConfig.userOptions.show"
171
+ :backgroundColor =" wheelConfig.style.chart.backgroundColor"
172
+ :color =" wheelConfig.style.chart.color"
173
+ :isPrinting =" isPrinting"
174
+ :isImaging =" isImaging"
175
+ :title =" wheelConfig.userOptions.title"
176
+ :uid =" uid"
177
+ :hasImg =" true"
178
+ :hasXls =" false"
179
+ @generatePdf =" generatePdf"
180
+ @generateImage =" generateImage"
181
+ />
182
+
183
+ <svg data-cy =" wheel-svg" :viewBox =" `0 0 ${svg.size} ${svg.size}`" :style =" `max-width:100%;overflow:visible;background:${wheelConfig.style.chart.backgroundColor};color:${wheelConfig.style.chart.color}`" >
184
+ <line
185
+ v-for =" (tick, i) in ticks"
186
+ :x1 =" tick.x1"
187
+ :x2 =" tick.x2"
188
+ :y1 =" tick.y1"
189
+ :y2 =" tick.y2"
190
+ :stroke =" tick.color"
191
+ :stroke-width =" 5"
192
+ :stroke-linecap =" wheelConfig.style.chart.layout.wheel.ticks.rounded ? 'round' : 'butt'"
193
+ />
194
+ <circle
195
+ v-if =" wheelConfig.style.chart.layout.innerCircle.show"
196
+ :cx =" wheel.centerX"
197
+ :cy =" wheel.centerY"
198
+ :r =" wheel.radius * 0.8"
199
+ :stroke =" wheelConfig.style.chart.layout.innerCircle.stroke"
200
+ :stroke-width =" wheelConfig.style.chart.layout.innerCircle.strokeWidth"
201
+ fill =" none"
202
+ />
203
+ <text
204
+ v-if =" wheelConfig.style.chart.layout.percentage.show"
205
+ :x =" wheel.centerX"
206
+ :y =" wheel.centerY + wheelConfig.style.chart.layout.percentage.fontSize / 3"
207
+ :font-size =" wheelConfig.style.chart.layout.percentage.fontSize"
208
+ :fill =" wheelConfig.style.chart.layout.wheel.ticks.gradient.show ? shiftHue(wheelConfig.style.chart.layout.wheel.ticks.activeColor, activeValue / 100 * (wheelConfig.style.chart.layout.wheel.ticks.gradient.shiftHueIntensity / 100)) : wheelConfig.style.chart.layout.wheel.ticks.activeColor"
209
+ text-anchor =" middle"
210
+ :font-weight =" wheelConfig.style.chart.layout.percentage.bold ? 'bold' : 'normal'"
211
+ >
212
+ {{ Number(activeValue.toFixed(wheelConfig.style.chart.layout.percentage.rounding)).toLocaleString() }}%
213
+ </text >
214
+ </svg >
215
+
216
+ </div >
217
+ </template >
218
+
219
+ <style scoped>
220
+ .vue-ui-wheel * {
221
+ transition : unset ;
222
+ }
223
+ .vue-ui-wheel {
224
+ user-select : none ;
225
+ position : relative ;
226
+ }
227
+ </style >
0 commit comments