@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.
- package/lib/component/rjs/index.js +15 -6
- package/lib/component/rjs/index.rjs +351 -17
- package/lib/props.d.ts +1 -1
- 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: {
|
|
@@ -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$
|
|
133
|
+
var _this$render2;
|
|
125
134
|
if (value === undefined) {
|
|
126
135
|
return;
|
|
127
136
|
}
|
|
128
|
-
(_this$
|
|
137
|
+
(_this$render2 = this.render) === null || _this$render2 === void 0 || _this$render2._getAnglePositionByValue(value);
|
|
129
138
|
},
|
|
130
139
|
_getRgb(x, y) {
|
|
131
|
-
var _this$
|
|
132
|
-
(_this$
|
|
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 =
|
|
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
|
-
|
|
47
|
-
|
|
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