22 * Utility functions for color conversions and operations.
33 */
44
5- import { normal as normalBlend } from "color-blend" ;
6-
75/**
86 * Blends two hexadecimal colors with a given opacity.
9- * @param color1 The first hexadecimal color value.
10- * @param color2 The second hexadecimal color value.
7+ * @param color1 The first hexadecimal color value (supports alpha channel) .
8+ * @param color2 The second hexadecimal color value (supports alpha channel) .
119 * @param opacity The opacity value between 0 and 1.
1210 * @returns A new hexadecimal color value representing the blend of color1 and color2.
1311 */
@@ -20,97 +18,158 @@ export function blendTwoHexColors(
2018 const rgb2 = hexToRgb ( color2 ) ;
2119
2220 if ( rgb1 && rgb2 ) {
23- const rgba1 = {
24- r : rgb1 . r ,
25- g : rgb1 . g ,
26- b : rgb1 . b ,
27- a : 1 ,
28- } ;
29- const rgba2 = {
30- r : rgb2 . r ,
31- g : rgb2 . g ,
32- b : rgb2 . b ,
33- a : opacity ,
34- } ;
35- const blended = normalBlend ( rgba1 , rgba2 ) ;
36- return rgbToHex ( blended . r , blended . g , blended . b ) ;
21+ // Simple alpha blending: result = color1 * (1 - opacity) + color2 * opacity
22+ const alpha1 = rgb1 . a ?? 1 ;
23+ const alpha2 = rgb2 . a ?? 1 ;
24+
25+ // Blend the colors
26+ const blendedR = Math . round ( rgb1 . r * ( 1 - opacity ) + rgb2 . r * opacity ) ;
27+ const blendedG = Math . round ( rgb1 . g * ( 1 - opacity ) + rgb2 . g * opacity ) ;
28+ const blendedB = Math . round ( rgb1 . b * ( 1 - opacity ) + rgb2 . b * opacity ) ;
29+
30+ // Blend the alpha channels
31+ const blendedA = alpha1 * ( 1 - opacity ) + alpha2 * opacity ;
32+
33+ // If either color had alpha or the blended alpha is not 1, include alpha in result
34+ if ( rgb1 . a !== undefined || rgb2 . a !== undefined || blendedA !== 1 ) {
35+ return rgbToHex ( blendedR , blendedG , blendedB , blendedA ) ;
36+ } else {
37+ return rgbToHex ( blendedR , blendedG , blendedB ) ;
38+ }
3739 } else {
3840 return "#ff00ffff" ;
3941 }
4042}
4143
4244/**
43- * Converts a hexadecimal color string to an RGB object.
44- * @param hex The hexadecimal color string (e.g., "#ff0000" or "#f00 ").
45- * @returns An object with 'r', 'g', and 'b ' properties representing the red, green, and blue components of the color, or undefined if the input is invalid.
45+ * Converts a hexadecimal color string to an RGB/RGBA object.
46+ * @param hex The hexadecimal color string (e.g., "#ff0000", "#f00", "#ff0000ff", or "#f00f ").
47+ * @returns An object with 'r', 'g', 'b', and optionally 'a ' properties representing the red, green, blue, and alpha components of the color, or undefined if the input is invalid.
4648 */
4749export function hexToRgb ( hex : string ) :
4850 | {
4951 r : number ;
5052 g : number ;
5153 b : number ;
54+ a ?: number ;
5255 }
5356 | undefined {
54- if ( ( hex . length !== 4 && hex . length !== 7 ) || ! hex . startsWith ( "#" ) ) {
57+ if (
58+ ( hex . length !== 4 &&
59+ hex . length !== 5 &&
60+ hex . length !== 7 &&
61+ hex . length !== 9 ) ||
62+ ! hex . startsWith ( "#" )
63+ ) {
5564 return undefined ;
5665 }
5766 let r : number ;
5867 let g : number ;
5968 let b : number ;
69+ let a : number | undefined ;
70+
6071 if ( hex . length === 4 ) {
72+ // #RGB format
73+ r = Number ( "0x" + hex [ 1 ] + hex [ 1 ] ) ;
74+ g = Number ( "0x" + hex [ 2 ] + hex [ 2 ] ) ;
75+ b = Number ( "0x" + hex [ 3 ] + hex [ 3 ] ) ;
76+ } else if ( hex . length === 5 ) {
77+ // #RGBA format
6178 r = Number ( "0x" + hex [ 1 ] + hex [ 1 ] ) ;
6279 g = Number ( "0x" + hex [ 2 ] + hex [ 2 ] ) ;
6380 b = Number ( "0x" + hex [ 3 ] + hex [ 3 ] ) ;
81+ a = Number ( "0x" + hex [ 4 ] + hex [ 4 ] ) / 255 ;
6482 } else if ( hex . length === 7 ) {
83+ // #RRGGBB format
6584 r = Number ( "0x" + hex [ 1 ] + hex [ 2 ] ) ;
6685 g = Number ( "0x" + hex [ 3 ] + hex [ 4 ] ) ;
6786 b = Number ( "0x" + hex [ 5 ] + hex [ 6 ] ) ;
87+ } else if ( hex . length === 9 ) {
88+ // #RRGGBBAA format
89+ r = Number ( "0x" + hex [ 1 ] + hex [ 2 ] ) ;
90+ g = Number ( "0x" + hex [ 3 ] + hex [ 4 ] ) ;
91+ b = Number ( "0x" + hex [ 5 ] + hex [ 6 ] ) ;
92+ a = Number ( "0x" + hex [ 7 ] + hex [ 8 ] ) / 255 ;
6893 } else {
6994 return undefined ;
7095 }
7196
72- return { r, g, b } ;
97+ const result : { r : number ; g : number ; b : number ; a ?: number } = { r, g, b } ;
98+ if ( a !== undefined ) {
99+ result . a = a ;
100+ }
101+ return result ;
73102}
74103
75104/**
76- * Converts RGB values to a hexadecimal color string.
105+ * Converts RGB/RGBA values to a hexadecimal color string.
77106 * @param r The red component (0-255).
78107 * @param g The green component (0-255).
79108 * @param b The blue component (0-255).
80- * @returns The hexadecimal color string (e.g., "#ff0000" for red).
109+ * @param a The alpha component (0-1), optional.
110+ * @returns The hexadecimal color string (e.g., "#ff0000" for red or "#ff0000ff" for red with full opacity).
81111 */
82- function rgbToHex ( r : number , g : number , b : number ) : string {
83- return "#" + ( ( 1 << 24 ) + ( r << 16 ) + ( g << 8 ) + b ) . toString ( 16 ) . slice ( 1 ) ;
112+ function rgbToHex ( r : number , g : number , b : number , a ?: number ) : string {
113+ const hexR = Math . round ( r ) . toString ( 16 ) . padStart ( 2 , "0" ) ;
114+ const hexG = Math . round ( g ) . toString ( 16 ) . padStart ( 2 , "0" ) ;
115+ const hexB = Math . round ( b ) . toString ( 16 ) . padStart ( 2 , "0" ) ;
116+
117+ if ( a !== undefined ) {
118+ const hexA = Math . round ( a * 255 )
119+ . toString ( 16 )
120+ . padStart ( 2 , "0" ) ;
121+ return `#${ hexR } ${ hexG } ${ hexB } ${ hexA } ` ;
122+ }
123+
124+ return `#${ hexR } ${ hexG } ${ hexB } ` ;
84125}
85126
86127/**
87128 * Converts a hexadecimal color string to its HSL (Hue, Saturation, Lightness) representation.
88- * @param hex The hexadecimal color string (e.g., "#ff0000" or "#f00 ").
89- * @returns An object with 'hue', 'sat', 'lgt', and 'string' properties representing the HSL values and an HSL string representation.
129+ * @param hex The hexadecimal color string (e.g., "#ff0000", "#f00", "#ff0000ff", or "#f00f ").
130+ * @returns An object with 'hue', 'sat', 'lgt', 'alpha', and 'string' properties representing the HSL values and an HSL string representation.
90131 */
91132export function hexToHSL ( hex : string ) : {
92133 hue : number ;
93134 sat : number ;
94135 lgt : number ;
136+ alpha ?: number ;
95137 string : string ;
96138} {
97139 // Convert hex to RGB first
98140 let r : number ;
99141 let g : number ;
100142 let b : number ;
143+ let a : number | undefined ;
144+
101145 if ( hex . length === 4 ) {
146+ // #RGB format
102147 r = ( "0x" + hex [ 1 ] + hex [ 1 ] ) as unknown as number ;
103148 g = ( "0x" + hex [ 2 ] + hex [ 2 ] ) as unknown as number ;
104149 b = ( "0x" + hex [ 3 ] + hex [ 3 ] ) as unknown as number ;
150+ } else if ( hex . length === 5 ) {
151+ // #RGBA format
152+ r = ( "0x" + hex [ 1 ] + hex [ 1 ] ) as unknown as number ;
153+ g = ( "0x" + hex [ 2 ] + hex [ 2 ] ) as unknown as number ;
154+ b = ( "0x" + hex [ 3 ] + hex [ 3 ] ) as unknown as number ;
155+ a = ( ( "0x" + hex [ 4 ] + hex [ 4 ] ) as unknown as number ) / 255 ;
105156 } else if ( hex . length === 7 ) {
157+ // #RRGGBB format
106158 r = ( "0x" + hex [ 1 ] + hex [ 2 ] ) as unknown as number ;
107159 g = ( "0x" + hex [ 3 ] + hex [ 4 ] ) as unknown as number ;
108160 b = ( "0x" + hex [ 5 ] + hex [ 6 ] ) as unknown as number ;
161+ } else if ( hex . length === 9 ) {
162+ // #RRGGBBAA format
163+ r = ( "0x" + hex [ 1 ] + hex [ 2 ] ) as unknown as number ;
164+ g = ( "0x" + hex [ 3 ] + hex [ 4 ] ) as unknown as number ;
165+ b = ( "0x" + hex [ 5 ] + hex [ 6 ] ) as unknown as number ;
166+ a = ( ( "0x" + hex [ 7 ] + hex [ 8 ] ) as unknown as number ) / 255 ;
109167 } else {
110168 r = 0x00 ;
111169 g = 0x00 ;
112170 b = 0x00 ;
113171 }
172+
114173 // Then to HSL
115174 r /= 255 ;
116175 g /= 255 ;
@@ -136,12 +195,27 @@ export function hexToHSL(hex: string): {
136195 s = + ( s * 100 ) . toFixed ( 1 ) ;
137196 l = + ( l * 100 ) . toFixed ( 1 ) ;
138197
139- return {
198+ const result : {
199+ hue : number ;
200+ sat : number ;
201+ lgt : number ;
202+ alpha ?: number ;
203+ string : string ;
204+ } = {
140205 hue : h ,
141206 sat : s ,
142207 lgt : l ,
143- string : "hsl(" + h + "," + s + "%," + l + "%)" ,
208+ string :
209+ a !== undefined
210+ ? `hsla(${ h } , ${ s } %, ${ l } %, ${ a . toFixed ( 3 ) } )`
211+ : `hsl(${ h } , ${ s } %, ${ l } %)` ,
144212 } ;
213+
214+ if ( a !== undefined ) {
215+ result . alpha = a ;
216+ }
217+
218+ return result ;
145219}
146220
147221/**
0 commit comments