@@ -25,13 +25,28 @@ const PathDataCache: Record<string, ParsedSvgData> = {};
2525export interface GuiIconCfg extends Omit < ImageStyleProps , 'fill' > {
2626 readonly name : string ;
2727 readonly fill ?: string | null ;
28+ /**
29+ * 图标渲染策略
30+ * @see S2BasicOptions.csp
31+ */
32+ readonly iconStrategy ?: 'blob' | 'path' ;
2833}
2934
3035/**
3136 * 从 SVG 字符串中解析 viewBox 和 path 数据
3237 * @see https://github.com/antvis/S2/issues/3125
3338 */
3439function parseSvgPaths ( svg : string ) : ParsedSvgData | null {
40+ // 如果 SVG 包含 transform, g, rect 等不支持的复杂标签或属性,暂不使用 Path 模式
41+ // 避免渲染错位
42+ if (
43+ / t r a n s f o r m | t r a n s l a t e | s c a l e | r o t a t e | < g | < r e c t | < c i r c l e | < e l l i p s e | < l i n e | < p o l y l i n e | < p o l y g o n | < t e x t | < t s p a n / i. test (
44+ svg ,
45+ )
46+ ) {
47+ return null ;
48+ }
49+
3550 // 提取 viewBox
3651 const viewBoxMatch = svg . match ( / v i e w B o x = [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / ) ;
3752
@@ -289,20 +304,20 @@ export class GuiIcon extends Group {
289304 public isOnlineLink = ( src : string ) => / ^ (?: h t t p s ? : ) ? (?: \/ \/ ) / . test ( src ) ;
290305
291306 private render ( ) {
292- const { name, fill } = this . cfg ;
307+ const { name, fill, iconStrategy } = this . cfg ;
293308
294- // 优先尝试 Path 模式 (完全绕过 CSP 限制)
295- if ( this . tryRenderAsPath ( name , fill ) ) {
309+ // 如果指定了 path 模式, 优先尝试 Path 模式 (完全绕过 CSP 限制)
310+ if ( iconStrategy === 'path' && this . tryRenderAsPath ( name , fill ) ) {
296311 this . usePathMode = true ;
297312
298313 return ;
299314 }
300315
301- // 回退到 Image 模式
316+ // 默认或失败时回退到 Image 模式
302317 this . usePathMode = false ;
303318 const attrs = clone ( this . cfg ) ;
304319 const image = new CustomImage ( GuiIcon . type , {
305- style : omit ( attrs , 'fill' ) ,
320+ style : omit ( attrs , [ 'fill' , 'iconStrategy' ] ) ,
306321 } ) ;
307322
308323 this . iconImageShape = image ;
@@ -312,7 +327,7 @@ export class GuiIcon extends Group {
312327 public reRender ( cfg : GuiIconCfg ) {
313328 this . name = cfg . name ;
314329 this . cfg = cfg ;
315- const { name, fill } = this . cfg ;
330+ const { name, fill, iconStrategy } = this . cfg ;
316331
317332 // 清除旧的渲染
318333 if ( this . usePathMode ) {
@@ -323,8 +338,8 @@ export class GuiIcon extends Group {
323338 this . iconPathShapes = [ ] ;
324339 }
325340
326- // 优先尝试 Path 模式
327- if ( this . tryRenderAsPath ( name , fill ) ) {
341+ // 如果指定了 path 模式, 优先尝试 Path 模式
342+ if ( iconStrategy === 'path' && this . tryRenderAsPath ( name , fill ) ) {
328343 this . usePathMode = true ;
329344
330345 return ;
@@ -336,11 +351,11 @@ export class GuiIcon extends Group {
336351
337352 if ( ! this . iconImageShape ) {
338353 this . iconImageShape = new CustomImage ( GuiIcon . type , {
339- style : omit ( attrs , 'fill' ) ,
354+ style : omit ( attrs , [ 'fill' , 'iconStrategy' ] ) ,
340355 } ) ;
341356 } else {
342357 this . iconImageShape . imgType = GuiIcon . type ;
343- batchSetStyle ( this . iconImageShape , omit ( attrs , 'fill' ) ) ;
358+ batchSetStyle ( this . iconImageShape , omit ( attrs , [ 'fill' , 'iconStrategy' ] ) ) ;
344359 }
345360
346361 this . setImageAttrs ( { name, fill } ) ;
0 commit comments