@@ -6,6 +6,7 @@ import VChart, {
66 type ILineChartSpec ,
77 type IMark ,
88 type IMarkGraphic ,
9+ type IPieChartSpec ,
910 type ISeries ,
1011 type IWordCloudChartSpec
1112} from '../../../src' ;
@@ -218,6 +219,52 @@ const getGraphicDatum = (graphic: AnimatedGraphic) => {
218219
219220const getStateDatum = ( datum : any ) => ( Array . isArray ( datum ) ? datum [ 0 ] : datum ) ;
220221
222+ const createCustomGroupSpec = ( x : number ) : IBarChartSpec =>
223+ ( {
224+ type : 'bar' ,
225+ data : [
226+ {
227+ id : 'data' ,
228+ values : [ { x : 'A' , y : 1 } ]
229+ }
230+ ] ,
231+ xField : 'x' ,
232+ yField : 'y' ,
233+ animation : true ,
234+ customMark : [
235+ {
236+ type : 'group' ,
237+ name : 'customGroup' ,
238+ animation : true ,
239+ animationUpdate : false ,
240+ style : {
241+ x,
242+ y : 20 ,
243+ width : 40 ,
244+ height : 30
245+ }
246+ }
247+ ]
248+ } as unknown as IBarChartSpec ) ;
249+
250+ const getCustomGroupGraphic = ( chart : VChart ) => {
251+ const mark = ( chart . getChart ( ) as any ) . getAllMarks ( ) . find ( ( m : IMark ) => m . name === 'customGroup' ) ;
252+
253+ expect ( mark ) . toBeDefined ( ) ;
254+ if ( ! mark ) {
255+ throw new Error ( 'Expected custom group mark to exist' ) ;
256+ }
257+
258+ const group = mark . getGraphics ?.( ) [ 0 ] as AnimatedGraphic ;
259+
260+ expect ( group ) . toBeDefined ( ) ;
261+ if ( ! group ) {
262+ throw new Error ( 'Expected custom group graphic to exist' ) ;
263+ }
264+
265+ return group ;
266+ } ;
267+
221268const getBarGraphicByDatum = ( chart : VChart , predicate : ( datum : any ) => boolean ) => {
222269 const bar = getBarGraphics ( chart ) . find ( graphic => predicate ( getGraphicDatum ( graphic ) ) ) ;
223270
@@ -308,6 +355,25 @@ const getBarClipPathRects = (chart: VChart) =>
308355
309356const getBarClipPathGraphics = ( chart : VChart ) => ( getBarMarkProduct ( chart ) . attribute . path ?? [ ] ) as AnimatedGraphic [ ] ;
310357
358+ const getPieGraphics = ( chart : VChart ) => {
359+ const model = chart . getChart ( ) as IChart ;
360+ const pieSeries = model . getAllSeries ( ) . find ( ( series : ISeries ) => series . type === 'pie' ) ;
361+
362+ expect ( pieSeries ) . toBeDefined ( ) ;
363+ if ( ! pieSeries ) {
364+ throw new Error ( 'Expected pie series to exist' ) ;
365+ }
366+
367+ const pieMark = pieSeries . getMarks ( ) . find ( ( mark : IMark ) => mark . name === 'pie' ) ;
368+
369+ expect ( pieMark ) . toBeDefined ( ) ;
370+ if ( ! pieMark ) {
371+ throw new Error ( 'Expected pie mark to exist' ) ;
372+ }
373+
374+ return pieMark . getGraphics ( ) as AnimatedGraphic [ ] ;
375+ } ;
376+
311377const clickLegendItem = ( chart : VChart , index : number ) => {
312378 const legendModel = chart . getComponents ( ) . find ( ( component : any ) => component . type === 'discreteLegend' ) as any ;
313379 const legendComponent = legendModel ?. _legendComponent ;
@@ -469,6 +535,57 @@ const createSeriesChangeDocBarSpec = (mode: 'grouped' | 'single') => {
469535 } as unknown as IBarChartSpec ;
470536} ;
471537
538+ const createPieLegendFilterSpec = ( ) : IPieChartSpec =>
539+ ( {
540+ type : 'pie' ,
541+ width : 500 ,
542+ height : 500 ,
543+ padding : 0 ,
544+ data : [
545+ {
546+ id : 'id0' ,
547+ values : [
548+ { group : '0' , value : 455 , labelValue : '455' , dataIndex : 0 , seriesIndex : 0 , name : '满意' } ,
549+ { group : '0' , value : 655 , labelValue : '655' , dataIndex : 1 , seriesIndex : 0 , name : '一般' } ,
550+ { group : '0' , value : 160 , labelValue : '160' , dataIndex : 2 , seriesIndex : 0 , name : '差评' }
551+ ]
552+ }
553+ ] ,
554+ categoryField : 'dataIndex' ,
555+ valueField : 'value' ,
556+ startAngle : - 90 ,
557+ endAngle : - 90 ,
558+ animation : true ,
559+ animationAppear : {
560+ duration : APPEAR_DURATION ,
561+ easing : 'linear'
562+ } ,
563+ animationUpdate : false ,
564+ animationEnter : false ,
565+ animationExit : false ,
566+ animationDisappear : false ,
567+ legends : {
568+ orient : 'bottom' ,
569+ position : 'middle' ,
570+ visible : true ,
571+ allowAllCanceled : false ,
572+ item : {
573+ visible : true
574+ }
575+ } ,
576+ pie : {
577+ state : {
578+ hover : {
579+ outerRadius : 1.06
580+ }
581+ } ,
582+ style : {
583+ cursor : 'pointer' ,
584+ lineWidth : 2
585+ }
586+ }
587+ } as unknown as IPieChartSpec ) ;
588+
472589const createLineGrowthSpec = ( values : Array < { time : string ; value : number } > ) =>
473590 ( {
474591 type : 'line' ,
@@ -1282,6 +1399,89 @@ const hasRenderableBarGeometry = (graphic: AnimatedGraphic) => {
12821399} ;
12831400
12841401describe ( 'manual ticker animation regressions' , ( ) => {
1402+ it ( 'keeps custom group final attributes after a prevented update animation' , ( ) => {
1403+ const { container, dom } = createChartContainer ( ) ;
1404+ const ticker = createManualTicker ( ) ;
1405+ const chart = new VChart ( createCustomGroupSpec ( 20 ) , {
1406+ dom,
1407+ ticker,
1408+ animation : true
1409+ } ) ;
1410+
1411+ chart . renderSync ( ) ;
1412+
1413+ try {
1414+ ticker . tickAt ( APPEAR_DURATION + 50 ) ;
1415+
1416+ chart . updateSpecSync ( createCustomGroupSpec ( 80 ) ) ;
1417+
1418+ const group = getCustomGroupGraphic ( chart ) ;
1419+
1420+ expectClose ( group . attribute . x , 80 ) ;
1421+ expectClose ( group . baseAttributes ?. x , 80 ) ;
1422+ expectClose ( getGraphicFinalAttribute ( group ) . x , 80 ) ;
1423+ } finally {
1424+ chart . release ( ) ;
1425+ ticker . release ( ) ;
1426+ removeDom ( container ) ;
1427+ }
1428+ } ) ;
1429+
1430+ it ( 'keeps pie arc angles stable after legend filtering and hover state changes' , ( ) => {
1431+ const { container, dom } = createChartContainer ( ) ;
1432+ const ticker = createManualTicker ( ) ;
1433+ const chart = new VChart ( createPieLegendFilterSpec ( ) , {
1434+ dom,
1435+ ticker,
1436+ animation : true
1437+ } ) ;
1438+
1439+ chart . renderSync ( ) ;
1440+
1441+ try {
1442+ ticker . tickAt ( APPEAR_DURATION + 50 ) ;
1443+
1444+ chart . setLegendSelectedDataByIndex ( 0 , [ 0 , 1 ] ) ;
1445+ chart . renderSync ( ) ;
1446+
1447+ const updateStart = ticker . getTime ( ) ;
1448+ ticker . tickAt ( updateStart + UPDATE_DURATION + 50 ) ;
1449+
1450+ const secondPie = getPieGraphics ( chart ) . find ( graphic => graphic . context . data [ 0 ] ?. dataIndex === 1 ) ;
1451+ const expectedStartAngle = - Math . PI / 2 + ( Math . PI * 2 * 455 ) / ( 455 + 655 ) ;
1452+ const expectedEndAngle = - Math . PI / 2 + Math . PI * 2 ;
1453+
1454+ expect ( secondPie ) . toBeDefined ( ) ;
1455+ if ( ! secondPie ) {
1456+ throw new Error ( 'Expected second pie graphic to exist' ) ;
1457+ }
1458+
1459+ expectClose ( secondPie . attribute . startAngle , expectedStartAngle ) ;
1460+ expectClose ( secondPie . attribute . endAngle , expectedEndAngle ) ;
1461+ expectClose ( secondPie . baseAttributes ?. startAngle , expectedStartAngle ) ;
1462+ expectClose ( secondPie . baseAttributes ?. endAngle , expectedEndAngle ) ;
1463+ expectClose ( getGraphicFinalAttribute ( secondPie ) . startAngle , expectedStartAngle ) ;
1464+ expectClose ( getGraphicFinalAttribute ( secondPie ) . endAngle , expectedEndAngle ) ;
1465+
1466+ secondPie . useStates ( [ 'hover' ] ) ;
1467+ expectClose ( secondPie . attribute . startAngle , expectedStartAngle ) ;
1468+ expectClose ( secondPie . attribute . endAngle , expectedEndAngle ) ;
1469+
1470+ ticker . tickAt ( ticker . getTime ( ) + UPDATE_DURATION + 50 ) ;
1471+
1472+ expectClose ( secondPie . attribute . startAngle , expectedStartAngle ) ;
1473+ expectClose ( secondPie . attribute . endAngle , expectedEndAngle ) ;
1474+ expectClose ( secondPie . baseAttributes ?. startAngle , expectedStartAngle ) ;
1475+ expectClose ( secondPie . baseAttributes ?. endAngle , expectedEndAngle ) ;
1476+ expectClose ( getGraphicFinalAttribute ( secondPie ) . startAngle , expectedStartAngle ) ;
1477+ expectClose ( getGraphicFinalAttribute ( secondPie ) . endAngle , expectedEndAngle ) ;
1478+ } finally {
1479+ chart . release ( ) ;
1480+ ticker . release ( ) ;
1481+ removeDom ( container ) ;
1482+ }
1483+ } ) ;
1484+
12851485 it ( 'runs word cloud scaleIn appear from zero scale' , ( ) => {
12861486 const { container, dom } = createChartContainer ( ) ;
12871487 const ticker = createManualTicker ( ) ;
0 commit comments