@ray-js/lamp-circle-picker 1.0.11-beta-1 → 1.0.11-beta-3

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: {
@@ -121,15 +126,15 @@ Component({
121
126
  value !== undefined && this._updatePosByRgb(value);
122
127
  },
123
128
  _updatePosByRgb(value) {
124
- var _this$render;
129
+ var _this$render2;
125
130
  if (value === undefined) {
126
131
  return;
127
132
  }
128
- (_this$render = this.render) === null || _this$render === void 0 || _this$render._getAnglePositionByValue(value);
133
+ (_this$render2 = this.render) === null || _this$render2 === void 0 || _this$render2._getAnglePositionByValue(value);
129
134
  },
130
135
  _getRgb(x, y) {
131
- var _this$render2;
132
- (_this$render2 = this.render) === null || _this$render2 === void 0 || _this$render2.getAnnulusImageData(x, y);
136
+ var _this$render3;
137
+ (_this$render3 = this.render) === null || _this$render3 === void 0 || _this$render3.getAnnulusImageData(x, y);
133
138
  },
134
139
  _getAnnulusImageData(dataRes) {
135
140
  const {
@@ -1,9 +1,329 @@
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
+ console.warn('renderAnnulusColorLowRank', JSON.stringify(options));
268
+ let canvas = null;
269
+ const { touchCircleStrokeStyle, ringBorderColor } =
270
+ options;
271
+ this.touchCircleStrokeStyle = touchCircleStrokeStyle;
272
+ try {
273
+ canvas = await getCanvasById(id);
274
+ } catch (error) {
275
+ console.error(`【ray-circle-picker】=> ${error}`);
276
+ return;
277
+ }
278
+ if (!canvas) {
279
+ console.error('【ray-circle-picker】=> canvas not found');
280
+ return;
281
+ }
282
+
283
+ this.options = options || {};
284
+ this.radius = radius;
285
+ this.innerRingRadius = innerRingRadius;
286
+
287
+ const diameter = radius * 2;
288
+ canvas.width = diameter * scale;
289
+ canvas.height = diameter * scale;
290
+ const ctx = canvas.getContext('2d');
291
+
292
+ const poxCenterX = radius * scale;
293
+ const poxCenterY = radius * scale;
294
+
295
+ const colorList = options.colorList.map(item => {
296
+ return {
297
+ offset: item.offset,
298
+ color: this.colorToRgb(item.color),
299
+ };
300
+ });
301
+ const startDegree = 135;
302
+ const offsetDegree = 270;
303
+ const endDegree = startDegree + offsetDegree;
304
+
305
+ // 颜色渲染时 按照逆时针渲染,需要按照顺时针渲染, 所以需要反转
306
+ const reversedColorStops = reverseColorStops(colorList);
307
+ this.drawRingWithConicGradient({
308
+ startAngle: startDegree,
309
+ endAngle: endDegree,
310
+ offsetDegree,
311
+ innerRadius: innerRingRadius,
312
+ outerRadius: radius,
313
+ colorList: reversedColorStops,
314
+ canvas,
315
+ ctx,
316
+ centerX: poxCenterX,
317
+ centerY: poxCenterY,
318
+ ringBorderColor,
319
+ });
320
+
321
+ ctx.scale(scale, scale);
322
+ canvas.style.width = `${diameter}px`;
323
+ canvas.style.height = `${diameter}px`;
324
+ this.annulusContext = ctx;
325
+ },
326
+
7
327
  // 环形色盘
8
328
  async renderAnnulusColor(id, radius, innerRingRadius, temp = 0, options = {}) {
9
329
  let canvas = null;
@@ -41,30 +361,42 @@ export default Render({
41
361
  endAngle
42
362
  );
43
363
 
44
- let grd = ctx.createConicGradient(startAngle - Math.PI * 0.1, radius * 2, radius * 2);
364
+ let grd = null;
365
+ if (ctx.createConicGradient) {
366
+ try {
367
+ grd = ctx.createConicGradient(startAngle - Math.PI * 0.1, radius * 2, radius * 2);
368
+ options.colorList?.forEach(item => {
369
+ grd.addColorStop(item.offset, item.color);
370
+ });
371
+ //设定曲线粗细度
372
+ ctx.lineWidth = (radius - innerRingRadius) * 2;
373
+ //给曲线着色
374
+ ctx.strokeStyle = grd;
375
+ //连接处样式
376
+ ctx.lineCap = 'round';
377
+ //给环着色
378
+ ctx.stroke();
379
+ ctx.closePath();
380
+ ctx.scale(2, 2);
381
+ canvas.style.width = `${radius * 2}px`;
382
+ canvas.style.height = `${radius * 2}px`;
383
+ this.annulusContext = ctx;
384
+ } catch (err) {
385
+ console.error('createConicGradient:', err);
386
+ }
387
+ !this.hideThumb && this.renderAnnulusColorThumb(id, temp);
388
+ this.callMethod('initedCanvas', {});
389
+ return;
390
+ }
45
391
 
46
- options.colorList?.forEach(item => {
47
- grd.addColorStop(item.offset, item.color);
48
- });
392
+ // 降级渲染,兼容安卓低版本机型
393
+ this.renderAnnulusColorLowRank(id, radius, innerRingRadius, options);
49
394
 
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
395
  !this.hideThumb && this.renderAnnulusColorThumb(id, temp);
64
396
  this.callMethod('initedCanvas', {});
65
397
  },
66
398
  async renderAnnulusColorThumb(id, temp = 0) {
67
- if (!this.hideThumb) {
399
+ if (this.hideThumb) {
68
400
  return;
69
401
  }
70
402
  if (!this.canvasThumb) {
@@ -85,6 +417,11 @@ export default Render({
85
417
  },
86
418
  _getAnglePositionByValue(value) {
87
419
  const ctx = this.annulusContext;
420
+ if (!ctx) {
421
+ console.error('ctx not found');
422
+ return;
423
+ }
424
+
88
425
  const angle = 135 + (value / 1000) * 270;
89
426
  const x =
90
427
  this.radius +
@@ -98,7 +435,7 @@ export default Render({
98
435
  this.updateThumbPosition(x, y, { r: data[0], g: data[1], b: data[2] });
99
436
  },
100
437
  updateThumbPosition(x, y, rgb) {
101
- if (!this.hideThumb) {
438
+ if (this.hideThumb) {
102
439
  return;
103
440
  }
104
441
  if (!this.canvasThumb) {
@@ -230,7 +567,7 @@ export default Render({
230
567
  this._getRgb(validXY.x, validXY.y);
231
568
  },
232
569
  handleCanvasMoveEvent(evt) {
233
- if (!this.hideThumb) {
570
+ if (this.hideThumb) {
234
571
  return;
235
572
  }
236
573
 
@@ -245,7 +582,7 @@ export default Render({
245
582
  this._getRgb(validXY.x, validXY.y);
246
583
  },
247
584
  addEventListeners() {
248
- if (!this.hideThumb) {
585
+ if (this.hideThumb) {
249
586
  return;
250
587
  }
251
588
  this.canvasThumb.addEventListener('touchstart', this.handleCanvasStartEvent, false);
@@ -253,7 +590,7 @@ export default Render({
253
590
  this.canvasThumb.addEventListener('touchend', this.handleCanvasEndEvent, false);
254
591
  },
255
592
  removeEventListeners() {
256
- if (!this.hideThumb) {
593
+ if (this.hideThumb) {
257
594
  return;
258
595
  }
259
596
  this.canvasThumb.removeEventListener('touchstart', this.handleCanvasStartEvent);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/lamp-circle-picker",
3
- "version": "1.0.11-beta-1",
3
+ "version": "1.0.11-beta-3",
4
4
  "description": "照明缺角色环",
5
5
  "main": "lib/index",
6
6
  "files": [