generate.uts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // https://github.com/ant-design/ant-design-colors/blob/main/src/generate.ts
  2. import { inputToRGB } from './format-input';
  3. import { rgbToHex, rgbToHsv } from './conversion';
  4. import { HSV, LColorInfo, LGenerateOptions} from '../utssdk/interface.uts';
  5. type DarkColorMapItem = {
  6. index : number;
  7. opacity : number;
  8. };
  9. const hueStep = 2; // 色相阶梯
  10. const saturationStep = 0.16; // 饱和度阶梯,浅色部分
  11. const saturationStep2 = 0.05; // 饱和度阶梯,深色部分
  12. const brightnessStep1 = 0.05; // 亮度阶梯,浅色部分
  13. const brightnessStep2 = 0.15; // 亮度阶梯,深色部分
  14. const lightColorCount = 5; // 浅色数量,主色上
  15. const darkColorCount = 4; // 深色数量,主色下
  16. // 暗色主题颜色映射关系表
  17. const darkColorMap = [
  18. { index: 7, opacity: 0.15 },
  19. { index: 6, opacity: 0.25 },
  20. { index: 5, opacity: 0.3 },
  21. { index: 5, opacity: 0.45 },
  22. { index: 5, opacity: 0.65 },
  23. { index: 5, opacity: 0.85 },
  24. { index: 4, opacity: 0.9 },
  25. { index: 3, opacity: 0.95 },
  26. { index: 2, opacity: 0.97 },
  27. { index: 1, opacity: 0.98 },
  28. ] as DarkColorMapItem[];
  29. // 从 TinyColor.toHsv 移植的包装函数
  30. // 保留这里,因为有 `hsv.h * 360`
  31. function toHsv({ r, g, b } : LColorInfo) : HSV {
  32. // 将 RGB 值转换为 HSV 值
  33. const hsv = rgbToHsv(r, g, b);
  34. // 返回一个 HsvObject,其中 h 值乘以 360
  35. return { h: hsv.h * 360, s: hsv.s, v: hsv.v } as HSV;
  36. }
  37. // 从 TinyColor.toHexString 移植的包装函数
  38. // 保留这里,因为有前缀 `#`
  39. function toHex({ r, g, b }: LColorInfo) : string {
  40. // 将 RGB 值转换为十六进制字符串,并添加前缀 `#`
  41. return `#${rgbToHex(r, g, b, false)}`;
  42. }
  43. // 从 TinyColor.mix 移植的包装函数,无法进行 tree-shaking
  44. // 数量范围为 [0, 1]
  45. // 假设 color1 和 color2 没有透明度,因为以下源代码也是如此
  46. function mix(rgb1 : LColorInfo, rgb2 : LColorInfo, amount : number) : LColorInfo {
  47. // 将 amount 除以 100,得到一个范围为 [0, 1] 的值
  48. const p = amount / 100;
  49. // 计算混合后的 RGB 值
  50. const rgb = {
  51. r: (rgb2.r - rgb1.r) * p + rgb1.r,
  52. g: (rgb2.g - rgb1.g) * p + rgb1.g,
  53. b: (rgb2.b - rgb1.b) * p + rgb1.b,
  54. a: 1
  55. } as LColorInfo;
  56. // 返回混合后的 RGB 对象
  57. return rgb;
  58. }
  59. // 根据给定的 HSV 对象和索引值计算新的色相值
  60. // 如果 light 参数为 true,则色相向左转动;否则向右转动
  61. function getHue(hsv : HSV, i : number, light : boolean = false) : number {
  62. let hue : number;
  63. // 根据色相不同,色相转向不同
  64. if (Math.round(hsv.h) >= 60 && Math.round(hsv.h) <= 240) {
  65. // 如果色相在 60 到 240 之间,向左转动
  66. hue = light ? Math.round(hsv.h) - hueStep * i : Math.round(hsv.h) + hueStep * i;
  67. } else {
  68. hue = light ? Math.round(hsv.h) + hueStep * i : Math.round(hsv.h) - hueStep * i;
  69. }
  70. if (hue < 0) {
  71. // 如果新的色相值小于 0,则加上 360
  72. hue += 360;
  73. } else if (hue >= 360) {
  74. // 如果新的色相值大于等于 360,则减去 360
  75. hue -= 360;
  76. }
  77. return hue;
  78. }
  79. // 根据给定的 HSV 对象和索引值计算新的饱和度值
  80. // 如果 light 参数为 true,则饱和度减小;否则增加
  81. function getSaturation(hsv : HSV, i : number, light : boolean = false) : number {
  82. // grey color don't change saturation
  83. // 如果颜色是灰色(色相和饱和度都为 0),则饱和度不变
  84. if (hsv.h == 0 && hsv.s == 0) {
  85. return hsv.s;
  86. }
  87. let saturation : number;
  88. // 如果 light 参数为 true,则饱和度减小
  89. if (light) {
  90. saturation = hsv.s - saturationStep * i;
  91. }
  92. // 如果 i 等于 darkColorCount,则饱和度增加
  93. else if (i == darkColorCount) {
  94. saturation = hsv.s + saturationStep;
  95. }
  96. // 否则,饱和度增加
  97. else {
  98. saturation = hsv.s + saturationStep2 * i;
  99. }
  100. // 边界值修正
  101. if (saturation > 1) {
  102. saturation = 1;
  103. }
  104. // 第一格的 s 限制在 0.06-0.1 之间
  105. if (light && i == lightColorCount && saturation > 0.1) {
  106. saturation = 0.1;
  107. }
  108. if (saturation < 0.06) {
  109. saturation = 0.06;
  110. }
  111. return parseFloat(saturation.toFixed(2))
  112. }
  113. // 根据给定的 HSV 对象和索引值计算新的亮度值
  114. // 如果 light 参数为 true,则亮度增加;否则减少
  115. function getValue(hsv : HSV, i : number, light : boolean = false) : number {
  116. let value : number;
  117. // 如果 light 参数为 true,则亮度增加
  118. if (light) {
  119. value = hsv.v + brightnessStep1 * i;
  120. } else {
  121. value = hsv.v - brightnessStep2 * i;
  122. }
  123. if (value > 1) {
  124. value = 1;
  125. }
  126. // 返回保留两位小数的亮度值
  127. return parseFloat(value.toFixed(2));
  128. }
  129. /**
  130. * generate 函数用于生成一组基于给定颜色的色彩模式。
  131. * 它可以生成亮色、暗色和深色主题颜色模式。
  132. *
  133. * @param {string} color - 输入的颜色值,可以是十六进制、RGB、RGBA、HSL、HSLA或颜色名称。
  134. * @param {LGenerateOptions} [opts] - 可选的生成选项。
  135. * @returns {string[]} - 返回一个包含生成的颜色模式的字符串数组。
  136. */
  137. export function generate(color : string, opts : LGenerateOptions = {} as LGenerateOptions) : string[] {
  138. const patterns : string[] = [];
  139. const pColor = inputToRGB(color);
  140. // 生成亮色模式
  141. for (let i = lightColorCount; i > 0; i -= 1) {
  142. const hsv = toHsv(pColor);
  143. const colorString : string = toHex(
  144. inputToRGB({
  145. h: getHue(hsv, i, true),
  146. s: getSaturation(hsv, i, true),
  147. v: getValue(hsv, i, true),
  148. }),
  149. );
  150. patterns.push(colorString);
  151. }
  152. // 添加原始颜色
  153. patterns.push(toHex(pColor));
  154. // 生成暗色模式
  155. for (let i = 1; i <= darkColorCount; i += 1) {
  156. const hsv = toHsv(pColor);
  157. const colorString : string = toHex(
  158. inputToRGB({
  159. h: getHue(hsv, i),
  160. s: getSaturation(hsv, i),
  161. v: getValue(hsv, i),
  162. }),
  163. );
  164. patterns.push(colorString);
  165. }
  166. // 如果选项中指定了 dark 主题,则生成深色主题颜色模式
  167. if (opts.theme == 'dark') {
  168. return darkColorMap.map(({ index, opacity }, _):string => {
  169. const darkColorString : string = toHex(
  170. mix(
  171. inputToRGB(opts.backgroundColor ?? '#141414'),
  172. inputToRGB(patterns[index]),
  173. opacity * 100,
  174. ),
  175. );
  176. return darkColorString;
  177. });
  178. }
  179. // 返回默认颜色模式
  180. return patterns;
  181. }