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