jmgraph 3.2.24 → 3.2.26

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.
package/dist/jmgraph.js CHANGED
@@ -2714,31 +2714,63 @@ var jmGradient = /*#__PURE__*/function () {
2714
2714
  }
2715
2715
 
2716
2716
  if (!d) {
2717
- d = Math.min(location.width, location.height);
2718
- } //let offsetLine = 1;//渐变长度或半径
2719
- //处理百分比参数
2717
+ d = Math.min(location.width || 0, location.height || 0);
2718
+ }
2719
+
2720
+ if (d <= 0) {
2721
+ d = Math.max(bounds.width || 0, bounds.height || 0, 100);
2722
+ }
2720
2723
 
2724
+ var width = bounds.width || d;
2725
+ var height = bounds.height || d;
2721
2726
 
2722
2727
  if (_jmUtils.jmUtils.checkPercent(x1)) {
2723
- x1 = _jmUtils.jmUtils.percentToNumber(x1) * (bounds.width || d);
2728
+ x1 = _jmUtils.jmUtils.percentToNumber(x1) * width;
2729
+ } else if (typeof x1 === 'number' && x1 >= 0 && x1 <= 1) {
2730
+ x1 = x1 * width;
2731
+ } else if (typeof x1 === 'string') {
2732
+ var num = parseFloat(x1);
2733
+ if (!isNaN(num)) x1 = num;
2724
2734
  }
2725
2735
 
2726
2736
  if (_jmUtils.jmUtils.checkPercent(x2)) {
2727
- x2 = _jmUtils.jmUtils.percentToNumber(x2) * (bounds.width || d);
2737
+ x2 = _jmUtils.jmUtils.percentToNumber(x2) * width;
2738
+ } else if (typeof x2 === 'number' && x2 >= 0 && x2 <= 1) {
2739
+ x2 = x2 * width;
2740
+ } else if (typeof x2 === 'string') {
2741
+ var _num = parseFloat(x2);
2742
+
2743
+ if (!isNaN(_num)) x2 = _num;
2728
2744
  }
2729
2745
 
2730
2746
  if (_jmUtils.jmUtils.checkPercent(y1)) {
2731
- y1 = _jmUtils.jmUtils.percentToNumber(y1) * (bounds.height || d);
2747
+ y1 = _jmUtils.jmUtils.percentToNumber(y1) * height;
2748
+ } else if (typeof y1 === 'number' && y1 >= 0 && y1 <= 1) {
2749
+ y1 = y1 * height;
2750
+ } else if (typeof y1 === 'string') {
2751
+ var _num2 = parseFloat(y1);
2752
+
2753
+ if (!isNaN(_num2)) y1 = _num2;
2732
2754
  }
2733
2755
 
2734
2756
  if (_jmUtils.jmUtils.checkPercent(y2)) {
2735
- y2 = _jmUtils.jmUtils.percentToNumber(y2) * (bounds.height || d);
2757
+ y2 = _jmUtils.jmUtils.percentToNumber(y2) * height;
2758
+ } else if (typeof y2 === 'number' && y2 >= 0 && y2 <= 1) {
2759
+ y2 = y2 * height;
2760
+ } else if (typeof y2 === 'string') {
2761
+ var _num3 = parseFloat(y2);
2762
+
2763
+ if (!isNaN(_num3)) y2 = _num3;
2736
2764
  }
2737
2765
 
2738
- var sx1 = Number(x1) + bounds.left;
2739
- var sy1 = Number(y1) + bounds.top;
2740
- var sx2 = Number(x2) + bounds.left;
2741
- var sy2 = Number(y2) + bounds.top;
2766
+ x1 = Number(x1) || 0;
2767
+ y1 = Number(y1) || 0;
2768
+ x2 = Number(x2) || 0;
2769
+ y2 = Number(y2) || 0;
2770
+ var sx1 = x1 + (bounds.left || 0);
2771
+ var sy1 = y1 + (bounds.top || 0);
2772
+ var sx2 = x2 + (bounds.left || 0);
2773
+ var sy2 = y2 + (bounds.top || 0);
2742
2774
 
