styimat 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +250 -24
  2. package/package.json +2 -2
  3. package/styimat.js +398 -28
  4. package/styimat.min.js +181 -22
package/README.md CHANGED
@@ -1,37 +1,52 @@
1
1
  # styimat
2
+ [![Stars](https://gitee.com/wxy6987/styimat/badge/star.svg?theme=white)](https://gitee.com/wxy6987/styimat/stargazers)
3
+ [![Forks](https://gitee.com/wxy6987/styimat/badge/fork.svg?theme=white)](https://gitee.com/wxy6987/styimat/members)
2
4
 
3
- 一个功能强大的 CSS 变量预处理库,支持嵌套规则、Lab/LCH 颜色空间转换和 Display P3 广色域。
5
+ 一个功能强大的 CSS 变量预处理库,支持嵌套规则、Lab/LCH 颜色空间转换、Display P3 广色域和增强的数学计算功能。
4
6
 
5
- ## 特性
7
+ ## 安装方式
8
+
9
+ ### 1. Git 克隆(推荐)
10
+ ```bash
11
+ # 克隆主仓库
12
+ git clone https://gitee.com/wxy6987/styimat.git
6
13
 
7
- - 🎨 **Lab/LCH 颜色支持** - 将 Lab 和 LCH 颜色转换为 CSS 兼容的 RGB
8
- - 🌈 **广色域显示** - 自动检测并支持 Display P3 广色域显示器
9
- - 🔧 **变量系统** - 类似 Sass 的变量系统,支持作用域变量
10
- - 🏗️ **嵌套规则** - 支持 Sass 风格的嵌套选择器
11
- - 📱 **十六进制语法** - 支持 `lab#L16A16B16` 和 `lch#L16C16H` 简洁语法
12
- - ⚡ **轻量高效** - 无依赖,压缩后仅约 20KB
13
- - 🔄 **多种使用方式** - 浏览器、Node.js、命令行均可使用
14
+ # 进入项目目录
15
+ cd styimat
14
16
 
15
- ## 安装
17
+ # 安装依赖
18
+ npm install
19
+ ```
16
20
 
17
- ### NPM
21
+ ### 2. 直接下载 ZIP
22
+ - [点击下载最新版本](https://gitee.com/wxy6987/styimat/repository/archive/main.zip)
18
23
 
24
+ ### 3. NPM 安装
19
25
  ```bash
20
26
  npm install styimat
21
27
  ```
22
28
 
23
- ### CDN (unpkg)
24
-
29
+ ### 4. CDN 引入
25
30
  ```html
31
+ <!-- unpkg -->
26
32
  <script src="https://unpkg.com/styimat"></script>
27
- ```
28
-
29
- ### CDN (jsdelivr)
30
33
 
31
- ```html
34
+ <!-- jsdelivr -->
32
35
  <script src="https://cdn.jsdelivr.net/npm/styimat/styimat.min.js"></script>
33
36
  ```
34
37
 
38
+ ## 特性
39
+
40
+ - **Lab/LCH 颜色支持** - 将 Lab 和 LCH 颜色转换为 CSS 兼容的 RGB
41
+ - **广色域显示** - 自动检测并支持 Display P3 广色域显示器
42
+ - **变量系统** - 类似 Sass 的变量系统,支持作用域变量
43
+ -️ **嵌套规则** - 支持 Sass 风格的嵌套选择器
44
+ - **十六进制语法** - 支持 `lab#L16A16B16` 和 `lch#L16C16H` 简洁语法
45
+ - **增强数学计算** - 支持 `math()` 函数和复杂数学表达式
46
+ - **轻量高效** - 无依赖,压缩后仅约 20KB
47
+ - **多种使用方式** - 浏览器、Node.js、命令行均可使用
48
+ - **灵活API** - 支持函数调用、模板标签、对象配置等多种用法
49
+
35
50
  ## 快速开始
36
51
 
37
52
  ### 浏览器中使用
@@ -108,6 +123,123 @@ npm install -g styimat
108
123
  styimat input.css output.css
109
124
  ```
110
125
 
126
+ ## 高级用法
127
+
128
+ ### 1. 作为函数直接调用
129
+
130
+ `styimat` 本身是一个可调用的函数,支持多种调用方式:
131
+
132
+ ```javascript
133
+ // 方式1:直接传入CSS字符串
134
+ const css1 = styimat(`
135
+ $primary: lab(54.7 77.9 80.1);
136
+ .button { color: $primary; }
137
+ `);
138
+
139
+ // 方式2:传入配置对象(返回函数本身,可以链式调用)
140
+ styimat({
141
+ enableP3: true,
142
+ enableMath: true,
143
+ mathPrecision: 8
144
+ });
145
+
146
+ // 方式3:无参数调用(自动处理页面中所有标记的<style>标签)
147
+ styimat();
148
+ ```
149
+
150
+ ### 2. 作为模板标签使用
151
+
152
+ `styimat` 可以作为ES6模板字符串的标签函数,提供更简洁的语法:
153
+
154
+ ```javascript
155
+ // 基本模板标签用法
156
+ const css = styimat`
157
+ $primary: lab(54.7 77.9 80.1);
158
+ $secondary: lch(44.7 67.9 210);
159
+
160
+ .button {
161
+ background: $primary;
162
+ color: white;
163
+
164
+ &:hover {
165
+ background: $secondary;
166
+ }
167
+ }
168
+ `;
169
+
170
+ // 模板标签支持插值
171
+ const theme = 'dark';
172
+ const cssWithVars = styimat`
173
+ $primary: ${theme === 'dark' ? 'lab(30 40 50)' : 'lab(70 20 -10)'};
174
+
175
+ body {
176
+ background: $primary;
177
+ }
178
+ `;
179
+
180
+ // 复杂的插值示例
181
+ const spacing = 1.5;
182
+ const cssComplex = styimat`
183
+ $base-spacing: ${spacing}rem;
184
+ $large-spacing: math(${spacing} * 2);
185
+
186
+ .container {
187
+ padding: $base-spacing;
188
+ margin: $large-spacing;
189
+ }
190
+ `;
191
+ ```
192
+
193
+ ### 3. 作为对象方法调用
194
+
195
+ 除了函数调用,`styimat` 还提供了对象方法:
196
+
197
+ ```javascript
198
+ // 1. 转换CSS
199
+ const converted = styimat.convert(cssString, config);
200
+
201
+ // 2. 应用到页面
202
+ const styleElement = styimat.apply(cssString, config);
203
+
204
+ // 3. 配置全局设置
205
+ styimat.config({
206
+ enableP3: true,
207
+ enableMath: true,
208
+ indentSize: 2
209
+ });
210
+
211
+ // 4. 使用工具方法
212
+ const mathResult = styimat.math.evaluate('100px / 2'); // "50px"
213
+ const rgbColor = styimat.colorUtils.labToRGB(54.7, 77.9, 80.1);
214
+ ```
215
+
216
+ ### 4. 混合使用示例
217
+
218
+ ```javascript
219
+ // 先配置,然后使用模板标签
220
+ styimat.config({ enableP3: true });
221
+
222
+ const designSystem = styimat`
223
+ /* 设计系统变量 */
224
+ $primary: lab#8CFF80;
225
+ $secondary: lch#6CFF180;
226
+ $spacing-unit: 1rem;
227
+
228
+ /* 组件样式 */
229
+ .btn {
230
+ padding: $spacing-unit;
231
+ background: $primary;
232
+
233
+ &.secondary {
234
+ background: $secondary;
235
+ }
236
+ }
237
+ `;
238
+
239
+ // 应用到页面
240
+ styimat.apply(designSystem);
241
+ ```
242
+
111
243
  ## 语法指南
112
244
 
113
245
  ### 变量定义
@@ -125,6 +257,24 @@ $font-family: 'Inter', sans-serif;
125
257
  }
126
258
  ```
127
259
 
260
+ ### Math 计算语法
261
+
262
+ ```css
263
+ /* math() 函数 */
264
+ .element {
265
+ width: math(100% / 3);
266
+ height: math(200px + 30px);
267
+ padding: math(2rem * 1.5);
268
+ margin: math(calc(100vh - 200px) / 2);
269
+ }
270
+
271
+ /* 支持单位运算 */
272
+ .container {
273
+ font-size: math(16px * 1.125); /* 18px */
274
+ gap: math(24px + 1rem); /* 支持混合单位 */
275
+ }
276
+ ```
277
+
128
278
  ### Lab 颜色语法
129
279
 
130
280
  ```css
@@ -188,10 +338,18 @@ LCH 颜色使用 `lch#` 前缀,后跟 6 个字符(最后1-3位是十进制
188
338
 
189
339
  .header {
190
340
  font-size: 2rem;
341
+
342
+ .title {
343
+ color: lab(20 40 60);
344
+ }
191
345
  }
192
346
 
193
347
  @media (min-width: 768px) {
194
348
  padding: 2rem;
349
+
350
+ .header {
351
+ font-size: 3rem;
352
+ }
195
353
  }
196
354
  }
197
355
  ```
@@ -203,10 +361,12 @@ LCH 颜色使用 `lch#` 前缀,后跟 6 个字符(最后1-3位是十进制
203
361
  $base-color: lab(54.7 77.9 80.1);
204
362
  $text-color: lch(20 30 270);
205
363
  $border-color: $base-color;
364
+ $padding-large: math(2rem * 1.5);
206
365
 
207
366
  .element {
208
367
  color: $text-color;
209
368
  border: 1px solid $border-color;
369
+ padding: $padding-large;
210
370
  }
211
371
  ```
212
372
 
@@ -230,14 +390,23 @@ styimat.config({
230
390
  convertLabToRGB: true,
231
391
  convertLchToRGB: true,
232
392
  enableP3: true,
393
+ enableMath: true,
394
+ mathPrecision: 6,
233
395
  indentSize: 2,
234
396
  enableNesting: true
235
397
  });
236
398
 
399
+ // 数学工具
400
+ styimat.math.evaluate('100px / 2'); // "50px"
401
+ styimat.math.evaluate('(16px * 1.5) + 4px'); // "28px"
402
+
237
403
  // 颜色工具
238
404
  const rgb = styimat.colorUtils.labToRGB(54.7, 77.9, 80.1);
239
405
  const p3 = styimat.colorUtils.labToP3(54.7, 77.9, 80.1);
240
406
  const lab = styimat.colorUtils.lchToLab(54.7, 78.9, 38.5);
407
+
408
+ // 解析颜色
409
+ const colorInfo = styimat.colorUtils.parseColor('lab(54.7 77.9 80.1 / 0.8)');
241
410
  ```
242
411
 
243
412
  ### 配置选项
@@ -254,6 +423,8 @@ const lab = styimat.colorUtils.lchToLab(54.7, 78.9, 38.5);
254
423
  | `convertLabToRGB` | boolean | `true` | 是否转换 Lab 颜色为 RGB |
255
424
  | `convertLchToRGB` | boolean | `true` | 是否转换 LCH 颜色为 RGB |
256
425
  | `enableP3` | boolean | `true` | 是否启用 Display P3 广色域支持 |
426
+ | `enableMath` | boolean | `true` | 是否启用 `math()` 函数增强 |
427
+ | `mathPrecision` | number | `6` | 数学计算的精度 |
257
428
 
258
429
  ## 示例
259
430
 
@@ -271,7 +442,7 @@ $border-radius: 0.5rem;
271
442
  .button {
272
443
  background-color: $brand-primary;
273
444
  color: white;
274
- padding: $spacing-unit calc($spacing-unit * 2);
445
+ padding: $spacing-unit math($spacing-unit * 2);
275
446
  border-radius: $border-radius;
276
447
  border: 2px solid lab(44.7 67.9 70.1);
277
448
 
@@ -281,8 +452,8 @@ $border-radius: 0.5rem;
281
452
  }
282
453
 
283
454
  &.large {
284
- padding: calc($spacing-unit * 1.5) calc($spacing-unit * 3);
285
- font-size: 1.25rem;
455
+ padding: math($spacing-unit * 1.5) math($spacing-unit * 3);
456
+ font-size: math(1rem * 1.25);
286
457
  }
287
458
  }
288
459
 
@@ -293,6 +464,7 @@ $border-radius: 0.5rem;
293
464
 
294
465
  @media (max-width: 768px) {
295
466
  padding: 0 $spacing-unit;
467
+ font-size: math(16px * 0.9);
296
468
  }
297
469
  }
298
470
  ```
@@ -318,18 +490,72 @@ $primary-transparent: lab(55 190 0 / 0.8);
318
490
  .card {
319
491
  background: linear-gradient(135deg, $primary, $secondary);
320
492
  border: 1px solid $accent;
493
+ width: math(100% / 3);
321
494
  }
322
495
  }
323
496
  ```
324
497
 
498
+ ### Math() 函数高级示例
499
+
500
+ ```css
501
+ /* 复杂数学计算 */
502
+ .grid {
503
+ --columns: 12;
504
+ --gap: 1rem;
505
+
506
+ .col-4 {
507
+ width: math(calc(100% / var(--columns)) * 4);
508
+ }
509
+
510
+ .col-6 {
511
+ width: math(calc(100% / var(--columns)) * 6 - var(--gap));
512
+ }
513
+ }
514
+
515
+ .responsive {
516
+ font-size: math(clamp(16px, 2vw + 8px, 24px));
517
+ padding: math(max(1rem, 2vh));
518
+ }
519
+
520
+ .aspect-ratio {
521
+ height: math(100vw / 16 * 9); /* 16:9 宽高比 */
522
+ }
523
+ ```
524
+
525
+ ## Git 仓库
526
+
527
+ - **主仓库**: [https://gitee.com/wxy6987/styimat](https://gitee.com/wxy6987/styimat)
528
+ - **ZIP 下载**: [https://gitee.com/wxy6987/styimat/repository/archive/main.zip](https://gitee.com/wxy6987/styimat/repository/archive/main.zip)
529
+ - **Issues**: [问题反馈](https://gitee.com/wxy6987/styimat/issues)
530
+ - **Pull Requests**: [贡献代码](https://gitee.com/wxy6987/styimat/pulls)
531
+
532
+ ## 贡献指南
533
+
534
+ 欢迎贡献代码!请遵循以下步骤:
535
+
536
+ 1. **Fork 本仓库**
537
+ 2. **创建特性分支**
538
+ ```bash
539
+ git checkout -b feature/AmazingFeature
540
+ ```
541
+ 3. **提交更改**
542
+ ```bash
543
+ git commit -m 'Add some AmazingFeature'
544
+ ```
545
+ 4. **推送到分支**
546
+ ```bash
547
+ git push origin feature/AmazingFeature
548
+ ```
549
+ 5. **提交 Pull Request**
550
+
325
551
  ## 许可证
326
552
 
327
553
  MIT © 王小玗 2025
328
554
 
329
- ## 贡献
555
+ ## 支持
330
556
 
331
- 欢迎提交 Issue Pull Request
557
+ 如果发现 bug 或有功能建议,请在 [Gitee Issues](https://gitee.com/wxy6987/styimat/issues) 中创建 Issue
332
558
 
333
- ## 支持
559
+ ---
334
560
 
335
- 如果发现 bug 或有功能建议,请在 GitHub 仓库中创建 Issue。
561
+ **如果这个项目对你有帮助,请给个 [![Star](https://gitee.com/wxy6987/styimat/badge/star.svg?theme=white)](https://gitee.com/wxy6987/styimat)**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "styimat",
3
- "version": "1.3.0",
4
- "description": "A powerful CSS variable preprocessor with nested selectors, Lab/LCH color spaces conversion and modern color features.",
3
+ "version": "1.5.0",
4
+ "description": "一个高效的CSS变量预处理库,支持Lab/LCH颜色空间自动转换、嵌套选择器和Display P3广色域,让现代CSS开发更简洁强大。",
5
5
  "keywords": [
6
6
  "css",
7
7
  "preprocessor",
package/styimat.js CHANGED
@@ -1,9 +1,14 @@
1
+ /*!
2
+ * MIT License
3
+ * Copyright (c) 2025 王小玗
4
+ */
1
5
  /**
2
6
  * CSS 变量预处理库 - 增强版
3
7
  * 支持 CSS 变量预处理、嵌套选择器和嵌套变量
4
8
  * 支持 Lab 和 LCH 颜色空间转换为 RGB(CSS标准格式)
5
9
  * 支持 lab# 和 lch# 十六进制语法
6
10
  * 支持 Display P3 广色域显示器
11
+ * 增强 math() 函数,支持复杂数学计算
7
12
  */
8
13
  (function(root, factory) {
9
14
  if (typeof define === 'function' && define.amd) {
@@ -26,9 +31,11 @@
26
31
  enableNesting: true,
27
32
  autoProcessStyleTags: true,
28
33
  styleTagAttribute: 'e',
29
- convertLabToRGB: true, // 是否将lab颜色转换为rgb
30
- convertLchToRGB: true, // 是否将lch颜色转换为rgb
31
- enableP3: true, // 是否启用P3广色域支持
34
+ convertLabToRGB: true,
35
+ convertLchToRGB: true,
36
+ enableP3: true,
37
+ enableMath: true, // 启用math()函数增强
38
+ mathPrecision: 6, // 数学计算精度
32
39
  };
33
40
 
34
41
  // 全局P3支持检测结果
@@ -181,6 +188,252 @@
181
188
  return -1;
182
189
  }
183
190
 
191
+ /**
192
+ * 增强的数学表达式解析和计算
193
+ * @param {string} expression - 数学表达式
194
+ * @param {Object} config - 配置对象
195
+ * @returns {string} 计算结果
196
+ */
197
+ function evaluateMathExpression(expression, config) {
198
+ // 如果禁用math()增强,则返回原始表达式
199
+ if (!config.enableMath) {
200
+ return `math(${expression})`;
201
+ }
202
+
203
+ try {
204
+ // 清理表达式:移除空白字符
205
+ let cleanExpr = expression.replace(/\s+/g, '');
206
+
207
+ // 解析数学表达式
208
+ const result = parseMathExpression(cleanExpr, config);
209
+
210
+ // 如果结果包含单位,保留原始格式
211
+ if (hasUnits(cleanExpr)) {
212
+ // 处理带单位的表达式
213
+ return processUnitExpression(cleanExpr, result);
214
+ } else {
215
+ // 纯数字表达式,直接计算结果
216
+ return roundNumber(result, config.mathPrecision);
217
+ }
218
+ } catch (error) {
219
+ console.warn('math()表达式解析失败:', expression, error);
220
+ return `math(${expression})`;
221
+ }
222
+ }
223
+
224
+ /**
225
+ * 解析数学表达式
226
+ * @param {string} expr - 清理后的表达式
227
+ * @param {Object} config - 配置对象
228
+ * @returns {number} 计算结果
229
+ */
230
+ function parseMathExpression(expr, config) {
231
+ // 处理括号
232
+ while (expr.includes('(') && expr.includes(')')) {
233
+ const start = expr.lastIndexOf('(');
234
+ const end = expr.indexOf(')', start);
235
+
236
+ if (end === -1) break;
237
+
238
+ const inner = expr.substring(start + 1, end);
239
+ const innerResult = parseMathExpression(inner, config);
240
+
241
+ expr = expr.substring(0, start) + innerResult + expr.substring(end + 1);
242
+ }
243
+
244
+ // 现在expr应该没有括号了
245
+ return evaluateSimpleExpression(expr, config);
246
+ }
247
+
248
+ /**
249
+ * 评估简单表达式(无括号)
250
+ * @param {string} expr - 简单表达式
251
+ * @param {Object} config - 配置对象
252
+ * @returns {number} 计算结果
253
+ */
254
+ function evaluateSimpleExpression(expr, config) {
255
+ // 处理操作符优先级:乘除优先于加减
256
+ const operators = [
257
+ { regex: /([\d.]+(?:[a-zA-Z%]+)?)\*([\d.]+(?:[a-zA-Z%]+)?)/, handler: multiply },
258
+ { regex: /([\d.]+(?:[a-zA-Z%]+)?)\/([\d.]+(?:[a-zA-Z%]+)?)/, handler: divide },
259
+ { regex: /([\d.]+(?:[a-zA-Z%]+)?)\+([\d.]+(?:[a-zA-Z%]+)?)/, handler: add },
260
+ { regex: /([\d.]+(?:[a-zA-Z%]+)?)-([\d.]+(?:[a-zA-Z%]+)?)/, handler: subtract }
261
+ ];
262
+
263
+ let lastExpr = expr;
264
+
265
+ // 按照优先级处理操作符
266
+ for (const op of operators) {
267
+ let match;
268
+ while ((match = expr.match(op.regex)) !== null) {
269
+ const left = parseValueWithUnit(match[1]);
270
+ const right = parseValueWithUnit(match[2]);
271
+ const result = op.handler(left, right);
272
+
273
+ // 替换匹配的部分
274
+ expr = expr.substring(0, match.index) +
275
+ result.value +
276
+ expr.substring(match.index + match[0].length);
277
+ }
278
+ }
279
+
280
+ // 如果表达式无法进一步简化,尝试解析为数字
281
+ if (expr !== lastExpr) {
282
+ return evaluateSimpleExpression(expr, config);
283
+ }
284
+
285
+ // 解析最终结果
286
+ const parsed = parseValueWithUnit(expr);
287
+ return parsed.value;
288
+ }
289
+
290
+ /**
291
+ * 解析带单位的数值
292
+ * @param {string} str - 字符串值
293
+ * @returns {Object} {value: number, unit: string}
294
+ */
295
+ function parseValueWithUnit(str) {
296
+ // 匹配数值和单位
297
+ const match = str.match(/^([\d.]+)([a-zA-Z%]*)$/);
298
+
299
+ if (!match) {
300
+ throw new Error(`无法解析值: ${str}`);
301
+ }
302
+
303
+ const value = parseFloat(match[1]);
304
+ const unit = match[2] || '';
305
+
306
+ return { value, unit };
307
+ }
308
+
309
+ /**
310
+ * 检查表达式是否包含单位
311
+ * @param {string} expr - 表达式
312
+ * @returns {boolean} 是否包含单位
313
+ */
314
+ function hasUnits(expr) {
315
+ return /[a-zA-Z%]/.test(expr);
316
+ }
317
+
318
+ /**
319
+ * 处理带单位的表达式
320
+ * @param {string} originalExpr - 原始表达式
321
+ * @param {number} result - 计算结果
322
+ * @returns {string} 处理后的表达式
323
+ */
324
+ function processUnitExpression(originalExpr, result) {
325
+ // 提取原始表达式中的单位
326
+ const unitMatch = originalExpr.match(/([a-zA-Z%]+)(?!.*[a-zA-Z%])/);
327
+
328
+ if (unitMatch) {
329
+ return result + unitMatch[1];
330
+ }
331
+
332
+ // 如果没有明确单位,检查是否为百分比
333
+ if (originalExpr.includes('%')) {
334
+ return result + '%';
335
+ }
336
+
337
+ // 默认为像素
338
+ return result + 'px';
339
+ }
340
+
341
+ // 数学运算函数
342
+ function multiply(a, b) {
343
+ if (a.unit === b.unit || (!a.unit && !b.unit)) {
344
+ return { value: a.value * b.value, unit: a.unit || b.unit };
345
+ }
346
+
347
+ // 单位不匹配,返回原始表达式
348
+ return { value: `${a.value}${a.unit}*${b.value}${b.unit}`, unit: '' };
349
+ }
350
+
351
+ function divide(a, b) {
352
+ if (b.value === 0) {
353
+ throw new Error('除以零');
354
+ }
355
+
356
+ if (a.unit && !b.unit) {
357
+ // 如 100px / 2
358
+ return { value: a.value / b.value, unit: a.unit };
359
+ } else if (a.unit === b.unit) {
360
+ // 如 100px / 2px
361
+ return { value: a.value / b.value, unit: '' };
362
+ }
363
+
364
+ // 单位不匹配,返回原始表达式
365
+ return { value: `${a.value}${a.unit}/${b.value}${b.unit}`, unit: '' };
366
+ }
367
+
368
+ function add(a, b) {
369
+ if (a.unit === b.unit) {
370
+ return { value: a.value + b.value, unit: a.unit };
371
+ }
372
+
373
+ // 单位不匹配,返回原始表达式
374
+ return { value: `${a.value}${a.unit}+${b.value}${b.unit}`, unit: '' };
375
+ }
376
+
377
+ function subtract(a, b) {
378
+ if (a.unit === b.unit) {
379
+ return { value: a.value - b.value, unit: a.unit };
380
+ }
381
+
382
+ // 单位不匹配,返回原始表达式
383
+ return { value: `${a.value}${a.unit}-${b.value}${b.unit}`, unit: '' };
384
+ }
385
+
386
+ /**
387
+ * 四舍五入到指定精度
388
+ * @param {number} num - 要四舍五入的数字
389
+ * @param {number} precision - 精度
390
+ * @returns {string} 四舍五入后的数字字符串
391
+ */
392
+ function roundNumber(num, precision = 6) {
393
+ const factor = Math.pow(10, precision);
394
+ const rounded = Math.round(num * factor) / factor;
395
+
396
+ // 移除不必要的尾随零
397
+ const str = rounded.toString();
398
+ if (str.includes('.')) {
399
+ return str.replace(/(\.\d*?)0+$/, '$1').replace(/\.$/, '');
400
+ }
401
+ return str;
402
+ }
403
+
404
+ /**
405
+ * 处理所有的math()函数
406
+ * @param {string} cssValue - CSS属性值
407
+ * @param {Object} config - 配置对象
408
+ * @returns {string} 转换后的CSS值
409
+ */
410
+ function processMathFunctions(cssValue, config) {
411
+ if (!config.enableMath) {
412
+ return cssValue;
413
+ }
414
+
415
+ let result = cssValue;
416
+
417
+ // 匹配math()函数
418
+ const mathRegex = /math\(([^)]+)\)/gi;
419
+
420
+ // 递归处理嵌套的math()函数
421
+ const processMath = (str) => {
422
+ return str.replace(mathRegex, (match, expression) => {
423
+ return evaluateMathExpression(expression, config);
424
+ });
425
+ };
426
+
427
+ // 递归处理,直到没有更多math()函数
428
+ let lastResult;
429
+ do {
430
+ lastResult = result;
431
+ result = processMath(result);
432
+ } while (result !== lastResult && result.includes('math('));
433
+
434
+ return result;
435
+ }
436
+
184
437
  /**
185
438
  * 解析并转换所有的 Lab 和 LCH 颜色(全局一次性处理)
186
439
  * @param {string} cssValue - CSS属性值
@@ -616,21 +869,32 @@
616
869
  }
617
870
 
618
871
  /**
619
- * 处理CSS值,根据配置转换LABLCH颜色
872
+ * 处理CSS值,根据配置转换LABLCH颜色和math()函数
620
873
  * @param {string} value - CSS属性值
621
874
  * @param {Object} config - 配置对象
622
875
  * @returns {string} 处理后的值
623
876
  */
624
877
  function processCSSValue(value, config) {
625
- // 直接调用全局转换函数
626
- return convertAllLabLchColors(value, config);
878
+ let result = value;
879
+
880
+ // 1. 首先处理math()函数
881
+ if (config.enableMath) {
882
+ result = processMathFunctions(result, config);
883
+ }
884
+
885
+ // 2. 然后处理LAB和LCH颜色
886
+ if (config.convertLabToRGB || config.convertLchToRGB) {
887
+ result = convertAllLabLchColors(result, config);
888
+ }
889
+
890
+ return result;
627
891
  }
628
892
 
629
893
  // 私有方法:提取变量定义并移除(支持嵌套作用域)
630
894
  function extractVariablesAndCSS(cssText, config) {
631
895
  const lines = cssText.split('\n');
632
896
  const globalVariables = {};
633
- const selectorVariables = new Map(); // 选择器 -> 变量映射
897
+ const selectorVariables = new Map();
634
898
  let cssWithoutVars = '';
635
899
  let currentSelector = null;
636
900
  let inSelectorBlock = false;
@@ -644,8 +908,8 @@
644
908
 
645
909
  if (varMatch) {
646
910
  const [, varName, varValue] = varMatch;
647
- // 处理变量值中的LAB/LCH颜色转换
648
- const processedValue = convertAllLabLchColors(
911
+ // 处理变量值中的数学表达式和颜色转换
912
+ const processedValue = processCSSValue(
649
913
  replaceVariableUsesInValue(varValue.trim(), {
650
914
  ...globalVariables,
651
915
  ...(currentSelector ? selectorVariables.get(currentSelector) || {} : {})
@@ -764,11 +1028,11 @@
764
1028
  if (!trimmed.includes('{') && !trimmed.includes('}') && trimmed.includes(':')) {
765
1029
  if (stack.length > 0) {
766
1030
  const currentRule = stack[stack.length - 1];
767
- // 处理属性值中的LAB/LCH颜色
1031
+ // 处理属性值中的math()函数和颜色转换
768
1032
  const parsed = parseSingleLineCSS(trimmed);
769
1033
  parsed.forEach(obj => {
770
1034
  const key = Object.keys(obj)[0];
771
- const value = convertAllLabLchColors(obj[key], config);
1035
+ const value = processCSSValue(obj[key], config);
772
1036
  currentRule.properties.push(`${key}: ${value}`);
773
1037
  });
774
1038
  }
@@ -821,11 +1085,11 @@
821
1085
  if (rule.properties.length > 0) {
822
1086
  result += (isAt ? "" : fullSelector) + ' {\n';
823
1087
  for (const prop of rule.properties) {
824
- // 再次处理属性值(确保LAB/LCH颜色被转换)
1088
+ // 再次处理属性值(确保math()函数和颜色被转换)
825
1089
  const parsed = parseSingleLineCSS(prop);
826
1090
  parsed.forEach(obj => {
827
1091
  const key = Object.keys(obj)[0];
828
- const value = convertAllLabLchColors(obj[key], config);
1092
+ const value = processCSSValue(obj[key], config);
829
1093
  result += (isParentAt ? ' ' : '') + ` ${key}: ${value};\n`;
830
1094
  });
831
1095
  }
@@ -870,10 +1134,8 @@
870
1134
  return `var(--${varName})`;
871
1135
  });
872
1136
 
873
- // 最后处理所有的LABLCH颜色(一次性全局处理)
874
- if (config.convertLabToRGB || config.convertLchToRGB) {
875
- result = convertAllLabLchColors(result, config);
876
- }
1137
+ // 最后处理所有的LABLCH颜色和math()函数
1138
+ result = processCSSValue(result, config);
877
1139
 
878
1140
  return result;
879
1141
  }
@@ -886,7 +1148,7 @@
886
1148
 
887
1149
  const declarations = Object.entries(variables)
888
1150
  .map(([name, value]) => {
889
- const processedValue = convertAllLabLchColors(
1151
+ const processedValue = processCSSValue(
890
1152
  replaceVariableUsesInValue(value, variables),
891
1153
  config
892
1154
  );
@@ -922,12 +1184,8 @@
922
1184
  currentSelector = null;
923
1185
  }
924
1186
 
925
- // 使用全局转换函数处理所有颜色
926
- if (config.convertLabToRGB || config.convertLchToRGB) {
927
- result += convertAllLabLchColors(line, config) + '\n';
928
- } else {
929
- result += line + '\n';
930
- }
1187
+ // 使用全局处理函数处理所有math()和颜色
1188
+ result += processCSSValue(line, config) + '\n';
931
1189
  }
932
1190
 
933
1191
  return result.trim();
@@ -1024,7 +1282,7 @@
1024
1282
  convert,
1025
1283
  apply,
1026
1284
  config,
1027
- version: '1.8.0',
1285
+ version: '1.9.0',
1028
1286
 
1029
1287
  // 检测P3支持
1030
1288
  supportsP3: detectP3Support(),
@@ -1032,6 +1290,58 @@
1032
1290
  // 重新检测P3支持(如果配置变化)
1033
1291
  detectP3Support: detectP3Support,
1034
1292
 
1293
+ // 数学计算工具方法
1294
+ math: {
1295
+ /**
1296
+ * 计算数学表达式
1297
+ * @param {string} expression - 数学表达式
1298
+ * @returns {string} 计算结果
1299
+ */
1300
+ evaluate: function(expression) {
1301
+ return evaluateMathExpression(expression, defaultConfig);
1302
+ },
1303
+
1304
+ /**
1305
+ * 解析带单位的数值
1306
+ * @param {string} value - 带单位的字符串
1307
+ * @returns {Object} {value: number, unit: string}
1308
+ */
1309
+ parseUnit: parseValueWithUnit,
1310
+
1311
+ /**
1312
+ * 四舍五入数字
1313
+ * @param {number} num - 要四舍五入的数字
1314
+ * @param {number} precision - 精度
1315
+ * @returns {string} 四舍五入后的数字字符串
1316
+ */
1317
+ round: roundNumber,
1318
+
1319
+ /**
1320
+ * 测试数学表达式
1321
+ * @param {string} expression - 要测试的表达式
1322
+ * @param {Object} testConfig - 测试配置
1323
+ * @returns {Object} 测试结果
1324
+ */
1325
+ test: function(expression, testConfig = {}) {
1326
+ const config = { ...defaultConfig, ...testConfig };
1327
+ try {
1328
+ const result = evaluateMathExpression(expression, config);
1329
+ return {
1330
+ success: true,
1331
+ expression,
1332
+ result,
1333
+ parsed: parseValueWithUnit(result.replace(/^calc\(|\)$/g, ''))
1334
+ };
1335
+ } catch (error) {
1336
+ return {
1337
+ success: false,
1338
+ expression,
1339
+ error: error.message
1340
+ };
1341
+ }
1342
+ }
1343
+ },
1344
+
1035
1345
  // 颜色转换工具方法
1036
1346
  colorUtils: {
1037
1347
  labToRGB: preciseLabToRGB,
@@ -1143,6 +1453,66 @@
1143
1453
  }
1144
1454
  }
1145
1455
  };
1456
+
1457
+
1458
+ // 创建一个可调用的主函数
1459
+ const styimat = function(...args) {
1460
+ // 检查是否是模板字符串调用(标签函数)
1461
+ if (args.length > 1 || (args[0] && args[0].raw)) {
1462
+ // 处理模板字符串
1463
+ return handleTemplateTag(...args);
1464
+ }
1465
+
1466
+ // 获取第一个参数
1467
+ const firstArg = args[0];
1468
+
1469
+ // 如果传入CSS文本,则编译并返回结果
1470
+ if (typeof firstArg === 'string') {
1471
+ return convert(firstArg, { ...defaultConfig, ...args[1] });
1472
+ }
1473
+
1474
+ // 如果传入配置对象,则应用配置
1475
+ if (typeof firstArg === 'object' && firstArg !== null) {
1476
+ defaultConfig = { ...defaultConfig, ...firstArg };
1477
+ return styimat;
1478
+ }
1479
+
1480
+ // 如果没有参数,执行自动处理
1481
+ if (args.length === 0) {
1482
+ return apply();
1483
+ }
1484
+
1485
+ return styimat;
1486
+ };
1487
+
1488
+ // 处理模板字符串的函数
1489
+ function handleTemplateTag(strings, ...values) {
1490
+ // 拼接模板字符串
1491
+ let cssText = strings[0];
1492
+
1493
+ for (let i = 0; i < values.length; i++) {
1494
+ // 处理插值(支持字符串、数字、函数等)
1495
+ const value = values[i];
1496
+ let result = '';
1497
+
1498
+ if (typeof value === 'function') {
1499
+ result = value();
1500
+ } else if (Array.isArray(value)) {
1501
+ result = value.join(' ');
1502
+ } else {
1503
+ result = String(value != null ? value : '');
1504
+ }
1505
+
1506
+ cssText += result + strings[i + 1];
1507
+ }
1508
+
1509
+ // 使用convert函数处理
1510
+ return convert(cssText, defaultConfig);
1511
+ }
1512
+
1513
+ // 将API的所有方法复制到主函数上
1514
+ Object.assign(styimat, api);
1515
+ Object.setPrototypeOf(styimat, Function.prototype);
1146
1516
 
1147
1517
  // 自动初始化
1148
1518
  if (typeof window !== 'undefined') {
@@ -1152,12 +1522,12 @@
1152
1522
  const element = this;
1153
1523
  return new Proxy({}, {
1154
1524
  get(target, prop) {
1155
- const varName = prop.startsWith('$') ? prop.slice(1) : prop;
1525
+ const varName = prop.startsWith('--') ? prop : `--${prop}`;
1156
1526
  return element.style.getPropertyValue(varName);
1157
1527
  },
1158
1528
 
1159
1529
  set(target, prop, value) {
1160
- const varName = prop.startsWith('$') ? prop.slice(1) : prop;
1530
+ const varName = prop.startsWith('--') ? prop : `--${prop}`;
1161
1531
  element.style.setProperty(varName, value);
1162
1532
  return true;
1163
1533
  }
@@ -1166,5 +1536,5 @@
1166
1536
  });
1167
1537
  }
1168
1538
 
1169
- return api;
1539
+ return styimat;
1170
1540
  }));
package/styimat.min.js CHANGED
@@ -1,9 +1,14 @@
1
+ /*!
2
+ * MIT License
3
+ * Copyright (c) 2025 王小玗
4
+ */
1
5
  /**
2
6
  * CSS 变量预处理库 - 增强版
3
7
  * 支持 CSS 变量预处理、嵌套选择器和嵌套变量
4
8
  * 支持 Lab 和 LCH 颜色空间转换为 RGB(CSS标准格式)
5
9
  * 支持 lab# 和 lch# 十六进制语法
6
10
  * 支持 Display P3 广色域显示器
11
+ * 增强 math() 函数,支持复杂数学计算
7
12
  */
8
13
  (function(root,factory){if(typeof define==="function"&&define.amd){
9
14
  // AMD 支持 (RequireJS)
@@ -13,9 +18,8 @@ module.exports=factory()}else{
13
18
  // 浏览器全局变量
14
19
  root.styimat=factory()}})(typeof self!=="undefined"?self:this,function(){
15
20
  // 默认配置
16
- let defaultConfig={rootSelector:":root",variablePrefix:"--",preserveOriginal:false,indentSize:2,enableNesting:true,autoProcessStyleTags:true,styleTagAttribute:"e",convertLabToRGB:true,// 是否将lab颜色转换为rgb
17
- convertLchToRGB:true,// 是否将lch颜色转换为rgb
18
- enableP3:true};
21
+ let defaultConfig={rootSelector:":root",variablePrefix:"--",preserveOriginal:false,indentSize:2,enableNesting:true,autoProcessStyleTags:true,styleTagAttribute:"e",convertLabToRGB:true,convertLchToRGB:true,enableP3:true,enableMath:true,// 启用math()函数增强
22
+ mathPrecision:6};
19
23
  // 全局P3支持检测结果
20
24
  let p3Supported=null;
21
25
  /**
@@ -68,6 +72,108 @@ result.push({[property]:value})}return result}
68
72
  if((char==='"'||char==="'")&&!inQuotes){inQuotes=true;quoteChar=char}else if(char===quoteChar&&inQuotes){inQuotes=false;quoteChar=""}
69
73
  // 如果找到冒号且不在引号内,返回位置
70
74
  if(char===":"&&!inQuotes){return i}}return-1}
75
+ /**
76
+ * 增强的数学表达式解析和计算
77
+ * @param {string} expression - 数学表达式
78
+ * @param {Object} config - 配置对象
79
+ * @returns {string} 计算结果
80
+ */function evaluateMathExpression(expression,config){
81
+ // 如果禁用math()增强,则返回原始表达式
82
+ if(!config.enableMath){return`math(${expression})`}try{
83
+ // 清理表达式:移除空白字符
84
+ let cleanExpr=expression.replace(/\s+/g,"");
85
+ // 解析数学表达式
86
+ const result=parseMathExpression(cleanExpr,config);
87
+ // 如果结果包含单位,保留原始格式
88
+ if(hasUnits(cleanExpr)){
89
+ // 处理带单位的表达式
90
+ return processUnitExpression(cleanExpr,result)}else{
91
+ // 纯数字表达式,直接计算结果
92
+ return roundNumber(result,config.mathPrecision)}}catch(error){console.warn("math()表达式解析失败:",expression,error);return`math(${expression})`}}
93
+ /**
94
+ * 解析数学表达式
95
+ * @param {string} expr - 清理后的表达式
96
+ * @param {Object} config - 配置对象
97
+ * @returns {number} 计算结果
98
+ */function parseMathExpression(expr,config){
99
+ // 处理括号
100
+ while(expr.includes("(")&&expr.includes(")")){const start=expr.lastIndexOf("(");const end=expr.indexOf(")",start);if(end===-1)break;const inner=expr.substring(start+1,end);const innerResult=parseMathExpression(inner,config);expr=expr.substring(0,start)+innerResult+expr.substring(end+1)}
101
+ // 现在expr应该没有括号了
102
+ return evaluateSimpleExpression(expr,config)}
103
+ /**
104
+ * 评估简单表达式(无括号)
105
+ * @param {string} expr - 简单表达式
106
+ * @param {Object} config - 配置对象
107
+ * @returns {number} 计算结果
108
+ */function evaluateSimpleExpression(expr,config){
109
+ // 处理操作符优先级:乘除优先于加减
110
+ const operators=[{regex:/([\d.]+(?:[a-zA-Z%]+)?)\*([\d.]+(?:[a-zA-Z%]+)?)/,handler:multiply},{regex:/([\d.]+(?:[a-zA-Z%]+)?)\/([\d.]+(?:[a-zA-Z%]+)?)/,handler:divide},{regex:/([\d.]+(?:[a-zA-Z%]+)?)\+([\d.]+(?:[a-zA-Z%]+)?)/,handler:add},{regex:/([\d.]+(?:[a-zA-Z%]+)?)-([\d.]+(?:[a-zA-Z%]+)?)/,handler:subtract}];let lastExpr=expr;
111
+ // 按照优先级处理操作符
112
+ for(const op of operators){let match;while((match=expr.match(op.regex))!==null){const left=parseValueWithUnit(match[1]);const right=parseValueWithUnit(match[2]);const result=op.handler(left,right);
113
+ // 替换匹配的部分
114
+ expr=expr.substring(0,match.index)+result.value+expr.substring(match.index+match[0].length)}}
115
+ // 如果表达式无法进一步简化,尝试解析为数字
116
+ if(expr!==lastExpr){return evaluateSimpleExpression(expr,config)}
117
+ // 解析最终结果
118
+ const parsed=parseValueWithUnit(expr);return parsed.value}
119
+ /**
120
+ * 解析带单位的数值
121
+ * @param {string} str - 字符串值
122
+ * @returns {Object} {value: number, unit: string}
123
+ */function parseValueWithUnit(str){
124
+ // 匹配数值和单位
125
+ const match=str.match(/^([\d.]+)([a-zA-Z%]*)$/);if(!match){throw new Error(`无法解析值: ${str}`)}const value=parseFloat(match[1]);const unit=match[2]||"";return{value:value,unit:unit}}
126
+ /**
127
+ * 检查表达式是否包含单位
128
+ * @param {string} expr - 表达式
129
+ * @returns {boolean} 是否包含单位
130
+ */function hasUnits(expr){return/[a-zA-Z%]/.test(expr)}
131
+ /**
132
+ * 处理带单位的表达式
133
+ * @param {string} originalExpr - 原始表达式
134
+ * @param {number} result - 计算结果
135
+ * @returns {string} 处理后的表达式
136
+ */function processUnitExpression(originalExpr,result){
137
+ // 提取原始表达式中的单位
138
+ const unitMatch=originalExpr.match(/([a-zA-Z%]+)(?!.*[a-zA-Z%])/);if(unitMatch){return result+unitMatch[1]}
139
+ // 如果没有明确单位,检查是否为百分比
140
+ if(originalExpr.includes("%")){return result+"%"}
141
+ // 默认为像素
142
+ return result+"px"}
143
+ // 数学运算函数
144
+ function multiply(a,b){if(a.unit===b.unit||!a.unit&&!b.unit){return{value:a.value*b.value,unit:a.unit||b.unit}}
145
+ // 单位不匹配,返回原始表达式
146
+ return{value:`${a.value}${a.unit}*${b.value}${b.unit}`,unit:""}}function divide(a,b){if(b.value===0){throw new Error("除以零")}if(a.unit&&!b.unit){
147
+ // 如 100px / 2
148
+ return{value:a.value/b.value,unit:a.unit}}else if(a.unit===b.unit){
149
+ // 如 100px / 2px
150
+ return{value:a.value/b.value,unit:""}}
151
+ // 单位不匹配,返回原始表达式
152
+ return{value:`${a.value}${a.unit}/${b.value}${b.unit}`,unit:""}}function add(a,b){if(a.unit===b.unit){return{value:a.value+b.value,unit:a.unit}}
153
+ // 单位不匹配,返回原始表达式
154
+ return{value:`${a.value}${a.unit}+${b.value}${b.unit}`,unit:""}}function subtract(a,b){if(a.unit===b.unit){return{value:a.value-b.value,unit:a.unit}}
155
+ // 单位不匹配,返回原始表达式
156
+ return{value:`${a.value}${a.unit}-${b.value}${b.unit}`,unit:""}}
157
+ /**
158
+ * 四舍五入到指定精度
159
+ * @param {number} num - 要四舍五入的数字
160
+ * @param {number} precision - 精度
161
+ * @returns {string} 四舍五入后的数字字符串
162
+ */function roundNumber(num,precision=6){const factor=Math.pow(10,precision);const rounded=Math.round(num*factor)/factor;
163
+ // 移除不必要的尾随零
164
+ const str=rounded.toString();if(str.includes(".")){return str.replace(/(\.\d*?)0+$/,"$1").replace(/\.$/,"")}return str}
165
+ /**
166
+ * 处理所有的math()函数
167
+ * @param {string} cssValue - CSS属性值
168
+ * @param {Object} config - 配置对象
169
+ * @returns {string} 转换后的CSS值
170
+ */function processMathFunctions(cssValue,config){if(!config.enableMath){return cssValue}let result=cssValue;
171
+ // 匹配math()函数
172
+ const mathRegex=/math\(([^)]+)\)/gi;
173
+ // 递归处理嵌套的math()函数
174
+ const processMath=str=>str.replace(mathRegex,(match,expression)=>evaluateMathExpression(expression,config));
175
+ // 递归处理,直到没有更多math()函数
176
+ let lastResult;do{lastResult=result;result=processMath(result)}while(result!==lastResult&&result.includes("math("));return result}
71
177
  /**
72
178
  * 解析并转换所有的 Lab 和 LCH 颜色(全局一次性处理)
73
179
  * @param {string} cssValue - CSS属性值
@@ -218,20 +324,21 @@ const rgb=preciseLabToRGB(L,a,b);if(alpha!==null){return`rgba(${rgb.r}, ${rgb.g}
218
324
  // 使用P3
219
325
  const p3=labToP3(L,a,b);if(alpha!==null){return`color(display-p3 ${p3.r.toFixed(4)} ${p3.g.toFixed(4)} ${p3.b.toFixed(4)} / ${alpha})`}return`color(display-p3 ${p3.r.toFixed(4)} ${p3.g.toFixed(4)} ${p3.b.toFixed(4)})`}}
220
326
  /**
221
- * 处理CSS值,根据配置转换LABLCH颜色
327
+ * 处理CSS值,根据配置转换LABLCH颜色和math()函数
222
328
  * @param {string} value - CSS属性值
223
329
  * @param {Object} config - 配置对象
224
330
  * @returns {string} 处理后的值
225
- */function processCSSValue(value,config){
226
- // 直接调用全局转换函数
227
- return convertAllLabLchColors(value,config)}
331
+ */function processCSSValue(value,config){let result=value;
332
+ // 1. 首先处理math()函数
333
+ if(config.enableMath){result=processMathFunctions(result,config)}
334
+ // 2. 然后处理LAB和LCH颜色
335
+ if(config.convertLabToRGB||config.convertLchToRGB){result=convertAllLabLchColors(result,config)}return result}
228
336
  // 私有方法:提取变量定义并移除(支持嵌套作用域)
229
- function extractVariablesAndCSS(cssText,config){const lines=cssText.split("\n");const globalVariables={};const selectorVariables=new Map;// 选择器 -> 变量映射
230
- let cssWithoutVars="";let currentSelector=null;let inSelectorBlock=false;let currentIndent=0;for(let line of lines){const trimmed=line.trim();
337
+ function extractVariablesAndCSS(cssText,config){const lines=cssText.split("\n");const globalVariables={};const selectorVariables=new Map;let cssWithoutVars="";let currentSelector=null;let inSelectorBlock=false;let currentIndent=0;for(let line of lines){const trimmed=line.trim();
231
338
  // 检查是否是变量定义
232
339
  const varMatch=trimmed.match(/^\$([a-zA-Z0-9_-]+)\s*:\s*(.+?);?$/);if(varMatch){const[,varName,varValue]=varMatch;
233
- // 处理变量值中的LAB/LCH颜色转换
234
- const processedValue=convertAllLabLchColors(replaceVariableUsesInValue(varValue.trim(),{...globalVariables,...currentSelector?selectorVariables.get(currentSelector)||{}:{}}),config);if(currentSelector){
340
+ // 处理变量值中的数学表达式和颜色转换
341
+ const processedValue=processCSSValue(replaceVariableUsesInValue(varValue.trim(),{...globalVariables,...currentSelector?selectorVariables.get(currentSelector)||{}:{}}),config);if(currentSelector){
235
342
  // 选择器内部的变量
236
343
  if(!selectorVariables.has(currentSelector)){selectorVariables.set(currentSelector,{})}selectorVariables.get(currentSelector)[varName]=processedValue}else{
237
344
  // 全局变量(:root 中)
@@ -263,8 +370,8 @@ const rule={selector:selector,properties:[],children:[],indentLevel:indentLevel}
263
370
  stack.push(rule);continue}
264
371
  // 处理属性行(不包含 { 或 } 的行)
265
372
  if(!trimmed.includes("{")&&!trimmed.includes("}")&&trimmed.includes(":")){if(stack.length>0){const currentRule=stack[stack.length-1];
266
- // 处理属性值中的LAB/LCH颜色
267
- const parsed=parseSingleLineCSS(trimmed);parsed.forEach(obj=>{const key=Object.keys(obj)[0];const value=convertAllLabLchColors(obj[key],config);currentRule.properties.push(`${key}: ${value}`)})}continue}}
373
+ // 处理属性值中的math()函数和颜色转换
374
+ const parsed=parseSingleLineCSS(trimmed);parsed.forEach(obj=>{const key=Object.keys(obj)[0];const value=processCSSValue(obj[key],config);currentRule.properties.push(`${key}: ${value}`)})}continue}}
268
375
  // 处理栈中剩余规则
269
376
  while(stack.length>0){const rule=stack.pop();if(stack.length===0){rootRules.push(rule)}else{const parent=stack[stack.length-1];if(!parent.children)parent.children=[];parent.children.push(rule)}}
270
377
  // 将规则树转换为CSS字符串
@@ -277,8 +384,8 @@ const isAt=rule.selector.startsWith("@");let fullSelector=(isParentAt?" ":"")+r
277
384
  if(fullSelector.includes("&")){fullSelector=fullSelector.replace(/&/g,parentSelector)}else if(fullSelector.trim().startsWith(":")){fullSelector=parentSelector+fullSelector}else{fullSelector=parentSelector+(isParentAt?"":" ")+fullSelector}}
278
385
  // 如果有属性,生成规则块
279
386
  if(rule.properties.length>0){result+=(isAt?"":fullSelector)+" {\n";for(const prop of rule.properties){
280
- // 再次处理属性值(确保LAB/LCH颜色被转换)
281
- const parsed=parseSingleLineCSS(prop);parsed.forEach(obj=>{const key=Object.keys(obj)[0];const value=convertAllLabLchColors(obj[key],config);result+=(isParentAt?" ":"")+` ${key}: ${value};\n`})}result+=isParentAt?" }\n":"}\n\n"}
387
+ // 再次处理属性值(确保math()函数和颜色被转换)
388
+ const parsed=parseSingleLineCSS(prop);parsed.forEach(obj=>{const key=Object.keys(obj)[0];const value=processCSSValue(obj[key],config);result+=(isParentAt?" ":"")+` ${key}: ${value};\n`})}result+=isParentAt?" }\n":"}\n\n"}
282
389
  // 递归处理子规则
283
390
  if(rule.children&&rule.children.length>0){result+=convertRulesToCSS(rule.children,config,fullSelector)}if(isParentAt){result+="}\n\n"}}return result.trim()+(isParentAt?"\n\n":"")}
284
391
  // 替换变量值中的变量引用
@@ -289,16 +396,16 @@ function replaceVariableUses(cssText,globalVariables,selectorVariables,config){l
289
396
  for(const[varName,varValue]of Object.entries(globalVariables)){const varRegex=new RegExp(`\\$${varName}(?![a-zA-Z0-9_-])`,"g");result=result.replace(varRegex,`var(--${varName})`)}
290
397
  // 然后处理选择器特定的变量
291
398
  result=result.replace(/\$([a-zA-Z0-9_-]+)/g,(match,varName)=>`var(--${varName})`);
292
- // 最后处理所有的LABLCH颜色(一次性全局处理)
293
- if(config.convertLabToRGB||config.convertLchToRGB){result=convertAllLabLchColors(result,config)}return result}
399
+ // 最后处理所有的LABLCH颜色和math()函数
400
+ result=processCSSValue(result,config);return result}
294
401
  // 生成根规则
295
- function generateRootRule(variables,config){if(Object.keys(variables).length===0){return""}const declarations=Object.entries(variables).map(([name,value])=>{const processedValue=convertAllLabLchColors(replaceVariableUsesInValue(value,variables),config);return` --${name}: ${processedValue};`}).join("\n");return`${config.rootSelector} {\n${declarations}\n}\n\n`}
402
+ function generateRootRule(variables,config){if(Object.keys(variables).length===0){return""}const declarations=Object.entries(variables).map(([name,value])=>{const processedValue=processCSSValue(replaceVariableUsesInValue(value,variables),config);return` --${name}: ${processedValue};`}).join("\n");return`${config.rootSelector} {\n${declarations}\n}\n\n`}
296
403
  // 处理选择器内部的变量
297
404
  function injectSelectorVariables(cssText,selectorVariables,config){let result="";const lines=cssText.split("\n");let currentSelector=null;for(let line of lines){const trimmed=line.trim();if(trimmed.endsWith("{")){currentSelector=trimmed.slice(0,-1).trim()}if(trimmed==="}"&&currentSelector){
298
405
  // 在选择器结束前插入变量声明
299
406
  if(selectorVariables.has(currentSelector)){const vars=selectorVariables.get(currentSelector);const indent=line.match(/^(\s*)/)[0];for(const[varName,varValue]of Object.entries(vars)){result+=indent+" --"+varName+": "+varValue+";\n"}}currentSelector=null}
300
- // 使用全局转换函数处理所有颜色
301
- if(config.convertLabToRGB||config.convertLchToRGB){result+=convertAllLabLchColors(line,config)+"\n"}else{result+=line+"\n"}}return result.trim()}
407
+ // 使用全局处理函数处理所有math()和颜色
408
+ result+=processCSSValue(line,config)+"\n"}return result.trim()}
302
409
  // 主转换函数
303
410
  function convert(cssText,customConfig={}){const config={...defaultConfig,...customConfig};
304
411
  // 1. 提取变量定义(区分全局和选择器局部)
@@ -324,11 +431,39 @@ if(!config.preserveOriginal){styleTag.remove()}else{styleTag.style.display="none
324
431
  // 初始化自动处理
325
432
  function apply(cssText,customConfig={}){const config={...defaultConfig,...customConfig};if(cssText){const converted=convert(cssText,config);const styleEl=document.createElement("style");styleEl.textContent=converted;document.head.appendChild(styleEl);return styleEl}else{if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",()=>{autoProcessStyleTags(config)})}else{autoProcessStyleTags(config)}}}
326
433
  // 返回公共 API
327
- const api={convert:convert,apply:apply,config:config,version:"1.8.0",
434
+ const api={convert:convert,apply:apply,config:config,version:"1.9.0",
328
435
  // 检测P3支持
329
436
  supportsP3:detectP3Support(),
330
437
  // 重新检测P3支持(如果配置变化)
331
438
  detectP3Support:detectP3Support,
439
+ // 数学计算工具方法
440
+ math:{
441
+ /**
442
+ * 计算数学表达式
443
+ * @param {string} expression - 数学表达式
444
+ * @returns {string} 计算结果
445
+ */
446
+ evaluate:function(expression){return evaluateMathExpression(expression,defaultConfig)},
447
+ /**
448
+ * 解析带单位的数值
449
+ * @param {string} value - 带单位的字符串
450
+ * @returns {Object} {value: number, unit: string}
451
+ */
452
+ parseUnit:parseValueWithUnit,
453
+ /**
454
+ * 四舍五入数字
455
+ * @param {number} num - 要四舍五入的数字
456
+ * @param {number} precision - 精度
457
+ * @returns {string} 四舍五入后的数字字符串
458
+ */
459
+ round:roundNumber,
460
+ /**
461
+ * 测试数学表达式
462
+ * @param {string} expression - 要测试的表达式
463
+ * @param {Object} testConfig - 测试配置
464
+ * @returns {Object} 测试结果
465
+ */
466
+ test:function(expression,testConfig={}){const config={...defaultConfig,...testConfig};try{const result=evaluateMathExpression(expression,config);return{success:true,expression:expression,result:result,parsed:parseValueWithUnit(result.replace(/^calc\(|\)$/g,""))}}catch(error){return{success:false,expression:expression,error:error.message}}}},
332
467
  // 颜色转换工具方法
333
468
  colorUtils:{labToRGB:preciseLabToRGB,lchToLab:lchToLab,lchToRGB:function(L,C,H){const lab=lchToLab(L,C,H);return preciseLabToRGB(lab.L,lab.a,lab.b)},labToP3:labToP3,lchToP3:function(L,C,H){const lab=lchToLab(L,C,H);return labToP3(lab.L,lab.a,lab.b)},parseHexLab:function(hexString){const match=hexString.match(/lab#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i);if(!match)return null;const L_hex=match[1];const A_hex=match[2];const B_hex=match[3];const L=parseInt(L_hex,16)/255*100;const A=(parseInt(A_hex,16)-128)*1.5;const B=(parseInt(B_hex,16)-128)*1.5;return preciseLabToRGB(L,A,B)},parseHexLch:function(hexString){const match=hexString.match(/lch#([0-9a-f]{2})([0-9a-f]{2})(\d{1,3})/i);if(!match)return null;const L_hex=match[1];const C_hex=match[2];const H_dec=match[3];const L=parseInt(L_hex,16)/255*100;const C=parseInt(C_hex,16)/255*150;const H=parseInt(H_dec)/100*360;const lab=lchToLab(L,C,H);return preciseLabToRGB(lab.L,lab.a,lab.b)},
334
469
  /**
@@ -351,5 +486,29 @@ parseColor:function(colorString){try{
351
486
  const labMatch=colorString.match(/lab\(\s*([\d.]+)(%?)\s+([\d.-]+)\s+([\d.-]+)(?:\s*\/\s*([\d.%]+))?\s*\)/i);if(labMatch){let L=parseFloat(labMatch[1]);const A=parseFloat(labMatch[3]);const B=parseFloat(labMatch[4]);const alpha=labMatch[5]?labMatch[5].includes("%")?parseFloat(labMatch[5])/100:parseFloat(labMatch[5]):null;const colorStr=generateColorString(L,A,B,defaultConfig,alpha);return{L:L,A:A,B:B,alpha:alpha,rgb:preciseLabToRGB(L,A,B),p3:labToP3(L,A,B),colorString:colorStr}}
352
487
  // 尝试解析lch()函数格式
353
488
  const lchMatch=colorString.match(/lch\(\s*([\d.]+)(%?)\s+([\d.]+)\s+([\d.]+)(deg)?(?:\s*\/\s*([\d.%]+))?\s*\)/i);if(lchMatch){let L=parseFloat(lchMatch[1]);const C=parseFloat(lchMatch[3]);let H=parseFloat(lchMatch[4]);const alpha=lchMatch[6]?lchMatch[6].includes("%")?parseFloat(lchMatch[6])/100:parseFloat(lchMatch[6]):null;const lab=lchToLab(L,C,H);const colorStr=generateColorString(lab.L,lab.a,lab.b,defaultConfig,alpha);return{L:L,C:C,H:H,alpha:alpha,lab:lab,rgb:preciseLabToRGB(lab.L,lab.a,lab.b),p3:labToP3(lab.L,lab.a,lab.b),colorString:colorStr}}return null}catch(error){console.warn("无法解析颜色:",colorString,error);return null}}}};
489
+ // 创建一个可调用的主函数
490
+ const styimat=function(...args){
491
+ // 检查是否是模板字符串调用(标签函数)
492
+ if(args.length>1||args[0]&&args[0].raw){
493
+ // 处理模板字符串
494
+ return handleTemplateTag(...args)}
495
+ // 获取第一个参数
496
+ const firstArg=args[0];
497
+ // 如果传入CSS文本,则编译并返回结果
498
+ if(typeof firstArg==="string"){return convert(firstArg,{...defaultConfig,...args[1]})}
499
+ // 如果传入配置对象,则应用配置
500
+ if(typeof firstArg==="object"&&firstArg!==null){defaultConfig={...defaultConfig,...firstArg};return styimat}
501
+ // 如果没有参数,执行自动处理
502
+ if(args.length===0){return apply()}return styimat};
503
+ // 处理模板字符串的函数
504
+ function handleTemplateTag(strings,...values){
505
+ // 拼接模板字符串
506
+ let cssText=strings[0];for(let i=0;i<values.length;i++){
507
+ // 处理插值(支持字符串、数字、函数等)
508
+ const value=values[i];let result="";if(typeof value==="function"){result=value()}else if(Array.isArray(value)){result=value.join(" ")}else{result=String(value!=null?value:"")}cssText+=result+strings[i+1]}
509
+ // 使用convert函数处理
510
+ return convert(cssText,defaultConfig)}
511
+ // 将API的所有方法复制到主函数上
512
+ Object.assign(styimat,api);Object.setPrototypeOf(styimat,Function.prototype);
354
513
  // 自动初始化
355
- if(typeof window!=="undefined"){apply();Object.defineProperty(window.HTMLElement.prototype,"cssVar",{get(){const element=this;return new Proxy({},{get(target,prop){const varName=prop.startsWith("$")?prop.slice(1):prop;return element.style.getPropertyValue(varName)},set(target,prop,value){const varName=prop.startsWith("$")?prop.slice(1):prop;element.style.setProperty(varName,value);return true}})}})}return api});
514
+ if(typeof window!=="undefined"){apply();Object.defineProperty(window.HTMLElement.prototype,"cssVar",{get(){const element=this;return new Proxy({},{get(target,prop){const varName=prop.startsWith("--")?prop:`--${prop}`;return element.style.getPropertyValue(varName)},set(target,prop,value){const varName=prop.startsWith("--")?prop:`--${prop}`;element.style.setProperty(varName,value);return true}})}})}return styimat});