@@ -12,6 +12,7 @@ import {
1212 createUid ,
1313 dataLabel ,
1414 downloadCsv ,
15+ easeOutCubic ,
1516 error ,
1617 getMissingDatasetAttributes ,
1718 isFunction ,
@@ -171,10 +172,6 @@ const FINAL_CONFIG = computed({
171172const isFirstLoad = ref (true );
172173const animatedValues = ref ([]);
173174
174- function easeOutCubic (t ) {
175- return 1 - Math .pow (1 - t, 3 );
176- }
177-
178175function animateWithGhost (finalValues , duration = 1000 , stagger = 50 ) {
179176 return new Promise (resolve => {
180177 const N = finalValues .length ;
@@ -280,7 +277,6 @@ const donutThickness = computed(() => {
280277
281278const emit = defineEmits ([' selectLegend' , ' selectDatapoint' ])
282279
283-
284280const immutableSet = computed (() => {
285281 return props .dataset
286282 .map ((serie , i ) => {
@@ -317,106 +313,102 @@ const rafUp = ref(null);
317313const rafDown = ref (null );
318314const isAnimating = ref (false );
319315
316+ function animateValue ({ from, to, duration, onUpdate, onDone, easing = easeOutCubic }) {
317+ const start = performance .now ();
318+ function step (now ) {
319+ const t = Math .min ((now - start) / duration, 1 );
320+ const eased = easing (t);
321+ const current = from + (to - from) * eased;
322+ onUpdate (current, t);
323+ if (t < 1 ) {
324+ requestAnimationFrame (step);
325+ } else {
326+ onUpdate (to, 1 );
327+ if (onDone) onDone ();
328+ }
329+ }
330+ requestAnimationFrame (step);
331+ }
332+
320333function segregate (index ) {
321- const target = immutableSet .value .find ((_ , idx ) => idx === index)
322- const source = mutableSet .value .find ((_ , idx ) => idx === index)
334+ const target = immutableSet .value .find ((_ , idx ) => idx === index);
335+ const source = mutableSet .value .find ((_ , idx ) => idx === index);
323336 let initVal = source .value ;
337+
324338 if (segregated .value .includes (index)) {
325339 segregated .value = segregated .value .filter (s => s !== index);
326340 const targetVal = target .value ;
327341
328342 function setFinalUpState () {
329- mutableSet .value = mutableSet .value .map ((ds , i ) => {
330- if (index === i) {
331- return {
332- ... ds,
333- value: targetVal
334- }
335- } else {
336- return ds
337- }
338- });
343+ mutableSet .value = mutableSet .value .map ((ds , i ) =>
344+ index === i ? { ... ds, value: targetVal } : ds
345+ );
339346 }
340347
341- function animUp () {
342- if (initVal > targetVal) {
343- cancelAnimationFrame (rafUp .value );
344- setFinalUpState ();
345- isAnimating .value = false ;
346- } else {
347- isAnimating .value = true ;
348- initVal += (targetVal * (FINAL_CONFIG .value .type === ' polar' ? 1 : 0.045 ));
349- mutableSet .value = mutableSet .value .map ((ds , i ) => {
350- if ((index === i)) {
351- return {
352- ... ds,
353- value: initVal
354- }
355- } else {
356- return ds
357- }
358- })
359- rafUp .value = requestAnimationFrame (animUp)
360- }
348+ function doAnimUp () {
349+ isAnimating .value = true ;
350+ animateValue ({
351+ from: initVal,
352+ to: targetVal,
353+ duration: FINAL_CONFIG .value .serieToggleAnimation .durationMs ,
354+ onUpdate : (val , t ) => {
355+ mutableSet .value = mutableSet .value .map ((ds , i ) =>
356+ index === i ? { ... ds, value: val } : ds
357+ );
358+ },
359+ onDone : () => {
360+ setFinalUpState ();
361+ isAnimating .value = false ;
362+ }
363+ });
361364 }
362365
363- if (FINAL_CONFIG .value .useSerieToggleAnimation ) {
364- animUp ();
366+ if (FINAL_CONFIG .value .serieToggleAnimation . show && FINAL_CONFIG . value . type === ' classic ' ) {
367+ doAnimUp ();
365368 } else {
366369 setFinalUpState ();
367370 }
368-
369371 } else if (segregated .value .length < immutableSet .value .length - 1 ) {
370372 function setFinalDownState () {
371373 segregated .value .push (index);
372- mutableSet .value = mutableSet .value .map ((ds , i ) => {
373- if (index === i) {
374- return {
375- ... ds,
376- value: 0 ,
377- }
378- } else {
379- return ds;
380- }
381- });
374+ mutableSet .value = mutableSet .value .map ((ds , i ) =>
375+ index === i ? { ... ds, value: 0 } : ds
376+ );
382377 }
383378
384- function animDown () {
385- if (initVal < source .value / 100 ) {
386- cancelAnimationFrame (rafDown .value );
387- setFinalDownState ();
388- isAnimating .value = false ;
389- } else {
390- isAnimating .value = true ;
391- initVal /= (FINAL_CONFIG .value .type === ' polar' ? 20 : 1.18 );
392- mutableSet .value = mutableSet .value .map ((ds , i ) => {
393- if (index === i) {
394- return {
395- ... ds,
396- value: initVal
397- }
398- } else {
399- return ds;
400- }
401- })
402- rafDown .value = requestAnimationFrame (animDown);
403- }
379+ function doAnimDown () {
380+ isAnimating .value = true ;
381+ animateValue ({
382+ from: initVal,
383+ to: 0 ,
384+ duration: FINAL_CONFIG .value .serieToggleAnimation .durationMs ,
385+ onUpdate : (val , t ) => {
386+ mutableSet .value = mutableSet .value .map ((ds , i ) =>
387+ index === i ? { ... ds, value: val } : ds
388+ );
389+ },
390+ onDone : () => {
391+ setFinalDownState ();
392+ isAnimating .value = false ;
393+ }
394+ });
404395 }
405- if (FINAL_CONFIG .value .useSerieToggleAnimation ) {
406- animDown ();
396+
397+ if (FINAL_CONFIG .value .serieToggleAnimation .show && FINAL_CONFIG .value .type === ' classic' ) {
398+ doAnimDown ();
407399 } else {
408400 setFinalDownState ();
409401 }
410402 }
411- emit (' selectLegend' , donutSet .value .map (ds => {
412- return {
413- name: ds .name ,
414- color: ds .color ,
415- value: ds .value
416- }
417- }));
403+
404+ emit (' selectLegend' , donutSet .value .map (ds => ({
405+ name: ds .name ,
406+ color: ds .color ,
407+ value: ds .value
408+ })));
418409}
419410
411+
420412const _total = computed (() => props .dataset .reduce ((sum , ds ) => sum + ds .values .reduce ((a , b ) => a + b, 0 ), 0 ));
421413
422414const donutSet = computed (() => {
@@ -922,14 +914,18 @@ defineExpose({
922914 </template >
923915 <template v-if =" FINAL_CONFIG .type === ' polar' " >
924916 <g v-for =" (arc, i) in currentDonut.filter(el => !el.ghost)" >
925- <line data-cy =" polar-datapoint" v-if =" isArcBigEnough(arc) && mutableConfig.dataLabels.show"
926- :x1 =" offsetFromCenterPoint({ initX: polarAreas[i].middlePoint.x, initY: polarAreas[i].middlePoint.y, offset: 24, centerX: svg.width / 2, centerY: svg.height / 2 }).x"
927- :y1 =" offsetFromCenterPoint({ initX: polarAreas[i].middlePoint.x, initY: polarAreas[i].middlePoint.y, offset: 24, centerX: svg.width / 2, centerY: svg.height / 2 }).y"
928- :x2 =" polarAreas[i].middlePoint.x" :y2 =" polarAreas[i].middlePoint.y" :stroke =" arc.color"
929- stroke-width =" 1" stroke-linecap =" round" stroke-linejoin =" round" fill =" none"
917+ <path
918+ data-cy =" polar-datapoint"
919+ v-if =" isArcBigEnough(arc) && mutableConfig.dataLabels.show"
920+ :d =" `M ${offsetFromCenterPoint({ initX: polarAreas[i].middlePoint.x, initY: polarAreas[i].middlePoint.y, offset: 24, centerX: svg.width / 2, centerY: svg.height / 2 }).x},${offsetFromCenterPoint({ initX: polarAreas[i].middlePoint.x, initY: polarAreas[i].middlePoint.y, offset: 24, centerX: svg.width / 2, centerY: svg.height / 2 }).y} ${polarAreas[i].middlePoint.x},${polarAreas[i].middlePoint.y}`"
921+ :stroke =" arc.color"
922+ stroke-width =" 1"
923+ stroke-linecap =" round"
924+ stroke-linejoin =" round"
925+ fill =" none"
930926 :filter =" getBlurFilter(i)"
931927 :style =" {
932- transition: isFirstLoad ? '' : ' all 0.1s ease-in-out'
928+ transition: isFirstLoad || !FINAL_CONFIG.serieToggleAnimation.show ? 'none ' : ` all ${FINAL_CONFIG.serieToggleAnimation.durationMs}ms ease-in-out`,
933929 }"
934930 />
935931 </g >
@@ -969,7 +965,7 @@ defineExpose({
969965 <path v-for =" (arc, i) in noGhostDonut" :stroke =" FINAL_CONFIG.style.chart.backgroundColor"
970966 :d =" polarAreas[i].path" fill =" #FFFFFF"
971967 :style =" {
972- transition: isFirstLoad ? 'none' : ' all 0.1s ease-in-out'
968+ transition: isFirstLoad || !FINAL_CONFIG.serieToggleAnimation.show ? 'none' : ` all ${FINAL_CONFIG.serieToggleAnimation.durationMs}ms ease-in-out`
973969 }"
974970 />
975971 <g v-if =" FINAL_CONFIG.style.chart.layout.donut.useShadow" >
@@ -979,7 +975,7 @@ defineExpose({
979975 :stroke-width =" FINAL_CONFIG.style.chart.layout.donut.borderWidth"
980976 :filter =" `url(#drop_shadow_${uid})`"
981977 :style =" {
982- transition: isFirstLoad ? 'none' : ' all 0.1s ease-in-out'
978+ transition: isFirstLoad || !FINAL_CONFIG.serieToggleAnimation.show ? 'none' : ` all ${FINAL_CONFIG.serieToggleAnimation.durationMs}ms ease-in-out`
983979 }"
984980 />
985981 </g >
@@ -992,7 +988,7 @@ defineExpose({
992988 :stroke-width =" FINAL_CONFIG.style.chart.layout.donut.borderWidth"
993989 :filter =" getBlurFilter(i)"
994990 :style =" {
995- transition: isFirstLoad ? 'none' : ' all 0.1s ease-in-out'
991+ transition: isFirstLoad || !FINAL_CONFIG.serieToggleAnimation.show ? 'none' : ` all ${FINAL_CONFIG.serieToggleAnimation.durationMs}ms ease-in-out`
996992 }"
997993 />
998994 </g >
@@ -1002,7 +998,7 @@ defineExpose({
1002998 :stroke =" FINAL_CONFIG.style.chart.backgroundColor"
1003999 :stroke-width =" FINAL_CONFIG.style.chart.layout.donut.borderWidth" :filter =" getBlurFilter(i)"
10041000 :style =" {
1005- transition: isFirstLoad ? 'none' : ' all 0.1s ease-in-out'
1001+ transition: isFirstLoad || !FINAL_CONFIG.serieToggleAnimation.show ? 'none' : ` all ${FINAL_CONFIG.serieToggleAnimation.durationMs}ms ease-in-out`
10061002 }"
10071003 />
10081004 </g >
@@ -1145,7 +1141,11 @@ defineExpose({
11451141 :fill =" arc.color" :stroke =" FINAL_CONFIG.style.chart.backgroundColor" :stroke-width =" 1"
11461142 :r =" 3"
11471143 :filter =" !FINAL_CONFIG.useBlurOnHover || [null, undefined].includes(selectedSerie) || selectedSerie === i ? `` : `url(#blur_${uid})`"
1148- @click =" selectDatapoint(arc, i)" />
1144+ @click =" selectDatapoint(arc, i)"
1145+ :style =" {
1146+ transition: isFirstLoad || !FINAL_CONFIG.serieToggleAnimation.show ? 'none' : `all ${FINAL_CONFIG.serieToggleAnimation.durationMs}ms ease-in-out`
1147+ }"
1148+ />
11491149 </template >
11501150 <template v-if =" FINAL_CONFIG .type === ' classic' " >
11511151 <text data-cy =" donut-label-value" v-if =" isArcBigEnough(arc) && mutableConfig.dataLabels.show"
@@ -1185,7 +1185,10 @@ defineExpose({
11851185 :y =" offsetFromCenterPoint({ initX: polarAreas[i].middlePoint.x, initY: polarAreas[i].middlePoint.y, offset: 42, centerX: svg.width / 2, centerY: svg.height / 2 }).y"
11861186 :fill =" FINAL_CONFIG.style.chart.layout.labels.percentage.color"
11871187 :font-size =" FINAL_CONFIG.style.chart.layout.labels.percentage.fontSize"
1188- :style =" `transition: all 0.1s ease-in-out; font-weight:${FINAL_CONFIG.style.chart.layout.labels.percentage.bold ? 'bold' : ''}`"
1188+ :style =" {
1189+ transition: isFirstLoad || !FINAL_CONFIG.serieToggleAnimation.show ? 'none' : `all ${FINAL_CONFIG.serieToggleAnimation.durationMs}ms ease-in-out`,
1190+ fontWeight: FINAL_CONFIG.style.chart.layout.labels.percentage.bold ? 'bold': 'normal'
1191+ }"
11891192 @click =" selectDatapoint(arc, i)" >
11901193 {{ displayArcPercentage(arc, noGhostDonut) }} {{
11911194 FINAL_CONFIG.style.chart.layout.labels.value.show ? `(${applyDataLabel(
@@ -1206,7 +1209,10 @@ defineExpose({
12061209 :y =" offsetFromCenterPoint({ initX: polarAreas[i].middlePoint.x, initY: polarAreas[i].middlePoint.y, offset: 42, centerX: svg.width / 2, centerY: svg.height / 2 }).y + FINAL_CONFIG.style.chart.layout.labels.percentage.fontSize"
12071210 :fill =" FINAL_CONFIG.style.chart.layout.labels.name.color"
12081211 :font-size =" FINAL_CONFIG.style.chart.layout.labels.name.fontSize"
1209- :style =" `transition: all 0.1s ease-in-out; font-weight:${FINAL_CONFIG.style.chart.layout.labels.name.bold ? 'bold' : ''}`"
1212+ :style =" {
1213+ transition: isFirstLoad || !FINAL_CONFIG.serieToggleAnimation.show ? 'none' : `all ${FINAL_CONFIG.serieToggleAnimation.durationMs}ms ease-in-out`,
1214+ fontWeight: FINAL_CONFIG.style.chart.layout.labels.name.bold ? 'bold': 'normal'
1215+ }"
12101216 @click =" selectDatapoint(arc, i)" >
12111217 {{ arc.name }}
12121218 </text >
@@ -1228,7 +1234,12 @@ defineExpose({
12281234 :x =" FINAL_CONFIG.style.chart.comments.offsetX + (getPolarAnchor(polarAreas[i].middlePoint) === 'end' ? offsetFromCenterPoint({ initX: polarAreas[i].middlePoint.x, initY: polarAreas[i].middlePoint.y, offset: 42, centerX: svg.width / 2, centerY: svg.height / 2 }).x - FINAL_CONFIG.style.chart.comments.width : getPolarAnchor(polarAreas[i].middlePoint) === 'middle' ? offsetFromCenterPoint({ initX: polarAreas[i].middlePoint.x, initY: polarAreas[i].middlePoint.y, offset: 42, centerX: svg.width / 2, centerY: svg.height / 2 }).x - (FINAL_CONFIG.style.chart.comments.width / 2) : offsetFromCenterPoint({ initX: polarAreas[i].middlePoint.x, initY: polarAreas[i].middlePoint.y, offset: 42, centerX: svg.width / 2, centerY: svg.height / 2 }).x)"
12291235 :y =" getPolarCommentY(polarAreas[i]) + FINAL_CONFIG.style.chart.comments.offsetY"
12301236 :width =" FINAL_CONFIG.style.chart.comments.width" height =" 200"
1231- style =" overflow : visible ; pointer-events : none " >
1237+ :style =" {
1238+ transition: isFirstLoad || !FINAL_CONFIG.serieToggleAnimation.show ? 'none' : `all ${FINAL_CONFIG.serieToggleAnimation.durationMs}ms ease-in-out`,
1239+ overflow: 'visible',
1240+ pointerEvents: 'none'
1241+ }"
1242+ >
12321243 <div >
12331244 <slot name =" plot-comment"
12341245 :plot =" { ...arc, textAlign: getPolarAnchor(polarAreas[i].middlePoint), flexAlign: getPolarAnchor(polarAreas[i].middlePoint), isFirstLoad }" />
0 commit comments