@ray-js/lamp-circle-picker 1.0.11-beta-2 → 1.0.11-beta-4

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.
@@ -79,10 +79,15 @@ Component({
79
79
  canvasId
80
80
  } = this.data;
81
81
  this.initCanvas();
82
- setTimeout(() => {
82
+ this.timer = setTimeout(() => {
83
+ var _this$render;
83
84
  this.initCanvas();
84
- this.render.checkIsRender(canvasId);
85
+ (_this$render = this.render) === null || _this$render === void 0 || _this$render.checkIsRender(canvasId);
85
86
  }, 300);
87
+ },
88
+ detached() {
89
+ clearTimeout(this.timer);
90
+ this.timer = null;
86
91
  }
87
92
  },
88
93
  methods: {
@@ -104,6 +109,10 @@ Component({
104
109
  touchCircleStrokeStyle = '',
105
110
  touchCircleLineWidth = 0
106
111
  } = this.data;
112
+ // 防止重复渲染
113
+ if (this.lastValue === value) {
114
+ return;
115
+ }
107
116
  canvasId && this.render.renderAnnulusColor(canvasId, radius, innerRingRadius, value, {
108
117
  useEventChannel,
109
118
  eventChannelName,
@@ -121,15 +130,15 @@ Component({
121
130
  value !== undefined && this._updatePosByRgb(value);
122
131
  },
123
132
  _updatePosByRgb(value) {
124
- var _this$render;
133
+ var _this$render2;
125
134
  if (value === undefined) {
126
135
  return;
127
136
  }
128
- (_this$render = this.render) === null || _this$render === void 0 || _this$render._getAnglePositionByValue(value);
137
+ (_this$render2 = this.render) === null || _this$render2 === void 0 || _this$render2._getAnglePositionByValue(value);
129
138
  },
130
139
  _getRgb(x, y) {
131
- var _this$render2;
132
- (_this$render2 = this.render) === null || _this$render2 === void 0 || _this$render2.getAnnulusImageData(x, y);
140
+ var _this$render3;
141
+ (_this$render3 = this.render) === null || _this$render3 === void 0 || _this$render3.getAnnulusImageData(x, y);
133
142
  },
134
143
  _getAnnulusImageData(dataRes) {
135
144
  const {
@@ -1,9 +1,326 @@
1
1
  // commonColor.rjs
2
+ const scale = 2;
3
+
4
+ function reverseColorStops(colorStops) {
5
+ if (!Array.isArray(colorStops)) {
6
+ console.error('【ray-circle-picker=> Input parameter must be an array.');
7
+ return [];
8
+ }
9
+
10
+ const _reversedColorStops = colorStops.map(stop => ({
11
+ offset: 1 - stop.offset,
12
+ color: stop.color,
13
+ }));
14
+
15
+ // 由于反转 offset 后,需要根据新的 offset 重新排序
16
+ _reversedColorStops.sort((a, b) => a.offset - b.offset);
17
+
18
+ return _reversedColorStops;
19
+ }
20
+
2
21
  export default Render({
3
22
  rectContext: null,
4
23
  touchCircleStrokeStyle: null,
5
24
  touchCircleLineWidth: null,
6
25
 
26
+ // 判断是否是 hex color
27
+ isHexColor(color) {
28
+ const hex = /^#([0-9a-fA-F]{6})$/;
29
+ return hex.test(color);
30
+ },
31
+ // 判断是否是 rgb color
32
+ isRgbColor(color) {
33
+ const rgb = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/;
34
+ return rgb.test(color);
35
+ },
36
+
37
+ // hex color to rgb
38
+ hexToRgb(hex) {
39
+ const rgb = /^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/;
40
+ const match = hex.match(rgb);
41
+ if (!match) {
42
+ console.warn(hex, '【ray-circle-picker=> Invalid hex color');
43
+ return null;
44
+ }
45
+ return {
46
+ r: parseInt(match[1], 16),
47
+ g: parseInt(match[2], 16),
48
+ b: parseInt(match[3], 16),
49
+ };
50
+ },
51
+ rgbStringToRgb(rgbString) {
52
+ const rgb = rgbString.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
53
+ return rgb ? { r: parseInt(rgb[1]), g: parseInt(rgb[2]), b: parseInt(rgb[3]) } : null;
54
+ },
55
+
56
+
57
+ getGradientColors(colorStops, steps) {
58
+ if (
59
+ !Array.isArray(colorStops) ||
60
+ colorStops.length < 2 ||
61
+ typeof steps !== 'number' ||
62
+ steps <= 0
63
+ ) {
64
+ console.error(
65
+ '【ray-circle-picker=> Invalid input parameters. Color stops array must have at least two colors, and steps must be a positive number.'
66
+ );
67
+ return [];
68
+ }
69
+
70
+ // 检查颜色停止点格式(简化,只检查 offset)
71
+ for (const stop of colorStops) {
72
+ if (
73
+ typeof stop !== 'object' ||
74
+ typeof stop.offset !== 'number' ||
75
+ isNaN(stop.offset) ||
76
+ stop.offset < 0 ||
77
+ stop.offset > 1
78
+ ) {
79
+ console.warn(
80
+ '【ray-circle-picker=> Invalid color stop format. Expected { offset: number (0-1), color: "rgb(r, g, b)" }.'
81
+ );
82
+ return [];
83
+ }
84
+ }
85
+
86
+ colorStops.sort((a, b) => a.offset - b.offset);
87
+
88
+ const gradientColors = [];
89
+ for (let i = 0; i <= steps; i++) {
90
+ const progress = i / steps;
91
+
92
+ let startStop, endStop;
93
+ for (let j = 0; j < colorStops.length - 1; j++) {
94
+ if (progress >= colorStops[j].offset && progress <= colorStops[j + 1].offset) {
95
+ startStop = colorStops[j];
96
+ endStop = colorStops[j + 1];
97
+ break;
98
+ }
99
+ }
100
+
101
+ if (!startStop) startStop = colorStops[0];
102
+ if (!endStop) endStop = colorStops[colorStops.length - 1];
103
+
104
+ const segmentProgress = (progress - startStop.offset) / (endStop.offset - startStop.offset);
105
+
106
+ const startColor = startStop.color; // 直接使用 parseRgb
107
+ const endColor = endStop.color;
108
+
109
+ const r = Math.round(startColor.r + (endColor.r - startColor.r) * segmentProgress);
110
+ const g = Math.round(startColor.g + (endColor.g - startColor.g) * segmentProgress);
111
+ const b = Math.round(startColor.b + (endColor.b - startColor.b) * segmentProgress);
112
+
113
+ // 范围检查和修正
114
+ const safeR = Math.max(0, Math.min(255, r));
115
+ const safeG = Math.max(0, Math.min(255, g));
116
+ const safeB = Math.max(0, Math.min(255, b));
117
+
118
+ gradientColors.push(`rgb(${safeR}, ${safeG}, ${safeB})`);
119
+ }
120
+
121
+ return gradientColors;
122
+ },
123
+
124
+ colorToRgb(color) {
125
+ if (this.isHexColor(color)) {
126
+ return this.hexToRgb(color);
127
+ } else if (this.isRgbColor(color)) {
128
+ return this.rgbStringToRgb(color);
129
+ }
130
+ return null;
131
+ },
132
+
133
+ drawRingWithConicGradient(params) {
134
+ let {
135
+ startAngle,
136
+ endAngle,
137
+ offsetDegree,
138
+ innerRadius,
139
+ outerRadius,
140
+ canvas,
141
+ colorList,
142
+ ctx,
143
+ centerX,
144
+ centerY,
145
+ ringBorderColor,
146
+ } = params;
147
+
148
+ // 检查 canvas 和 context
149
+ if (!canvas || !ctx) {
150
+ console.error('【ray-circle-picker=> canvas or ctx not found');
151
+ return;
152
+ }
153
+
154
+ // 检查半径
155
+ if (
156
+ typeof innerRadius !== 'number' ||
157
+ typeof outerRadius !== 'number' ||
158
+ isNaN(innerRadius) ||
159
+ isNaN(outerRadius) ||
160
+ innerRadius < 0 ||
161
+ outerRadius < 0 ||
162
+ innerRadius >= outerRadius
163
+ ) {
164
+ console.error('【ray-circle-picker=> innerRadius or outerRadius is not a number');
165
+ return;
166
+ }
167
+
168
+ // 检查颜色数组
169
+ if (!Array.isArray(colorList) || colorList.length < 1) {
170
+ console.error('【ray-circle-picker=> colors array must contain at least one color');
171
+ return;
172
+ }
173
+
174
+ const _colorList = colorList.map(item => item.color);
175
+ // 检查颜色对象格式
176
+ for (const color of _colorList) {
177
+ let { r, g, b } = color;
178
+ if (
179
+ typeof color !== 'object' ||
180
+ typeof r !== 'number' ||
181
+ typeof g !== 'number' ||
182
+ typeof b !== 'number' ||
183
+ isNaN(r) ||
184
+ isNaN(g) ||
185
+ isNaN(b) ||
186
+ r < 0 ||
187
+ r > 255 ||
188
+ g < 0 ||
189
+ g > 255 ||
190
+ b < 0 ||
191
+ b > 255
192
+ ) {
193
+ console.error(
194
+ '【ray-circle-picker】=> color object format is not correct. r, g, b values should be between 0-255'
195
+ );
196
+ return;
197
+ }
198
+ }
199
+ ctx.beginPath();
200
+ ctx.strokeStyle = ringBorderColor || 'rgba(0, 0, 0, 0)';
201
+ this.ringBorderColor = ringBorderColor;
202
+ const counterclockwise = true; // 逆时针绘制圆弧
203
+ ctx.arc(
204
+ centerX,
205
+ centerY,
206
+ outerRadius * scale,
207
+ (startAngle * Math.PI) / 180,
208
+ (endAngle * Math.PI) / 180
209
+ ); // 外弧
210
+ ctx.arc(
211
+ centerX,
212
+ centerY,
213
+ innerRadius * scale,
214
+ (endAngle * Math.PI) / 180,
215
+ (startAngle * Math.PI) / 180,
216
+ true
217
+ ); // 内弧(逆时针)
218
+ ctx.closePath();
219
+ ctx.stroke();
220
+
221
+ ctx.clip(); // 设置裁剪区域
222
+
223
+ const steps = Math.abs(offsetDegree);
224
+ if (steps === 0) return;
225
+ let modifyColorList = colorList;
226
+ // 说明是整圆环, 颜色需要修正偏移下
227
+ if (offsetDegree === 360) {
228
+ modifyColorList = colorList
229
+ .slice(0, -1)
230
+ .concat({
231
+ ...colorList[colorList.length - 1],
232
+ offset: 0.9,
233
+ })
234
+ .concat({
235
+ ...colorList[0],
236
+ offset: 1,
237
+ });
238
+ }
239
+ const gradientColors = this.getGradientColors(modifyColorList, steps);
240
+ for (let angle = 1; angle < steps; angle += 1) {
241
+ const startDegree = ((startAngle + angle - 1) * Math.PI) / 180;
242
+ const endDegree = ((startAngle + angle + 1) * Math.PI) / 180;
243
+ ctx.beginPath();
244
+ ctx.moveTo(centerX, centerY);
245
+ ctx.arc(centerX, centerY, outerRadius * scale, startDegree, endDegree, false);
246
+ const gradient = ctx.createRadialGradient(
247
+ centerX,
248
+ centerY,
249
+ innerRadius * scale,
250
+ centerX,
251
+ centerY,
252
+ outerRadius * scale
253
+ );
254
+
255
+ const currentColor = gradientColors[angle];
256
+ gradient.addColorStop(0, currentColor);
257
+ gradient.addColorStop(1, currentColor);
258
+ ctx.fillStyle = gradient;
259
+ ctx.fill();
260
+ ctx.closePath();
261
+ }
262
+ ctx.globalCompositeOperation = 'destination-out';
263
+ },
264
+
265
+ // 环形色盘 降级绘制
266
+ async renderAnnulusColorLowRank(id, radius, innerRingRadius, options = {}) {
267
+ let canvas = null;
268
+ const { touchCircleStrokeStyle, ringBorderColor } =
269
+ options;
270
+ this.touchCircleStrokeStyle = touchCircleStrokeStyle;
271
+ try {
272
+ canvas = await getCanvasById(id);
273
+ } catch (error) {
274
+ console.error(`【ray-circle-picker】=> ${error}`);
275
+ return;
276
+ }
277
+ if (!canvas) {
278
+ console.error('【ray-circle-picker】=> canvas not found');
279
+ return;
280
+ }
281
+
282
+ this.options = options || {};
283
+ this.radius = radius;
284
+ this.innerRingRadius = innerRingRadius;
285
+
286
+ const diameter = radius * 2;
287
+ canvas.width = diameter * scale;
288
+ canvas.height = diameter * scale;
289
+ const ctx = canvas.getContext('2d');
290
+
291
+ const poxCenterX = radius * scale;
292
+ const poxCenterY = radius * scale;
293
+
294
+ const colorList = options.colorList.map(item => {
295
+ return {
296
+ offset: item.offset,
297
+ color: this.colorToRgb(item.color),
298
+ };
299
+ });
300
+ const startDegree = 135;
301
+ const offsetDegree = 270;
302
+ const endDegree = startDegree + offsetDegree;
303
+
304
+ this.drawRingWithConicGradient({
305
+ startAngle: startDegree,
306
+ endAngle: endDegree,
307
+ offsetDegree,
308
+ innerRadius: innerRingRadius,
309
+ outerRadius: radius,
310
+ colorList: colorList,
311
+ canvas,
312
+ ctx,
313
+ centerX: poxCenterX,
314
+ centerY: poxCenterY,
315
+ ringBorderColor,
316
+ });
317
+
318
+ ctx.scale(scale, scale);
319
+ canvas.style.width = `${diameter}px`;
320
+ canvas.style.height = `${diameter}px`;
321
+ this.annulusContext = ctx;
322
+ },
323
+
7
324
  // 环形色盘
8
325
  async renderAnnulusColor(id, radius, innerRingRadius, temp = 0, options = {}) {
9
326
  let canvas = null;
@@ -41,25 +358,37 @@ export default Render({
41
358
  endAngle
42
359
  );
43
360
 
44
- let grd = ctx.createConicGradient(startAngle - Math.PI * 0.1, radius * 2, radius * 2);
361
+ let grd = null;
362
+ if (ctx.createConicGradient) {
363
+ try {
364
+ grd = ctx.createConicGradient(startAngle - Math.PI * 0.1, radius * 2, radius * 2);
365
+ options.colorList?.forEach(item => {
366
+ grd.addColorStop(item.offset, item.color);
367
+ });
368
+ //设定曲线粗细度
369
+ ctx.lineWidth = (radius - innerRingRadius) * 2;
370
+ //给曲线着色
371
+ ctx.strokeStyle = grd;
372
+ //连接处样式
373
+ ctx.lineCap = 'round';
374
+ //给环着色
375
+ ctx.stroke();
376
+ ctx.closePath();
377
+ ctx.scale(2, 2);
378
+ canvas.style.width = `${radius * 2}px`;
379
+ canvas.style.height = `${radius * 2}px`;
380
+ this.annulusContext = ctx;
381
+ } catch (err) {
382
+ console.error('createConicGradient:', err);
383
+ }
384
+ !this.hideThumb && this.renderAnnulusColorThumb(id, temp);
385
+ this.callMethod('initedCanvas', {});
386
+ return;
387
+ }
45
388
 
46
- options.colorList?.forEach(item => {
47
- grd.addColorStop(item.offset, item.color);
48
- });
389
+ // 降级渲染,兼容安卓低版本机型
390
+ this.renderAnnulusColorLowRank(id, radius, innerRingRadius, options);
49
391
 
50
- //设定曲线粗细度
51
- ctx.lineWidth = (radius - innerRingRadius) * 2;
52
- //给曲线着色
53
- ctx.strokeStyle = grd;
54
- //连接处样式
55
- ctx.lineCap = 'round';
56
- //给环着色
57
- ctx.stroke();
58
- ctx.closePath();
59
- ctx.scale(2, 2);
60
- canvas.style.width = `${radius * 2}px`;
61
- canvas.style.height = `${radius * 2}px`;
62
- this.annulusContext = ctx;
63
392
  !this.hideThumb && this.renderAnnulusColorThumb(id, temp);
64
393
  this.callMethod('initedCanvas', {});
65
394
  },
@@ -85,6 +414,11 @@ export default Render({
85
414
  },
86
415
  _getAnglePositionByValue(value) {
87
416
  const ctx = this.annulusContext;
417
+ if (!ctx) {
418
+ console.error('ctx not found');
419
+ return;
420
+ }
421
+
88
422
  const angle = 135 + (value / 1000) * 270;
89
423
  const x =
90
424
  this.radius +
package/lib/props.d.ts CHANGED
@@ -19,7 +19,7 @@ export interface IProps {
19
19
  * @description.en Whether to hide the drag ring
20
20
  * @default false
21
21
  */
22
- hideThumb: boolean;
22
+ hideThumb?: boolean;
23
23
  /**
24
24
  * @description.en temperature
25
25
  * @description.zh 色温
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/lamp-circle-picker",
3
- "version": "1.0.11-beta-2",
3
+ "version": "1.0.11-beta-4",
4
4
  "description": "照明缺角色环",
5
5
  "main": "lib/index",
6
6
  "files": [