@@ -18,12 +18,7 @@ import Fill from '../Style/Fill';
18
18
import Color from '../Style/Color' ;
19
19
20
20
import { defaultExportOptions } from '../utils' ;
21
- import {
22
- getRenderedSvgString ,
23
- getUseReplacement ,
24
- inlineStyles ,
25
- optimizeSvgString ,
26
- } from '../../utils/svg' ;
21
+
27
22
import { getGroupLayout } from '../../utils/layout' ;
28
23
29
24
import {
@@ -32,6 +27,7 @@ import {
32
27
FrameType ,
33
28
ShapeGroupType ,
34
29
SvgDefsStyle ,
30
+ SvgLayerType ,
35
31
} from '../type' ;
36
32
37
33
interface SvgInitParams extends Partial < BaseLayerParams > {
@@ -242,80 +238,6 @@ class Svg extends BaseLayer {
242
238
return scale ;
243
239
}
244
240
245
- /**
246
- * 从 Url 初始化 Svg
247
- * @param url Url
248
- * @param frame 尺寸
249
- */
250
- static async initFromUrl ( url : string , frame : Frame ) {
251
- let data ;
252
- try {
253
- data = await fetch ( url , {
254
- mode : 'cors' ,
255
- } ) ;
256
- } catch ( error ) {
257
- const maybeCorsError = error . toString ( ) . includes ( 'Failed to fetch' ) ;
258
- if ( maybeCorsError ) {
259
- const corsPrefix = `https://cors-anywhere.herokuapp.com/` ;
260
- data = await fetch ( corsPrefix + url , {
261
- mode : 'cors' ,
262
- } ) ;
263
- console . warn (
264
- '该图片存在跨域问题! 请在服务器端设置允许图片跨域,以提升解析速度:' ,
265
- url ,
266
- ) ;
267
- }
268
- }
269
- if ( ! data ) return ;
270
-
271
- let svgString = await data . text ( ) ;
272
-
273
- const { x, y, width, height } = frame ;
274
-
275
- svgString = await getRenderedSvgString ( svgString , { width, height } ) ;
276
-
277
- return new Svg ( { svgString, x, y, width, height } ) ;
278
- }
279
-
280
- /**
281
- * 将 Svg Node 转为 SvgString
282
- * @param svgNode
283
- */
284
- static getSVGString = async ( svgNode : Element ) : Promise < string > => {
285
- // NOTE: this code modifies the original node by inlining all styles
286
- // this is not ideal and probably fixable
287
- const queue = Array . from ( svgNode . children ) ;
288
-
289
- while ( queue . length ) {
290
- const node = queue . pop ( ) ;
291
-
292
- if (
293
- ! ( node instanceof SVGElement ) ||
294
- node instanceof SVGDefsElement ||
295
- node instanceof SVGTitleElement
296
- ) {
297
- continue ;
298
- }
299
-
300
- if ( node instanceof SVGUseElement ) {
301
- const replacement = getUseReplacement ( node ) ;
302
-
303
- if ( replacement ) {
304
- node . parentNode ! . replaceChild ( replacement , node ) ;
305
- queue . push ( replacement ) ;
306
- }
307
- continue ;
308
- }
309
-
310
- if ( node ) {
311
- inlineStyles ( < SVGElement > node ) ;
312
- Array . from ( node . children ) . forEach ( ( child ) => queue . push ( child ) ) ;
313
- }
314
- }
315
-
316
- return optimizeSvgString ( svgNode . outerHTML ) ;
317
- } ;
318
-
319
241
/**
320
242
* 一致化缠绕规则参数
321
243
* @param ruleStr
@@ -348,7 +270,7 @@ class Svg extends BaseLayer {
348
270
* 解析 Svgson 变成 layer
349
271
* @param node
350
272
*/
351
- parseSvgson = ( node : svgson . INode ) : any => {
273
+ parseSvgson = ( node : svgson . INode ) : SvgLayerType => {
352
274
switch ( node . name ) {
353
275
// 全局定义
354
276
case 'defs' :
@@ -396,16 +318,17 @@ class Svg extends BaseLayer {
396
318
397
319
const shapeGroupType = Svg . pathToShapeGroup ( path ) ;
398
320
321
+ const isClose = ! shapeGroupType . shapes . every ( ( shape ) => ! shape . isClose ) ;
399
322
const shapePaths = this . shapeGroupDataToLayers ( shapeGroupType ) ;
400
323
401
324
if ( shapePaths . length === 1 ) {
402
325
const shapePath = shapePaths [ 0 ] ;
403
- shapePath . style = this . parseNodeAttrToStyle ( node . attributes ) ;
326
+ shapePath . style = this . parseNodeAttrToStyle ( node . attributes , isClose ) ;
404
327
}
405
328
const shapeGroup = new ShapeGroup ( shapeGroupType . frame ) ;
406
329
407
330
shapeGroup . addLayers ( shapePaths ) ;
408
- shapeGroup . style = this . parseNodeAttrToStyle ( node . attributes ) ;
331
+ shapeGroup . style = this . parseNodeAttrToStyle ( node . attributes , isClose ) ;
409
332
410
333
return shapeGroup ;
411
334
}
@@ -430,13 +353,52 @@ class Svg extends BaseLayer {
430
353
y : parseFloat ( attributes . y2 ) / 100 ,
431
354
} ,
432
355
stops : defsNode . children . map ( ( item ) => {
433
- const {
434
- // TODO 有待改造 Stop 方法
435
- // offset,
436
- stopColor,
437
- } = item . attributes ;
438
- // const color = new Color(stopColor);
439
- return stopColor ;
356
+ const { offset, stopColor, stopOpacity } = item . attributes ;
357
+ const color = new Color ( stopColor ) ;
358
+
359
+ return {
360
+ color : [
361
+ color . red ,
362
+ color . green ,
363
+ color . blue ,
364
+ Number ( stopOpacity ) || 1 ,
365
+ ] ,
366
+ offset : parseFloat ( offset ) / 100 ,
367
+ } ;
368
+ } ) ,
369
+ } ) ;
370
+ case 'radialGradient' :
371
+ console . log ( attributes ) ;
372
+ return new Gradient ( {
373
+ type : SketchFormat . GradientType . Radial ,
374
+ name : attributes . id ,
375
+ from : {
376
+ // 解析得到的是 109% 这样的值
377
+ x : parseFloat ( attributes . fx ) / 100 ,
378
+ y : parseFloat ( attributes . fy ) / 100 ,
379
+ } ,
380
+ to : {
381
+ x : ( parseFloat ( attributes . cx ) + parseFloat ( attributes . r ) ) / 100 ,
382
+ y : parseFloat ( attributes . cy ) / 100 ,
383
+ } ,
384
+ // radius: parseFloat(attributes.r) / 100,
385
+ stops : defsNode . children . map ( ( item ) => {
386
+ const { offset, stopColor, stopOpacity } = item . attributes ;
387
+ const color = new Color ( stopColor ) ;
388
+
389
+ console . log ( stopOpacity ) ;
390
+
391
+ const opacity = Number ( stopOpacity ) ;
392
+
393
+ return {
394
+ color : [
395
+ color . red ,
396
+ color . green ,
397
+ color . blue ,
398
+ isNaN ( opacity ) ? 1 : opacity ,
399
+ ] ,
400
+ offset : parseFloat ( offset ) / 100 ,
401
+ } ;
440
402
} ) ,
441
403
} ) ;
442
404
case 'style' :
@@ -454,14 +416,19 @@ class Svg extends BaseLayer {
454
416
/**
455
417
* 解析 Node 的 Attribute 变成 style
456
418
* @param attributes node 的属性
419
+ * @param isClose
457
420
*/
458
- parseNodeAttrToStyle = ( attributes : svgson . INode [ 'attributes' ] ) => {
421
+ parseNodeAttrToStyle = (
422
+ attributes : svgson . INode [ 'attributes' ] ,
423
+ isClose : boolean = true ,
424
+ ) => {
459
425
const {
460
426
stroke,
461
427
strokeWidth,
462
428
fill : fillStr ,
463
429
style : styleString ,
464
430
class : className ,
431
+ opacity,
465
432
} = attributes ;
466
433
467
434
const style = new Style ( ) ;
@@ -479,10 +446,12 @@ class Svg extends BaseLayer {
479
446
}
480
447
}
481
448
482
- // 直接使用自带的 fill
483
-
484
- const baseFill = this . getFillByString ( fillStr ) ;
485
- if ( baseFill ) style . fills . push ( baseFill ) ;
449
+ // 如果闭合了的话
450
+ // 直接使用默认填充
451
+ if ( isClose ) {
452
+ const baseFill = this . getFillByString ( fillStr ) ;
453
+ if ( baseFill ) style . fills . push ( baseFill ) ;
454
+ }
486
455
487
456
// 如果存在currentColor 则采用 inline Style 的 fill
488
457
if ( fillStr === 'currentColor' && styleObj ?. fill ) {
@@ -497,6 +466,8 @@ class Svg extends BaseLayer {
497
466
} ) ;
498
467
}
499
468
469
+ // 设置不透明度
470
+ style . opacity = Number ( opacity ) || 1 ;
500
471
return style ;
501
472
} ;
502
473
@@ -696,18 +667,24 @@ class Svg extends BaseLayer {
696
667
return classStyle ?. rules . find ( ( r ) => r . className === `.${ className } ` ) ;
697
668
} ;
698
669
670
+ /**
671
+ * 根据 fill 字符填充 Fill 对象
672
+ * @param fill 填充文本
673
+ */
699
674
private getFillByString = ( fill : string ) => {
700
675
if ( fill === 'none' ) return ;
701
676
702
- if ( ! fill )
677
+ // TODO 针对 path 类型的对象 如果没有 fill 不能默认填充黑色
678
+ if ( ! fill ) {
703
679
return new Fill ( { type : SketchFormat . FillType . Color , color : '#000' } ) ;
680
+ }
704
681
705
682
if ( fill . startsWith ( 'url' ) ) {
706
683
// 说明来自 defs
707
684
const id = / u r l \( # ( .* ) \) / . exec ( fill ) ?. [ 1 ] ;
708
685
// 从 defs 中拿到相应的配置项
709
686
const defsFill = this . defs . find (
710
- ( def ) => def . class === 'gradient' && def . name === id ,
687
+ ( def ) => def ? .class === 'gradient' && def . name === id ,
711
688
) ;
712
689
713
690
switch ( defsFill ?. class ) {
0 commit comments