color.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. const themeColor = {};
  2. /**
  3. * 单例模式确保全局只有一个Color实例
  4. * @param {Object} themeColor 主题颜色
  5. */
  6. const Color = (function () {
  7. let instance;
  8. return function (themeColor) {
  9. if (instance) return instance;
  10. return new ColorConstructor(themeColor);
  11. };
  12. })();
  13. class ColorConstructor {
  14. // 定义私有属性,用于存储主题颜色和颜色变化程度
  15. #themeColor; // 主题颜色
  16. #changeSpan; // 颜色变化程度
  17. #themeKey; // 主题颜色key
  18. #subThemeKey; // 辅助颜色key
  19. #theme; // 主题
  20. constructor({
  21. themeColor,
  22. changeSpan,
  23. themeKey,
  24. theme = "light",
  25. subThemeKey,
  26. }) {
  27. // 如果没有传入主题颜色,则抛出错误
  28. if (!themeColor) throw new Error(`主题颜色是必须的`);
  29. // 设置主题
  30. this.setTheme(theme);
  31. // 设置主题颜色
  32. this.setThemeColor({
  33. themeColor,
  34. changeSpan,
  35. themeKey,
  36. theme,
  37. subThemeKey,
  38. });
  39. }
  40. // 判断颜色是否为16进制颜色
  41. isHEX(color) {
  42. // 如果颜色长度不为7或4,则返回false
  43. if (color.length !== 7 && color.length !== 4) return false;
  44. // 定义正则表达式,用于匹配16进制颜色
  45. const hexreg = /^\#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;
  46. // 返回正则表达式匹配结果
  47. return hexreg.test(color);
  48. }
  49. isRGB(color) {
  50. const rgbreg = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/;
  51. return rgbreg.test(color);
  52. }
  53. // 格式化十六进制颜色值
  54. formatHex(hex) {
  55. // 如果长度不等于4,则返回原值
  56. if (hex.length !== 4) return hex;
  57. // 返回格式化后的十六进制颜色值
  58. return `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}`;
  59. }
  60. // 参数归一化颜色
  61. normalizeColor(color) {
  62. let colorMap = {};
  63. if (typeof color === "string") {
  64. if (!this.isHEX(color) && !this.isRGB(color)) {
  65. throw new Error(`颜色值${color}格式不正确`);
  66. }
  67. colorMap[this.#themeKey] = color;
  68. } else if (Object.prototype.toString.call(color) !== "[object Object]") {
  69. throw new Error(`颜色值${color}格式不正确 必须是字符串或者对象的形式`);
  70. } else {
  71. colorMap = color;
  72. }
  73. return colorMap;
  74. }
  75. // 设置主题颜色
  76. setThemeColor({
  77. themeColor,
  78. changeSpan = 0.2,
  79. themeKey = "primary",
  80. subThemeKey = "success",
  81. onSuccess,
  82. }) {
  83. // 将传入的颜色赋值给私有属性#themeColor
  84. const newTheme = this.normalizeColor(themeColor);
  85. this.#changeSpan = changeSpan;
  86. this.#themeColor = this.#setThemeMap(newTheme, this.#changeSpan);
  87. this.#themeKey = themeKey;
  88. this.#subThemeKey = subThemeKey;
  89. onSuccess?.(this.#themeColor);
  90. }
  91. // 设置主题
  92. setTheme(theme) {
  93. if (theme !== "dark" && theme !== "light")
  94. throw new Error(`主题必须是dark或者light`);
  95. // 将传入的主题赋值给私有属性#theme
  96. this.#theme = theme;
  97. // 调用私有方法#setThemeMap,传入私有属性#themeColor和#changeSpan,将返回值赋值给私有属性#themeColor
  98. this.#themeColor = this.#setThemeMap(this.#themeColor, this.#changeSpan);
  99. }
  100. // 获取主题颜色
  101. get themeColor() {
  102. // 返回主题颜色
  103. return this.#themeColor[this.#themeKey];
  104. }
  105. // 获取子主题颜色
  106. get subThemeColor() {
  107. // 返回主题颜色中子主题键对应的颜色值
  108. return this.#themeColor[this.#subThemeKey];
  109. }
  110. // 获取变淡以及加深的颜色
  111. #setThemeMap(themeColor, changeSpan) {
  112. const newTheme = { ...themeColor };
  113. const changeList = new Array(5)
  114. .fill(0)
  115. .map((_, index) => index * changeSpan)
  116. .filter((item) => item > 0 && item < 1);
  117. for (const key in newTheme) {
  118. changeList.forEach((item, index) => {
  119. newTheme[`${key}-light-${index + 1}`] = this.getLightColor(
  120. newTheme[key],
  121. item
  122. );
  123. newTheme[`${key}-dark-${index + 1}`] = this.getDarkColor(
  124. newTheme[key],
  125. item
  126. );
  127. });
  128. }
  129. return newTheme;
  130. }
  131. // 获取主题颜色 1-4 或者 空
  132. getThemeColor(index, themeKey = this.#themeKey) {
  133. // 打印深色主题颜色
  134. const key = `${themeKey}${!!index ? `-${this.#theme}-${index}` : ""}`;
  135. return this.#themeColor[key];
  136. }
  137. //16进制颜色转GRB颜色
  138. HexToRgb(str) {
  139. if (!this.isHEX(str)) {
  140. return;
  141. }
  142. str = this.formatHex(str);
  143. let hexs = null;
  144. str = str.replace("#", ""); // 去掉#
  145. hexs = str.match(/../g); // 切割成数组 409EFF => ['40','9E','FF']
  146. // 将切割的色值转换为16进制
  147. for (let i = 0; i < hexs.length; i++) hexs[i] = parseInt(hexs[i], 16);
  148. return hexs;
  149. }
  150. //GRB颜色转16进制颜色
  151. rgbaToHex(rgba) {
  152. let hex = "#";
  153. for (const i of rgba) {
  154. hex += i.toString(16).padStart(2, "0");
  155. }
  156. return hex;
  157. }
  158. // 计算颜色
  159. computedColor(color, fn) {
  160. // 将颜色转换为rgb格式
  161. let rgb = color;
  162. if (this.isHEX(color)) {
  163. rgb = this.HexToRgb(color);
  164. }
  165. // 创建一个空数组,用于存储转换后的颜色值
  166. let sColorChange = [];
  167. // 遍历rgb数组
  168. for (var i = 0; i < rgb.length; i++) {
  169. // 对每个颜色值进行转换
  170. let val = fn(rgb[i]);
  171. // 如果转换后的值小于0,则将其设置为0
  172. if (val < 0) {
  173. val = 0;
  174. }
  175. // 如果转换后的值大于255,则将其设置为255
  176. if (val > 255) {
  177. val = 255;
  178. }
  179. // 将转换后的值添加到数组中
  180. sColorChange.push(val);
  181. }
  182. // 将转换后的rgb数组转换为hex格式
  183. return this.rgbaToHex(sColorChange);
  184. }
  185. //得到16进制颜色值为color的加深颜色值,level为加深的程度,限0-1之间
  186. getDarkColor(color, level) {
  187. return this.computedColor(color, (item) => item - Math.floor(item * level));
  188. }
  189. //得到16进制颜色值为color的减淡颜色值,level为加深的程度,限0-1之间
  190. getLightColor(color, level) {
  191. return this.computedColor(color, (item) => item + Math.floor(item * level));
  192. }
  193. }
  194. export default new Color({ themeColor });