2743
2775
  if (this.type === 'linear') {
2744
2776
  if (control.mode === 'webgl' && control.webglControl) {
@@ -2752,25 +2784,41 @@ var jmGradient = /*#__PURE__*/function () {
2752
2784
  var r1 = this.r1 || 0;
2753
2785
  var r2 = this.r2;
2754
2786
 
2787
+ if (d <= 0) {
2788
+ d = Math.max(bounds.width || 0, bounds.height || 0, 1);
2789
+ }
2790
+
2755
2791
  if (_jmUtils.jmUtils.checkPercent(r1)) {
2756
2792
  r1 = _jmUtils.jmUtils.percentToNumber(r1);
2757
2793
  r1 = d * r1;
2794
+ } else if (typeof r1 === 'number' && r1 >= 0 && r1 <= 1) {
2795
+ r1 = r1 * d;
2796
+ } else if (typeof r1 === 'string') {
2797
+ r1 = parseFloat(r1) || 0;
2758
2798
  }
2759
2799
 
2760
2800
  if (_jmUtils.jmUtils.checkPercent(r2)) {
2761
2801
  r2 = _jmUtils.jmUtils.percentToNumber(r2);
2762
2802
  r2 = d * r2;
2803
+ } else if (typeof r2 === 'number' && r2 >= 0 && r2 <= 1) {
2804
+ r2 = r2 * d;
2805
+ } else if (typeof r2 === 'string') {
2806
+ r2 = parseFloat(r2);
2763
2807
  }
2764
2808
 
2809
+ if (r2 === undefined || r2 === null || isNaN(Number(r2)) || Number(r2) <= 0) {
2810
+ r2 = d / 2;
2811
+ }
2812
+
2813
+ r1 = Number(r1) || 0;
2814
+ r2 = Number(r2) || d / 2;
2815
+
2765
2816
  if (control.mode === 'webgl' && control.webglControl) {
2766
- // WebGL 着色器中 v_text_coord 是绝对坐标,需要传递绝对坐标
2767
2817
  gradient = control.webglControl.createRadialGradient(sx1, sy1, r1, sx2, sy2, r2, bounds);
2768
2818
  gradient.key = this.toString();
2769
- } //offsetLine = Math.abs(r2 - r1);//二圆半径差
2770
- else if (context.createRadialGradient) {
2819
+ } else if (context.createRadialGradient) {
2771
2820
  gradient = context.createRadialGradient(sx1, sy1, r1, sx2, sy2, r2);
2772
- } //小程序的接口特殊
2773
- else if (context.createCircularGradient) {
2821
+ } else if (context.createCircularGradient) {
2774
2822
  gradient = context.createCircularGradient(sx1, sy1, r2);
2775
2823
  }
2776
2824
  } //颜色渐变
@@ -2791,74 +2839,690 @@ var jmGradient = /*#__PURE__*/function () {
2791
2839
  return gradient;
2792
2840
  }
2793
2841
  /**
2794
- * 变换为字条串格式
2795
- * linear-gradient(x1 y1 x2 y2, color1 step, color2 step, ...); //radial-gradient(x1 y1 r1 x2 y2 r2, color1 step,color2 step, ...);
2796
- * linear-gradient线性渐变,x1 y1表示起点,x2 y2表示结束点,color表颜色,step为当前颜色偏移
2797
- * radial-gradient径向渐变,x1 y1 r1分别表示内圆中心和半径,x2 y2 r2为结束圆 中心和半径,颜色例似线性渐变 step为0-1之间
2842
+ * 解析渐变字符串
2843
+ * 支持的格式:
2844
+ * - linear-gradient(180deg, #8b5cf6 0%, #6366f1 50%, #4f46e5 100%)
2845
+ * - linear-gradient(to top, red, blue)
2846
+ * - linear-gradient(to right bottom, red, blue)
2847
+ * - linear-gradient(45deg, red, blue)
2848
+ * - linear-gradient(0.5turn, red, blue)
2849
+ * - radial-gradient(circle, red, blue)
2850
+ * - radial-gradient(ellipse at top, red, blue)
2798
2851
  *
2799
2852
  * @method fromString
2800
2853
  * @for jmGradient
2801
- * @return {string}
2854
+ * @param {string} s 渐变字符串
2802
2855
  */
2803
2856
 
2804
2857
  }, {
2805
2858
  key: "fromString",
2806
2859
  value: function fromString(s) {
2807
- if (!s) return;
2808
- var ms = s.match(/(linear|radial)-gradient\s*\(\s*([^,]+)\s*,\s*((.|\s)+)\)/i);
2809
- if (!ms || ms.length < 3) return;
2810
- this.type = ms[1].toLowerCase();
2860
+ if (!s) {
2861
+ console.warn('jmGradient: 渐变字符串为空');
2862
+ return;
2863
+ }
2811
2864
 
2812
- var ps = _jmUtils.jmUtils.trim(ms[2]).split(/\s+/); //线性渐变
2865
+ var gradientMatch = s.match(/(linear|radial)-gradient\s*\(\s*(.+)\)/i);
2813
2866
 
2867
+ if (!gradientMatch || gradientMatch.length < 3) {
2868
+ console.warn('jmGradient: 无效的渐变字符串格式: "' + s + '"');
2869
+ return;
2870
+ }
2814
2871
 
2815
- if (this.type == 'linear') {
2816
- if (ps.length <= 2) {
2817
- this.x2 = ps[0];
2818
- this.y2 = ps[1] || 0;
2819
- } else {
2820
- this.x1 = ps[0];
2821
- this.y1 = ps[1];
2822
- this.x2 = ps[2];
2823
- this.y2 = ps[3];
2824
- }
2825
- } //径向渐变
2826
- else {
2827
- if (ps.length <= 3) {
2828
- this.x2 = ps[0];
2829
- this.y2 = ps[1] || 0;
2830
- this.r2 = ps[2] || 0;
2831
- } else {
2832
- this.x1 = ps[0];
2833
- this.y1 = ps[1];
2834
- this.r1 = ps[2];
2835
- this.x2 = ps[3];
2836
- this.y2 = ps[3];
2837
- this.r2 = ps[3];
2872
+ var type = gradientMatch[1].toLowerCase();
2873
+
2874
+ if (type !== 'linear' && type !== 'radial') {
2875
+ console.warn('jmGradient: 不支持的渐变类型 "' + type + '",仅支持 linear 和 radial');
2876
+ return;
2877
+ }
2878
+
2879
+ this.type = type;
2880
+
2881
+ var content = _jmUtils.jmUtils.trim(gradientMatch[2]);
2882
+
2883
+ var splitIndex = this._findSplitIndex(content);
2884
+
2885
+ if (splitIndex < 0) {
2886
+ console.warn('jmGradient: 无法解析渐变内容: "' + content + '"');
2887
+ return;
2888
+ }
2889
+
2890
+ var params = content.substring(0, splitIndex).trim();
2891
+ var colorPart = content.substring(splitIndex + 1).trim();
2892
+
2893
+ if (!colorPart) {
2894
+ console.warn('jmGradient: 未找到颜色停止点');
2895
+ return;
2896
+ }
2897
+
2898
+ if (this.type === 'linear') {
2899
+ this._parseLinearParams(params);
2900
+ } else {
2901
+ this._parseRadialParams(params);
2902
+ }
2903
+
2904
+ var colorCount = this._parseColorStops(colorPart);
2905
+
2906
+ if (colorCount === 0) {
2907
+ console.warn('jmGradient: 未找到有效的颜色停止点: "' + colorPart + '"');
2908
+ } else if (colorCount < 2) {
2909
+ console.warn('jmGradient: 颜色停止点至少需要2个,当前只有 ' + colorCount + ' 个');
2910
+ }
2911
+ }
2912
+ /**
2913
+ * 找到参数和颜色的分割位置(第一个不在括号内的逗号)
2914
+ * @param {string} content 内容字符串
2915
+ * @returns {number} 分割位置索引
2916
+ */
2917
+
2918
+ }, {
2919
+ key: "_findSplitIndex",
2920
+ value: function _findSplitIndex(content) {
2921
+ var depth = 0;
2922
+
2923
+ for (var i = 0; i < content.length; i++) {
2924
+ var _char = content[i];
2925
+
2926
+ if (_char === '(') {
2927
+ depth++;
2928
+ } else if (_char === ')') {
2929
+ depth--;
2930
+ } else if (_char === ',' && depth === 0) {
2931
+ return i;
2838
2932
  }
2839
- } //解析颜色偏移
2840
- //color step
2933
+ }
2841
2934
 
2935
+ return -1;
2936
+ }
2937
+ /**
2938
+ * 验证渐变配置是否有效
2939
+ * @returns {boolean} 是否有效
2940
+ */
2842
2941
 
2843
- var pars = ms[3].match(/((rgb(a)?\s*\([\d,\.\s]+\))|(#[a-zA-Z\d]+))\s+([\d\.]+)/ig);
2942
+ }, {
2943
+ key: "isValid",
2944
+ value: function isValid() {
2945
+ if (!this.type) return false;
2844
2946
 
2845
- if (pars && pars.length) {
2846
- for (var i = 0; i < pars.length; i++) {
2847
- var par = _jmUtils.jmUtils.trim(pars[i]);
2947
+ if (this.type === 'linear') {
2948
+ return typeof this.x1 !== 'undefined' || typeof this.x2 !== 'undefined' || typeof this._angle !== 'undefined';
2949
+ }
2848
2950
 
2849
- var spindex = par.lastIndexOf(' ');
2951
+ if (this.type === 'radial') {
2952
+ return this.stops && this.stops.length >= 2;
2953
+ }
2954
+
2955
+ return false;
2956
+ }
2957
+ /**
2958
+ * 解析线性渐变参数
2959
+ * @param {string} params 参数字符串
2960
+ */
2961
+
2962
+ }, {
2963
+ key: "_parseLinearParams",
2964
+ value: function _parseLinearParams(params) {
2965
+ var trimmed = _jmUtils.jmUtils.trim(params);
2850
2966
 
2851
- if (spindex > -1) {
2852
- var offset = Number(par.substr(spindex + 1));
2967
+ if (trimmed.startsWith('to ')) {
2968
+ var direction = trimmed.substring(3).toLowerCase().trim();
2853
2969
 
2854
- var color = _jmUtils.jmUtils.trim(par.substr(0, spindex));
2970
+ var dir = this._directionToAngle(direction);
2971
+
2972
+ this._angle = dir.angle;
2973
+ this.x1 = dir.x1;
2974
+ this.y1 = dir.y1;
2975
+ this.x2 = dir.x2;
2976
+ this.y2 = dir.y2;
2977
+ } else if (this._hasAngleUnit(trimmed)) {
2978
+ var angle = this._parseAngle(trimmed);
2979
+
2980
+ this._angle = angle;
2981
+
2982
+ var coords = this._angleToCoords(angle);
2983
+
2984
+ this.x1 = coords.x1;
2985
+ this.y1 = coords.y1;
2986
+ this.x2 = coords.x2;
2987
+ this.y2 = coords.y2;
2988
+ } else if (trimmed.startsWith('at ')) {
2989
+ var radialMatch = trimmed.match(/at\s+(.+)/i);
2990
+
2991
+ if (radialMatch) {
2992
+ this._parseRadialParams(trimmed);
2993
+ }
2994
+ } else {
2995
+ var parts = trimmed.split(/\s+/);
2996
+
2997
+ if (parts.length >= 4) {
2998
+ this.x1 = parts[0];
2999
+ this.y1 = parts[1];
3000
+ this.x2 = parts[2];
3001
+ this.y2 = parts[3];
3002
+ } else if (parts.length === 2) {
3003
+ this.x1 = 0;
3004
+ this.y1 = 0;
3005
+ this.x2 = parts[0];
3006
+ this.y2 = parts[1];
3007
+ }
3008
+ }
3009
+ }
3010
+ /**
3011
+ * 解析径向渐变参数
3012
+ * @param {string} params 参数字符串
3013
+ */
3014
+
3015
+ }, {
3016
+ key: "_parseRadialParams",
3017
+ value: function _parseRadialParams(params) {
3018
+ var trimmed = _jmUtils.jmUtils.trim(params);
3019
+
3020
+ this.shape = 'ellipse';
3021
+ this.position = {
3022
+ x: '50%',
3023
+ y: '50%'
3024
+ };
3025
+ var atMatch = trimmed.match(/^(.+?)\s+at\s+(.+)$/i);
3026
+
3027
+ if (atMatch) {
3028
+ var shapePart = _jmUtils.jmUtils.trim(atMatch[1]);
3029
+
3030
+ var posPart = _jmUtils.jmUtils.trim(atMatch[2]);
3031
+
3032
+ this._parseRadialShape(shapePart);
3033
+
3034
+ this._parseRadialPosition(posPart);
3035
+ } else if (trimmed.startsWith('circle') || trimmed.startsWith('ellipse')) {
3036
+ this._parseRadialShape(trimmed);
3037
+
3038
+ this.x1 = '50%';
3039
+ this.y1 = '50%';
3040
+ this.x2 = '50%';
3041
+ this.y2 = '50%';
3042
+ } else {
3043
+ var parts = trimmed.split(/\s+/);
3044
+
3045
+ if (parts.length >= 3) {
3046
+ this.x1 = parts[0];
3047
+ this.y1 = parts[1];
3048
+ this.r1 = parts[2];
3049
+ }
3050
+
3051
+ if (parts.length >= 6) {
3052
+ this.x2 = parts[3];
3053
+ this.y2 = parts[4];
3054
+ this.r2 = parts[5];
3055
+ }
3056
+ }
3057
+
3058
+ if (this.x1 === undefined && this.y1 === undefined) {
3059
+ this.x1 = '50%';
3060
+ this.y1 = '50%';
3061
+ }
3062
+
3063
+ if (this.x2 === undefined && this.y2 === undefined) {
3064
+ this.x2 = '50%';
3065
+ this.y2 = '50%';
3066
+ }
3067
+
3068
+ if (this.r2 === undefined) {
3069
+ this.r2 = '50%';
3070
+ }
3071
+ }
3072
+ /**
3073
+ * 解析径向渐变形状
3074
+ * @param {string} shapePart 形状描述
3075
+ */
3076
+
3077
+ }, {
3078
+ key: "_parseRadialShape",
3079
+ value: function _parseRadialShape(shapePart) {
3080
+ if (shapePart.startsWith('circle')) {
3081
+ this.shape = 'circle';
3082
+ var sizeMatch = shapePart.match(/circle\s*\(\s*([^)]+)\s*\)/i);
3083
+
3084
+ if (sizeMatch) {
3085
+ this.r2 = _jmUtils.jmUtils.trim(sizeMatch[1]);
3086
+ }
3087
+ } else if (shapePart.startsWith('ellipse')) {
3088
+ this.shape = 'ellipse';
3089
+
3090
+ var _sizeMatch = shapePart.match(/ellipse\s*\(\s*([^)]+)\s*\)/i);
3091
+
3092
+ if (_sizeMatch) {
3093
+ var sizes = _jmUtils.jmUtils.trim(_sizeMatch[1]).split(/\s+/);
3094
+
3095
+ if (sizes.length >= 2) {
3096
+ this.rx = sizes[0];
3097
+ this.ry = sizes[1];
3098
+ }
3099
+ }
3100
+ }
3101
+ }
3102
+ /**
3103
+ * 解析径向渐变位置
3104
+ * @param {string} posPart 位置描述
3105
+ */
3106
+
3107
+ }, {
3108
+ key: "_parseRadialPosition",
3109
+ value: function _parseRadialPosition(posPart) {
3110
+ var parts = posPart.split(/\s+/);
3111
+
3112
+ if (parts.length >= 2) {
3113
+ this.x1 = parts[0];
3114
+ this.y1 = parts[1];
3115
+ this.x2 = parts[0];
3116
+ this.y2 = parts[1];
3117
+ }
3118
+ }
3119
+ /**
3120
+ * 解析颜色停止点
3121
+ * @param {string} colorPart 颜色部分字符串
3122
+ * @returns {number} 成功解析的颜色数量
3123
+ */
3124
+
3125
+ }, {
3126
+ key: "_parseColorStops",
3127
+ value: function _parseColorStops(colorPart) {
3128
+ if (!colorPart) {
3129
+ return 0;
3130
+ }
2855
3131
 
2856
- if (!isNaN(offset) && color) {
2857
- this.addStop(offset, color);
3132
+ var stops = this._splitColorStops(colorPart);
3133
+
3134
+ var lastOffset = -1;
3135
+ var colorCount = 0;
3136
+
3137
+ for (var i = 0; i < stops.length; i++) {
3138
+ var stop = _jmUtils.jmUtils.trim(stops[i]);
3139
+
3140
+ if (!stop) continue;
3141
+
3142
+ var parsed = this._parseSingleColorStop(stop);
3143
+
3144
+ if (!parsed) {
3145
+ continue;
3146
+ }
3147
+
3148
+ var color = parsed.color,
3149
+ offset = parsed.offset;
3150
+
3151
+ if (color === 'transparent') {
3152
+ color = 'rgba(0,0,0,0)';
3153
+ }
3154
+
3155
+ if (!this._isValidColor(color)) {
3156
+ console.warn('jmGradient: 无效的颜色格式 "' + color + '"');
3157
+ continue;
3158
+ }
3159
+
3160
+ var normalizedOffset = this._normalizeOffset(offset);
3161
+
3162
+ var finalOffset = normalizedOffset;
3163
+
3164
+ if (finalOffset === null) {
3165
+ if (i === 0) {
3166
+ finalOffset = 0;
3167
+ } else if (i === stops.length - 1) {
3168
+ finalOffset = 1;
3169
+ } else {
3170
+ var nextOffset = this._findNextOffset(stops, i);
3171
+
3172
+ if (nextOffset !== null) {
3173
+ finalOffset = (lastOffset + nextOffset) / 2;
3174
+ } else {
3175
+ finalOffset = Math.min(1, lastOffset + (1 - lastOffset) / (stops.length - i));
2858
3176
  }
2859
3177
  }
2860
3178
  }
3179
+
3180
+ if (finalOffset !== null) {
3181
+ if (finalOffset < 0 || finalOffset > 1) {
3182
+ console.warn('jmGradient: 颜色偏移量 ' + finalOffset + ' 超出有效范围 [0, 1],将调整为有效范围');
3183
+ finalOffset = Math.max(0, Math.min(1, finalOffset));
3184
+ }
3185
+
3186
+ lastOffset = finalOffset;
3187
+ this.addStop(finalOffset, color);
3188
+ colorCount++;
3189
+ }
3190
+ }
3191
+
3192
+ return colorCount;
3193
+ }
3194
+ /**
3195
+ * 分割颜色停止点字符串
3196
+ * @param {string} colorPart 颜色部分字符串
3197
+ * @returns {string[]} 颜色停止点数组
3198
+ */
3199
+
3200
+ }, {
3201
+ key: "_splitColorStops",
3202
+ value: function _splitColorStops(colorPart) {
3203
+ var stops = [];
3204
+ var depth = 0;
3205
+ var current = '';
3206
+
3207
+ for (var i = 0; i < colorPart.length; i++) {
3208
+ var _char2 = colorPart[i];
3209
+
3210
+ if (_char2 === '(') {
3211
+ depth++;
3212
+ current += _char2;
3213
+ } else if (_char2 === ')') {
3214
+ depth--;
3215
+ current += _char2;
3216
+ } else if (_char2 === ',' && depth === 0) {
3217
+ stops.push(current.trim());
3218
+ current = '';
3219
+ } else {
3220
+ current += _char2;
3221
+ }
3222
+ }
3223
+
3224
+ if (current.trim()) {
3225
+ stops.push(current.trim());
3226
+ }
3227
+
3228
+ return stops;
3229
+ }
3230
+ /**
3231
+ * 解析单个颜色停止点
3232
+ * @param {string} stop 单个颜色停止点字符串
3233
+ * @returns {object|null} {color, offset} 或 null
3234
+ */
3235
+
3236
+ }, {
3237
+ key: "_parseSingleColorStop",
3238
+ value: function _parseSingleColorStop(stop) {
3239
+ var hexMatch = stop.match(/^(#[a-fA-F0-9]{3,8})\s*(\d+(?:\.\d+)?%?)?$/i);
3240
+
3241
+ if (hexMatch) {
3242
+ return {
3243
+ color: hexMatch[1],
3244
+ offset: hexMatch[2] || null
3245
+ };
3246
+ }
3247
+
3248
+ var rgbaMatch = stop.match(/^(rgba?\s*\([^)]+\))\s*(\d+(?:\.\d+)?%?)?$/i);
3249
+
3250
+ if (rgbaMatch) {
3251
+ return {
3252
+ color: rgbaMatch[1],
3253
+ offset: rgbaMatch[2] || null
3254
+ };
3255
+ }
3256
+
3257
+ var hslaMatch = stop.match(/^(hsla?\s*\([^)]+\))\s*(\d+(?:\.\d+)?%?)?$/i);
3258
+
3259
+ if (hslaMatch) {
3260
+ return {
3261
+ color: hslaMatch[1],
3262
+ offset: hslaMatch[2] || null
3263
+ };
3264
+ }
3265
+
3266
+ var namedMatch = stop.match(/^([a-zA-Z]+)\s*(\d+(?:\.\d+)?%?)?$/i);
3267
+
3268
+ if (namedMatch && this._isValidColor(namedMatch[1])) {
3269
+ return {
3270
+ color: namedMatch[1],
3271
+ offset: namedMatch[2] || null
3272
+ };
3273
+ }
3274
+
3275
+ return null;
3276
+ }
3277
+ /**
3278
+ * 查找下一个有偏移量的颜色停止点
3279
+ * @param {string[]} stops 颜色停止点数组
3280
+ * @param {number} currentIndex 当前索引
3281
+ * @returns {number|null} 下一个偏移量或null
3282
+ */
3283
+
3284
+ }, {
3285
+ key: "_findNextOffset",
3286
+ value: function _findNextOffset(stops, currentIndex) {
3287
+ for (var i = currentIndex + 1; i < stops.length; i++) {
3288
+ var parsed = this._parseSingleColorStop(_jmUtils.jmUtils.trim(stops[i]));
3289
+
3290
+ if (parsed && parsed.offset) {
3291
+ return this._normalizeOffset(parsed.offset);
3292
+ }
3293
+ }
3294
+
3295
+ return null;
3296
+ }
3297
+ /**
3298
+ * 验证颜色格式是否有效
3299
+ * @param {string} color 颜色字符串
3300
+ * @returns {boolean} 是否有效
3301
+ */
3302
+
3303
+ }, {
3304
+ key: "_isValidColor",
3305
+ value: function _isValidColor(color) {
3306
+ if (!color) return false;
3307
+ var hexPattern = /^#([a-fA-F0-9]{3,8})$/;
3308
+ if (hexPattern.test(color)) return true;
3309
+ var rgbPattern = /^rgba?\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*[\d.]+\s*)?\)$/i;
3310
+ if (rgbPattern.test(color)) return true;
3311
+ var hslPattern = /^hsla?\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})%?\s*,\s*(\d{1,3})%?\s*(,\s*[\d.]+\s*)?\)$/i;
3312
+ if (hslPattern.test(color)) return true;
3313
+ var namedColors = ['red', 'blue', 'green', 'yellow', 'orange', 'purple', 'pink', 'white', 'black', 'cyan', 'magenta', 'gray', 'grey', 'brown', 'navy', 'teal', 'olive', 'maroon', 'silver', 'lime', 'aqua', 'fuchsia', 'violet', 'indigo', 'gold', 'silver', 'transparent'];
3314
+ if (namedColors.includes(color.toLowerCase())) return true;
3315
+ return false;
3316
+ }
3317
+ /**
3318
+ * 标准化偏移值
3319
+ * @param {string} offset 偏移字符串
3320
+ * @returns {number|null} 0-1之间的数值或null
3321
+ */
3322
+
3323
+ }, {
3324
+ key: "_normalizeOffset",
3325
+ value: function _normalizeOffset(offset) {
3326
+ if (!offset) return null;
3327
+ offset = _jmUtils.jmUtils.trim(offset);
3328
+
3329
+ if (offset.endsWith('%')) {
3330
+ return parseFloat(offset) / 100;
3331
+ }
3332
+
3333
+ var num = parseFloat(offset);
3334
+ if (isNaN(num)) return null;
3335
+
3336
+ if (num > 1) {
3337
+ return num / 100;
3338
+ }
3339
+
3340
+ return num;
3341
+ }
3342
+ /**
3343
+ * 检查字符串是否包含角度单位
3344
+ * @param {string} str 待检查字符串
3345
+ * @returns {boolean}
3346
+ */
3347
+
3348
+ }, {
3349
+ key: "_hasAngleUnit",
3350
+ value: function _hasAngleUnit(str) {
3351
+ return /^-?\d+(\.\d+)?\s*(deg|rad|grad|turn)$/i.test(str);
3352
+ }
3353
+ /**
3354
+ * 解析角度值
3355
+ * @param {string} angleStr 角度字符串
3356
+ * @returns {number} 弧度值
3357
+ */
3358
+
3359
+ }, {
3360
+ key: "_parseAngle",
3361
+ value: function _parseAngle(angleStr) {
3362
+ angleStr = _jmUtils.jmUtils.trim(angleStr);
3363
+ var match = angleStr.match(/^(-?\d+(\.\d+)?)\s*(deg|rad|grad|turn)?$/i);
3364
+ if (!match) return 0;
3365
+ var value = parseFloat(match[1]);
3366
+ var unit = (match[3] || 'deg').toLowerCase();
3367
+
3368
+ switch (unit) {
3369
+ case 'deg':
3370
+ return value * Math.PI / 180;
3371
+
3372
+ case 'rad':
3373
+ return value;
3374
+
3375
+ case 'grad':
3376
+ return value * Math.PI / 200;
3377
+
3378
+ case 'turn':
3379
+ return value * 2 * Math.PI;
3380
+
3381
+ default:
3382
+ return value * Math.PI / 180;
3383
+ }
3384
+ }
3385
+ /**
3386
+ * 将角度转换为起点和终点坐标
3387
+ * @param {number} angle 弧度值
3388
+ * @returns {object} 坐标对象
3389
+ */
3390
+
3391
+ }, {
3392
+ key: "_angleToCoords",
3393
+ value: function _angleToCoords(angle) {
3394
+ var x = Math.cos(angle);
3395
+ var y = -Math.sin(angle);
3396
+ return {
3397
+ x1: Math.round((0.5 - x * 0.5) * 1000) / 1000,
3398
+ y1: Math.round((0.5 + y * 0.5) * 1000) / 1000,
3399
+ x2: Math.round((0.5 + x * 0.5) * 1000) / 1000,
3400
+ y2: Math.round((0.5 - y * 0.5) * 1000) / 1000
3401
+ };
3402
+ }
3403
+ /**
3404
+ * 将方向关键词转换为角度和坐标
3405
+ * @param {string} direction 方向描述
3406
+ * @returns {object} 包含angle和坐标的对象
3407
+ */
3408
+
3409
+ }, {
3410
+ key: "_directionToAngle",
3411
+ value: function _directionToAngle(direction) {
3412
+ var directions = {
3413
+ 'to top': {
3414
+ angle: 0,
3415
+ x1: '50%',
3416
+ y1: '100%',
3417
+ x2: '50%',
3418
+ y2: '0%'
3419
+ },
3420
+ 'to bottom': {
3421
+ angle: Math.PI,
3422
+ x1: '50%',
3423
+ y1: '0%',
3424
+ x2: '50%',
3425
+ y2: '100%'
3426
+ },
3427
+ 'to left': {
3428
+ angle: -Math.PI / 2,
3429
+ x1: '100%',
3430
+ y1: '50%',
3431
+ x2: '0%',
3432
+ y2: '50%'
3433
+ },
3434
+ 'to right': {
3435
+ angle: Math.PI / 2,
3436
+ x1: '0%',
3437
+ y1: '50%',
3438
+ x2: '100%',
3439
+ y2: '50%'
3440
+ },
3441
+ 'to top left': {
3442
+ angle: -Math.PI * 3 / 4,
3443
+ x1: '100%',
3444
+ y1: '100%',
3445
+ x2: '0%',
3446
+ y2: '0%'
3447
+ },
3448
+ 'to top right': {
3449
+ angle: -Math.PI / 4,
3450
+ x1: '0%',
3451
+ y1: '100%',
3452
+ x2: '100%',
3453
+ y2: '0%'
3454
+ },
3455
+ 'to bottom left': {
3456
+ angle: Math.PI * 3 / 4,
3457
+ x1: '100%',
3458
+ y1: '0%',
3459
+ x2: '0%',
3460
+ y2: '100%'
3461
+ },
3462
+ 'to bottom right': {
3463
+ angle: Math.PI / 4,
3464
+ x1: '0%',
3465
+ y1: '0%',
3466
+ x2: '100%',
3467
+ y2: '100%'
3468
+ },
3469
+ 'top': {
3470
+ angle: 0,
3471
+ x1: '50%',
3472
+ y1: '100%',
3473
+ x2: '50%',
3474
+ y2: '0%'
3475
+ },
3476
+ 'bottom': {
3477
+ angle: Math.PI,
3478
+ x1: '50%',
3479
+ y1: '0%',
3480
+ x2: '50%',
3481
+ y2: '100%'
3482
+ },
3483
+ 'left': {
3484
+ angle: -Math.PI / 2,
3485
+ x1: '100%',
3486
+ y1: '50%',
3487
+ x2: '0%',
3488
+ y2: '50%'
3489
+ },
3490
+ 'right': {
3491
+ angle: Math.PI / 2,
3492
+ x1: '0%',
3493
+ y1: '50%',
3494
+ x2: '100%',
3495
+ y2: '50%'
3496
+ }
3497
+ };
3498
+ var dir = directions[direction];
3499
+
3500
+ if (dir) {
3501
+ return dir;
3502
+ }
3503
+
3504
+ var keywordMatch = direction.match(/to\s+(top|bottom|left|right)/i);
3505
+
3506
+ if (keywordMatch) {
3507
+ var mainDir = keywordMatch[1].toLowerCase();
3508
+ var secDir = direction.replace(keywordMatch[0], '').trim();
3509
+
3510
+ if (secDir) {
3511
+ var combined = "to ".concat(mainDir, " ").concat(secDir);
3512
+
3513
+ if (directions[combined]) {
3514
+ return directions[combined];
3515
+ }
3516
+ }
2861
3517
  }
3518
+
3519
+ return {
3520
+ angle: 0,
3521
+ x1: '50%',
3522
+ y1: '100%',
3523
+ x2: '50%',
3524
+ y2: '0%'
3525
+ };
2862
3526
  }
2863
3527
  /**
2864
3528
  * 转换为渐变的字符串表达