11import { AttributeUpdateType , type EasingType , type IAnimate , type IStep } from '@visactor/vrender-core' ;
22import { ACustomAnimate } from './custom-animate' ;
3+ import { applyAnimationTransientAttributes } from './transient' ;
4+
5+ const clipPathGeometryAttrs : Record < string , true > = {
6+ x : true ,
7+ y : true ,
8+ x1 : true ,
9+ y1 : true ,
10+ width : true ,
11+ height : true
12+ } ;
313
414export interface IUpdateAnimationOptions {
515 diffAttrs : Record < string , any > ;
@@ -16,6 +26,10 @@ export interface IUpdateAnimationOptions {
1626export class Update extends ACustomAnimate < Record < string , number > > {
1727 declare valid : boolean ;
1828 // params: IUpdateAnimationOptions;
29+ private clipPathSyncKeys : string [ ] | null = null ;
30+ private clipPathSyncParent : any = null ;
31+ private clipPathSyncChildIndex : number = - 1 ;
32+ private clipPathSyncDisabled : boolean = false ;
1933
2034 constructor ( from : null , to : null , duration : number , easing : EasingType , params ?: IUpdateAnimationOptions ) {
2135 super ( from , to , duration , easing , params ) ;
@@ -35,6 +49,9 @@ export class Update extends ACustomAnimate<Record<string, number>> {
3549 }
3650
3751 this . props = diffAttrs ;
52+ this . clipPathSyncKeys = Object . keys ( diffAttrs ) . filter ( key => clipPathGeometryAttrs [ key ] ) ;
53+ this . clipPathSyncDisabled = ! this . clipPathSyncKeys . length ;
54+ this . syncParentClipPathToTarget ( ) ;
3855 }
3956
4057 private getStaticCommitAttrs ( ) : Record < string , any > | null {
@@ -78,6 +95,7 @@ export class Update extends ACustomAnimate<Record<string, number>> {
7895 if ( commitAttrs ) {
7996 this . target . setAttributes ( commitAttrs , false , { type : AttributeUpdateType . ANIMATE_END } ) ;
8097 }
98+ this . syncParentClipPathToTarget ( ) ;
8199 super . onEnd ( ) ;
82100 }
83101
@@ -100,6 +118,106 @@ export class Update extends ACustomAnimate<Record<string, number>> {
100118 const toValue = this . props [ key ] ;
101119 func ( key , fromValue , toValue , easedRatio , this , this . target ) ;
102120 } ) ;
121+ this . syncParentClipPathToTarget ( ) ;
103122 this . onUpdate ( end , easedRatio , out ) ;
104123 }
124+
125+ private syncParentClipPathToTarget ( ) : void {
126+ if ( this . clipPathSyncDisabled ) {
127+ return ;
128+ }
129+
130+ const target = this . target as any ;
131+ const parent = target . parent as any ;
132+ const path = parent ?. attribute ?. path ;
133+ if ( ! parent ?. attribute ?. clip || ! Array . isArray ( path ) || ! path . length ) {
134+ return ;
135+ }
136+
137+ const childIndex = this . getClipPathSyncChildIndex ( parent ) ;
138+ if ( childIndex < 0 || childIndex >= path . length ) {
139+ return ;
140+ }
141+
142+ const clipGraphic = path [ childIndex ] as any ;
143+ if ( ! clipGraphic ?. attribute || clipGraphic . type !== target . type || ! this . isClipPathStaticTarget ( clipGraphic ) ) {
144+ this . clipPathSyncDisabled = true ;
145+ return ;
146+ }
147+
148+ const syncAttrs = this . buildClipPathTransientAttrs ( clipGraphic ) ;
149+ if ( syncAttrs ) {
150+ applyAnimationTransientAttributes ( clipGraphic , syncAttrs , AttributeUpdateType . ANIMATE_UPDATE ) ;
151+ }
152+ }
153+
154+ private getClipPathSyncChildIndex ( parent : any ) : number {
155+ if ( this . clipPathSyncParent === parent && this . clipPathSyncChildIndex >= 0 ) {
156+ return this . clipPathSyncChildIndex ;
157+ }
158+
159+ const target = this . target as any ;
160+ let childIndex = - 1 ;
161+ parent . forEachChildren ?.( ( child : unknown , index : number ) => {
162+ if ( child === target ) {
163+ childIndex = index ;
164+ return true ;
165+ }
166+ return false ;
167+ } ) ;
168+
169+ this . clipPathSyncParent = parent ;
170+ this . clipPathSyncChildIndex = childIndex ;
171+ return childIndex ;
172+ }
173+
174+ private isClipPathStaticTarget ( clipGraphic : any ) : boolean {
175+ const target = this . target as any ;
176+ const targetFinalAttrs = this . getTargetFinalAttrs ( ) ;
177+ const clipGraphicFinalAttrs =
178+ typeof clipGraphic . getFinalAttribute === 'function'
179+ ? clipGraphic . getFinalAttribute ( )
180+ : clipGraphic . finalAttribute ;
181+ const clipFinalAttrs = clipGraphicFinalAttrs ?? clipGraphic . baseAttributes ?? clipGraphic . attribute ;
182+ const keys = this . clipPathSyncKeys ?? [ ] ;
183+ if ( ! keys . length || ! targetFinalAttrs || ! clipFinalAttrs ) {
184+ return false ;
185+ }
186+
187+ return keys . every ( key =>
188+ this . isSameClipPathValue ( clipFinalAttrs [ key ] , targetFinalAttrs [ key ] ?? target . attribute ?. [ key ] )
189+ ) ;
190+ }
191+
192+ private getTargetFinalAttrs ( ) : Record < string , any > | null {
193+ const target = this . target as any ;
194+ return (
195+ target . context ?. finalAttrs ??
196+ ( typeof target . getFinalAttribute === 'function' ? target . getFinalAttribute ( ) : target . finalAttribute ) ??
197+ null
198+ ) ;
199+ }
200+
201+ private isSameClipPathValue ( a : any , b : any ) : boolean {
202+ if ( typeof a === 'number' && typeof b === 'number' ) {
203+ return Math . abs ( a - b ) < 1e-8 ;
204+ }
205+ return a === b ;
206+ }
207+
208+ private buildClipPathTransientAttrs ( clipGraphic : any ) : Record < string , any > | null {
209+ const target = this . target as any ;
210+ const attrs : Record < string , any > = { } ;
211+ ( this . clipPathSyncKeys ?? [ ] ) . forEach ( key => {
212+ const nextValue = target . attribute ?. [ key ] ;
213+ if (
214+ Object . prototype . hasOwnProperty . call ( clipGraphic . attribute , key ) &&
215+ nextValue !== undefined &&
216+ ! this . isSameClipPathValue ( clipGraphic . attribute [ key ] , nextValue )
217+ ) {
218+ attrs [ key ] = nextValue ;
219+ }
220+ } ) ;
221+ return Object . keys ( attrs ) . length ? attrs : null ;
222+ }
105223}
0 commit comments