1+ import debounce from "lodash/debounce" ;
2+
13import { cache } from "../../../../lib/utils" ;
24
35export type Path2DRenderStyleResult =
@@ -9,40 +11,56 @@ export interface Path2DRenderInstance {
911 getPath ( ) : Path2D | undefined | null ;
1012 style ( ctx : CanvasRenderingContext2D ) : Path2DRenderStyleResult | undefined ;
1113 afterRender ?( ctx : CanvasRenderingContext2D ) : void ;
14+ isPathVisible ?( ) : boolean ;
1215}
1316
14- class Path2DGroup {
17+ const CHUNK_SIZE = 100 ;
18+
19+ class Path2DChunk {
1520 protected items : Set < Path2DRenderInstance > = new Set ( ) ;
1621
22+ protected visibleItems = cache ( ( ) => {
23+ return Array . from ( this . items ) . filter ( ( item ) => item . isPathVisible ?.( ) ?? true ) ;
24+ } ) ;
25+
1726 protected path = cache ( ( ) => {
1827 const path = new Path2D ( ) ;
1928 path . moveTo ( 0 , 0 ) ;
20- return Array . from ( this . items ) . reduce ( ( path , item ) => {
21- const subPath = item . getPath ( ) ;
22- if ( subPath ) {
23- path . addPath ( subPath ) ;
29+ return this . visibleItems . get ( ) . reduce ( ( path , item ) => {
30+ if ( item . isPathVisible ?.( ) ?? true ) {
31+ const subPath = item . getPath ( ) ;
32+ if ( subPath ) {
33+ path . addPath ( subPath ) ;
34+ }
2435 }
2536 return path ;
2637 } , path ) ;
2738 } ) ;
2839
2940 protected applyStyles ( ctx ) {
30- const val = Array . from ( this . items ) [ 0 ] ;
31- return val . style ( ctx ) ;
41+ const val = Array . from ( this . items ) . find ( ( item ) => item . isPathVisible ?. ( ) ?? true ) ;
42+ return val ? .style ( ctx ) ;
3243 }
3344
3445 public add ( item : Path2DRenderInstance ) {
3546 this . items . add ( item ) ;
36- this . path . reset ( ) ;
47+ this . reset ( ) ;
3748 }
3849
39- public delete ( item ) {
50+ public delete ( item : Path2DRenderInstance ) {
4051 this . items . delete ( item ) ;
52+ this . reset ( ) ;
53+ }
54+
55+ public reset ( ) {
4156 this . path . reset ( ) ;
57+ this . visibleItems . reset ( ) ;
4258 }
4359
4460 public render ( ctx : CanvasRenderingContext2D ) {
45- if ( this . items . size ) {
61+ const visibleItems = this . visibleItems . get ( ) ;
62+
63+ if ( visibleItems . length ) {
4664 ctx . save ( ) ;
4765
4866 const result = this . applyStyles ( ctx ) ;
@@ -63,11 +81,55 @@ class Path2DGroup {
6381 }
6482 }
6583 ctx . restore ( ) ;
66- for ( const item of this . items ) {
84+ for ( const item of visibleItems ) {
6785 item . afterRender ?.( ctx ) ;
6886 }
6987 }
7088 }
89+
90+ public get size ( ) {
91+ return this . items . size ;
92+ }
93+ }
94+
95+ class Path2DGroup {
96+ protected chunks : Path2DChunk [ ] = [ new Path2DChunk ( ) ] ;
97+ protected itemToChunk : Map < Path2DRenderInstance , Path2DChunk > = new Map ( ) ;
98+
99+ public add ( item : Path2DRenderInstance ) {
100+ let lastChunk = this . chunks [ this . chunks . length - 1 ] ;
101+ if ( lastChunk . size >= CHUNK_SIZE ) {
102+ lastChunk = new Path2DChunk ( ) ;
103+ this . chunks . push ( lastChunk ) ;
104+ }
105+ lastChunk . add ( item ) ;
106+ this . itemToChunk . set ( item , lastChunk ) ;
107+ }
108+
109+ public delete ( item : Path2DRenderInstance ) {
110+ const chunk = this . itemToChunk . get ( item ) ;
111+ if ( chunk ) {
112+ chunk . delete ( item ) ;
113+ this . itemToChunk . delete ( item ) ;
114+ if ( chunk . size === 0 && this . chunks . length > 1 ) {
115+ const index = this . chunks . indexOf ( chunk ) ;
116+ if ( index > - 1 ) {
117+ this . chunks . splice ( index , 1 ) ;
118+ }
119+ }
120+ }
121+ }
122+
123+ public resetItem ( item : Path2DRenderInstance ) {
124+ const chunk = this . itemToChunk . get ( item ) ;
125+ chunk ?. reset ( ) ;
126+ }
127+
128+ public render ( ctx : CanvasRenderingContext2D ) {
129+ for ( const chunk of this . chunks ) {
130+ chunk . render ( ctx ) ;
131+ }
132+ }
71133}
72134
73135export class BatchPath2DRenderer {
@@ -126,4 +188,13 @@ export class BatchPath2DRenderer {
126188 this . orderedPaths . reset ( ) ;
127189 this . onChange ?.( ) ;
128190 }
191+
192+ public markDirty ( item : Path2DRenderInstance ) {
193+ const params = this . itemParams . get ( item ) ;
194+ if ( params ) {
195+ const group = this . getGroup ( params . zIndex , params . group ) ;
196+ group . resetItem ( item ) ;
197+ this . onChange ?.( ) ;
198+ }
199+ }
129200}
0 commit comments