1
1
import { DOCUMENT } from '@angular/common' ;
2
2
import { inject , Pipe } from '@angular/core' ;
3
3
4
+ const RGBA_REGEX = / r g b a ? \( ( \d + ) , \s * ( \d + ) , \s * ( \d + ) , ? \s * ( \d * \. ? \d + ) ? \) / ;
5
+ const DEFAULT_COLOR = 0x000000 ;
6
+
4
7
@Pipe ( { name : 'hexify' , pure : true , standalone : true } )
5
8
export class NgtHexify {
6
9
private document = inject ( DOCUMENT , { optional : true } ) ;
7
10
private ctx ?: CanvasRenderingContext2D | null ;
11
+ private cache : Record < string , number > = { } ;
8
12
9
13
/**
10
14
* transforms a:
@@ -17,45 +21,66 @@ export class NgtHexify {
17
21
* @param value
18
22
*/
19
23
transform ( value : string ) : number {
20
- if ( value == null ) return 0x000000 ;
24
+ if ( value == null ) return DEFAULT_COLOR ;
21
25
22
26
if ( value . startsWith ( '#' ) ) {
23
- return this . hexStringToNumber ( value ) ;
27
+ if ( ! this . cache [ value ] ) {
28
+ this . cache [ value ] = this . hexStringToNumber ( value ) ;
29
+ }
30
+ return this . cache [ value ] ;
24
31
}
25
32
26
33
if ( ! this . ctx ) {
27
34
this . ctx = this . document ?. createElement ( 'canvas' ) . getContext ( '2d' ) ;
28
35
}
29
36
30
- if ( ! this . ctx ) return 0x000000 ;
37
+ if ( ! this . ctx ) {
38
+ console . warn ( '[NGT] hexify: canvas context is not available' ) ;
39
+ return DEFAULT_COLOR ;
40
+ }
31
41
32
42
this . ctx . fillStyle = value ;
33
43
const computedValue = this . ctx . fillStyle ;
34
44
35
45
if ( computedValue . startsWith ( '#' ) ) {
36
- return this . hexStringToNumber ( computedValue ) ;
46
+ if ( ! this . cache [ computedValue ] ) {
47
+ this . cache [ computedValue ] = this . hexStringToNumber ( computedValue ) ;
48
+ }
49
+ return this . cache [ computedValue ] ;
37
50
}
38
51
39
- if ( ! computedValue . startsWith ( 'rgba' ) ) return 0x000000 ;
52
+ if ( ! computedValue . startsWith ( 'rgba' ) ) {
53
+ console . warn ( `[NGT] hexify: invalid color format. Expected rgba or hex, receive: ${ computedValue } ` ) ;
54
+ return DEFAULT_COLOR ;
55
+ }
40
56
41
- const regex = / r g b a ? \( ( \d + ) , \s * ( \d + ) , \s * ( \d + ) , ? \s * ( \d * \. ? \d + ) ? \) / ;
42
- const match = computedValue . match ( regex ) ;
43
- if ( ! match ) return 0x000000 ;
57
+ const match = computedValue . match ( RGBA_REGEX ) ;
58
+ if ( ! match ) {
59
+ console . warn ( `[NGT] hexify: invalid color format. Expected rgba or hex, receive: ${ computedValue } ` ) ;
60
+ return DEFAULT_COLOR ;
61
+ }
44
62
45
63
const r = parseInt ( match [ 1 ] , 10 ) ;
46
64
const g = parseInt ( match [ 2 ] , 10 ) ;
47
65
const b = parseInt ( match [ 3 ] , 10 ) ;
48
66
const a = match [ 4 ] ? parseFloat ( match [ 4 ] ) : 1.0 ;
49
67
50
- // Convert the components to hex strings
51
- const hexR = this . componentToHex ( r ) ;
52
- const hexG = this . componentToHex ( g ) ;
53
- const hexB = this . componentToHex ( b ) ;
54
- const hexA = this . componentToHex ( Math . round ( a * 255 ) ) ;
68
+ const cacheKey = `${ r } :${ g } :${ b } :${ a } ` ;
69
+
70
+ // check result from cache
71
+ if ( ! this . cache [ cacheKey ] ) {
72
+ // Convert the components to hex strings
73
+ const hexR = this . componentToHex ( r ) ;
74
+ const hexG = this . componentToHex ( g ) ;
75
+ const hexB = this . componentToHex ( b ) ;
76
+ const hexA = this . componentToHex ( Math . round ( a * 255 ) ) ;
77
+
78
+ // Combine the hex components into a single hex string
79
+ const hex = `#${ hexR } ${ hexG } ${ hexB } ${ hexA } ` ;
80
+ this . cache [ cacheKey ] = this . hexStringToNumber ( hex ) ;
81
+ }
55
82
56
- // Combine the hex components into a single hex string
57
- const hex = `#${ hexR } ${ hexG } ${ hexB } ${ hexA } ` ;
58
- return this . hexStringToNumber ( hex ) ;
83
+ return this . cache [ cacheKey ] ;
59
84
}
60
85
61
86
private hexStringToNumber ( hexString : string ) : number {
0 commit comments