@ray-js/lamp-circle-picker 1.0.11-beta-2 → 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.
- package/lib/component/rjs/index.js +11 -6
- package/lib/component/rjs/index.rjs +354 -17
- package/package.json +1 -1
|
@@ -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$
|
|
129
|
+
var _this$render2;
|
|
125
130
|
if (value === undefined) {
|
|
126
131
|
return;
|
|
127
132
|
}
|
|
128
|
-
(_this$
|
|
133
|
+
(_this$render2 = this.render) === null || _this$render2 === void 0 || _this$render2._getAnglePositionByValue(value);
|
|
129
134
|
},
|
|
130
135
|
_getRgb(x, y) {
|
|
131
|
-
var _this$
|
|
132
|
-
(_this$
|
|
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,25 +361,37 @@ export default Render({
|
|
|
41
361
|
endAngle
|
|
42
362
|
);
|
|
43
363
|
|
|
44
|
-
let grd =
|
|
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
|
-
|
|
47
|
-
|
|
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
|
},
|
|
@@ -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 +
|