@codehz/draw-call 0.5.3 → 0.6.0
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/README.md +7 -7
- package/dist/browser/index.cjs +101 -107
- package/dist/browser/index.d.cts +16 -4
- package/dist/browser/index.d.ts +16 -4
- package/dist/browser/index.js +101 -107
- package/dist/node/index.cjs +101 -107
- package/dist/node/index.d.cts +16 -4
- package/dist/node/index.d.mts +16 -4
- package/dist/node/index.mjs +101 -107
- package/examples/customdraw-basic.ts +38 -34
- package/examples/customdraw.ts +62 -58
- package/package.json +1 -1
|
@@ -20,13 +20,14 @@ const SimpleRect = CustomDraw({
|
|
|
20
20
|
width: 150,
|
|
21
21
|
height: 80,
|
|
22
22
|
draw(ctx, { width, height }) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
const canvas = ctx.canvas;
|
|
24
|
+
canvas.fillStyle = "#667eea";
|
|
25
|
+
canvas.fillRect(0, 0, width, height);
|
|
26
|
+
canvas.fillStyle = "#000000";
|
|
27
|
+
canvas.font = "bold 16px sans-serif";
|
|
28
|
+
canvas.textAlign = "center";
|
|
29
|
+
canvas.textBaseline = "middle";
|
|
30
|
+
canvas.fillText("Simple Rect", width / 2, height / 2);
|
|
30
31
|
},
|
|
31
32
|
});
|
|
32
33
|
|
|
@@ -35,20 +36,21 @@ const RotatedRect = CustomDraw({
|
|
|
35
36
|
width: 150,
|
|
36
37
|
height: 80,
|
|
37
38
|
draw(ctx, { width, height }) {
|
|
39
|
+
const canvas = ctx.canvas;
|
|
38
40
|
// save/restore 会自动平衡,即使不显式调用也能正确恢复
|
|
39
41
|
ctx.save();
|
|
40
42
|
ctx.translate(width / 2, height / 2);
|
|
41
43
|
ctx.rotate((Math.PI / 4) * 0.3); // 15 度
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
canvas.fillStyle = "#764ba2";
|
|
45
|
+
canvas.fillRect(-width / 2, -height / 2, width, height);
|
|
44
46
|
ctx.restore();
|
|
45
47
|
|
|
46
48
|
// 恢复后可以正常绘制
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
canvas.fillStyle = "#000000";
|
|
50
|
+
canvas.font = "16px sans-serif";
|
|
51
|
+
canvas.textAlign = "center";
|
|
52
|
+
canvas.textBaseline = "middle";
|
|
53
|
+
canvas.fillText("Rotated", width / 2, height / 2);
|
|
52
54
|
},
|
|
53
55
|
});
|
|
54
56
|
|
|
@@ -57,24 +59,25 @@ const CircleProgress = CustomDraw({
|
|
|
57
59
|
width: 140,
|
|
58
60
|
height: 140,
|
|
59
61
|
draw(ctx, { width, height, inner }) {
|
|
62
|
+
const canvas = ctx.canvas;
|
|
60
63
|
const centerX = width / 2;
|
|
61
64
|
const centerY = height / 2;
|
|
62
65
|
const radius = Math.min(width, height) / 2 - 8;
|
|
63
66
|
|
|
64
67
|
// 绘制背景圆
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
canvas.strokeStyle = "rgba(0, 0, 0, 0.1)";
|
|
69
|
+
canvas.lineWidth = 8;
|
|
70
|
+
canvas.beginPath();
|
|
71
|
+
canvas.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
|
72
|
+
canvas.stroke();
|
|
70
73
|
|
|
71
74
|
// 绘制进度圆(65% 完成)
|
|
72
75
|
const percentage = 65;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
canvas.strokeStyle = "#ff6b6b";
|
|
77
|
+
canvas.lineCap = "round";
|
|
78
|
+
canvas.beginPath();
|
|
79
|
+
canvas.arc(centerX, centerY, radius, -Math.PI / 2, -Math.PI / 2 + (percentage / 100) * Math.PI * 2);
|
|
80
|
+
canvas.stroke();
|
|
78
81
|
|
|
79
82
|
// 调用 inner() 在中央渲染子元素(百分比文本)
|
|
80
83
|
inner?.();
|
|
@@ -99,14 +102,15 @@ const StarShape = CustomDraw({
|
|
|
99
102
|
width: 150,
|
|
100
103
|
height: 150,
|
|
101
104
|
draw(ctx, { width, height }) {
|
|
105
|
+
const canvas = ctx.canvas;
|
|
102
106
|
const centerX = width / 2;
|
|
103
107
|
const centerY = height / 2;
|
|
104
108
|
const points = 5;
|
|
105
109
|
const outerRadius = Math.min(width, height) / 2 - 5;
|
|
106
110
|
const innerRadius = outerRadius * 0.4;
|
|
107
111
|
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
canvas.fillStyle = "#ffd93d";
|
|
113
|
+
canvas.beginPath();
|
|
110
114
|
|
|
111
115
|
for (let i = 0; i < points * 2; i++) {
|
|
112
116
|
const radius = i % 2 === 0 ? outerRadius : innerRadius;
|
|
@@ -115,19 +119,19 @@ const StarShape = CustomDraw({
|
|
|
115
119
|
const y = centerY + radius * Math.sin(angle);
|
|
116
120
|
|
|
117
121
|
if (i === 0) {
|
|
118
|
-
|
|
122
|
+
canvas.moveTo(x, y);
|
|
119
123
|
} else {
|
|
120
|
-
|
|
124
|
+
canvas.lineTo(x, y);
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
|
|
124
|
-
|
|
125
|
-
|
|
128
|
+
canvas.closePath();
|
|
129
|
+
canvas.fill();
|
|
126
130
|
|
|
127
131
|
// 描边
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
132
|
+
canvas.strokeStyle = "#fa8c16";
|
|
133
|
+
canvas.lineWidth = 2;
|
|
134
|
+
canvas.stroke();
|
|
131
135
|
},
|
|
132
136
|
});
|
|
133
137
|
|
|
@@ -246,7 +250,7 @@ const layout = canvas.render(
|
|
|
246
250
|
}),
|
|
247
251
|
Text({
|
|
248
252
|
content:
|
|
249
|
-
"CustomDraw
|
|
253
|
+
"CustomDraw 提供受控的 transform/save/restore 能力,并通过 ctx.canvas 暴露原生 Canvas API。使用 inner() 方法可以在自定义绘制中渲染子元素。",
|
|
250
254
|
font: { size: 12, family: "unifont" },
|
|
251
255
|
color: "#1890ff",
|
|
252
256
|
wrap: true,
|
package/examples/customdraw.ts
CHANGED
|
@@ -23,6 +23,7 @@ function PieChart(data: Array<{ label: string; value: number; color: string }>)
|
|
|
23
23
|
width: 200,
|
|
24
24
|
height: 200,
|
|
25
25
|
draw(ctx, { width, height }) {
|
|
26
|
+
const canvas = ctx.canvas;
|
|
26
27
|
const centerX = width / 2;
|
|
27
28
|
const centerY = height / 2;
|
|
28
29
|
const radius = Math.min(width, height) / 2 - 5;
|
|
@@ -33,17 +34,17 @@ function PieChart(data: Array<{ label: string; value: number; color: string }>)
|
|
|
33
34
|
const sliceAngle = (item.value / total) * Math.PI * 2;
|
|
34
35
|
|
|
35
36
|
// 绘制扇形
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
canvas.beginPath();
|
|
38
|
+
canvas.moveTo(centerX, centerY);
|
|
39
|
+
canvas.arc(centerX, centerY, radius, currentAngle, currentAngle + sliceAngle);
|
|
40
|
+
canvas.closePath();
|
|
41
|
+
canvas.fillStyle = item.color;
|
|
42
|
+
canvas.fill();
|
|
42
43
|
|
|
43
44
|
// 绘制边框
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
canvas.strokeStyle = "#ffffff";
|
|
46
|
+
canvas.lineWidth = 2;
|
|
47
|
+
canvas.stroke();
|
|
47
48
|
|
|
48
49
|
currentAngle += sliceAngle;
|
|
49
50
|
}
|
|
@@ -57,25 +58,26 @@ function GridBackground() {
|
|
|
57
58
|
width: 100,
|
|
58
59
|
height: 100,
|
|
59
60
|
draw(ctx, { width, height }) {
|
|
61
|
+
const canvas = ctx.canvas;
|
|
60
62
|
const gridSize = 20;
|
|
61
63
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
canvas.strokeStyle = "rgba(200, 200, 200, 0.3)";
|
|
65
|
+
canvas.lineWidth = 1;
|
|
64
66
|
|
|
65
67
|
// 绘制垂直线
|
|
66
68
|
for (let x = 0; x <= width; x += gridSize) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
canvas.beginPath();
|
|
70
|
+
canvas.moveTo(x, 0);
|
|
71
|
+
canvas.lineTo(x, height);
|
|
72
|
+
canvas.stroke();
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
// 绘制水平线
|
|
74
76
|
for (let y = 0; y <= height; y += gridSize) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
canvas.beginPath();
|
|
78
|
+
canvas.moveTo(0, y);
|
|
79
|
+
canvas.lineTo(width, y);
|
|
80
|
+
canvas.stroke();
|
|
79
81
|
}
|
|
80
82
|
},
|
|
81
83
|
});
|
|
@@ -87,30 +89,31 @@ function ProgressRing(percentage: number, color: string) {
|
|
|
87
89
|
width: 120,
|
|
88
90
|
height: 120,
|
|
89
91
|
draw(ctx, { inner, width, height }) {
|
|
92
|
+
const canvas = ctx.canvas;
|
|
90
93
|
const centerX = width / 2;
|
|
91
94
|
const centerY = height / 2;
|
|
92
95
|
const radius = Math.min(width, height) / 2 - 8;
|
|
93
96
|
|
|
94
97
|
// 绘制背景圆
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
canvas.strokeStyle = "rgba(0, 0, 0, 0.1)";
|
|
99
|
+
canvas.lineWidth = 8;
|
|
100
|
+
canvas.beginPath();
|
|
101
|
+
canvas.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
|
102
|
+
canvas.stroke();
|
|
100
103
|
|
|
101
104
|
// 绘制进度圆
|
|
102
105
|
const endAngle = (percentage / 100) * Math.PI * 2 - Math.PI / 2;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
canvas.strokeStyle = color;
|
|
107
|
+
canvas.lineCap = "round";
|
|
108
|
+
canvas.beginPath();
|
|
109
|
+
canvas.arc(centerX, centerY, radius, -Math.PI / 2, endAngle);
|
|
110
|
+
canvas.stroke();
|
|
108
111
|
|
|
109
112
|
// 绘制百分比文本的背景
|
|
110
113
|
ctx.save();
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
canvas.fillStyle = "rgba(0, 0, 0, 0.5)";
|
|
115
|
+
canvas.globalAlpha = 0.1;
|
|
116
|
+
canvas.fillRect(centerX - 30, centerY - 20, 60, 40);
|
|
114
117
|
ctx.restore();
|
|
115
118
|
|
|
116
119
|
inner?.();
|
|
@@ -139,6 +142,7 @@ function LineChart() {
|
|
|
139
142
|
width: 300,
|
|
140
143
|
height: 150,
|
|
141
144
|
draw(ctx, { width, height }) {
|
|
145
|
+
const canvas = ctx.canvas;
|
|
142
146
|
const padding = 10;
|
|
143
147
|
const graphWidth = width - padding * 2;
|
|
144
148
|
const graphHeight = height - padding * 2;
|
|
@@ -147,61 +151,61 @@ function LineChart() {
|
|
|
147
151
|
const pointSpacing = graphWidth / (data.length - 1);
|
|
148
152
|
|
|
149
153
|
// 绘制网格线
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
canvas.strokeStyle = "rgba(200, 200, 200, 0.2)";
|
|
155
|
+
canvas.lineWidth = 1;
|
|
152
156
|
for (let i = 0; i <= 4; i++) {
|
|
153
157
|
const y = padding + (graphHeight / 4) * i;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
+
canvas.beginPath();
|
|
159
|
+
canvas.moveTo(padding, y);
|
|
160
|
+
canvas.lineTo(width - padding, y);
|
|
161
|
+
canvas.stroke();
|
|
158
162
|
}
|
|
159
163
|
|
|
160
164
|
// 绘制折线
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
canvas.strokeStyle = "#667eea";
|
|
166
|
+
canvas.lineWidth = 2;
|
|
167
|
+
canvas.beginPath();
|
|
164
168
|
|
|
165
169
|
for (let i = 0; i < data.length; i++) {
|
|
166
170
|
const x = padding + i * pointSpacing;
|
|
167
171
|
const y = height - padding - (data[i] / maxValue) * graphHeight;
|
|
168
172
|
|
|
169
173
|
if (i === 0) {
|
|
170
|
-
|
|
174
|
+
canvas.moveTo(x, y);
|
|
171
175
|
} else {
|
|
172
|
-
|
|
176
|
+
canvas.lineTo(x, y);
|
|
173
177
|
}
|
|
174
178
|
}
|
|
175
179
|
|
|
176
|
-
|
|
180
|
+
canvas.stroke();
|
|
177
181
|
|
|
178
182
|
// 绘制数据点
|
|
179
|
-
|
|
183
|
+
canvas.fillStyle = "#667eea";
|
|
180
184
|
for (let i = 0; i < data.length; i++) {
|
|
181
185
|
const x = padding + i * pointSpacing;
|
|
182
186
|
const y = height - padding - (data[i] / maxValue) * graphHeight;
|
|
183
187
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
188
|
+
canvas.beginPath();
|
|
189
|
+
canvas.arc(x, y, 3, 0, Math.PI * 2);
|
|
190
|
+
canvas.fill();
|
|
187
191
|
}
|
|
188
192
|
|
|
189
193
|
// 绘制阴影
|
|
190
194
|
ctx.save();
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
+
canvas.globalAlpha = 0.2;
|
|
196
|
+
canvas.fillStyle = "#667eea";
|
|
197
|
+
canvas.beginPath();
|
|
198
|
+
canvas.moveTo(padding, height - padding);
|
|
195
199
|
|
|
196
200
|
for (let i = 0; i < data.length; i++) {
|
|
197
201
|
const x = padding + i * pointSpacing;
|
|
198
202
|
const y = height - padding - (data[i] / maxValue) * graphHeight;
|
|
199
|
-
|
|
203
|
+
canvas.lineTo(x, y);
|
|
200
204
|
}
|
|
201
205
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
206
|
+
canvas.lineTo(width - padding, height - padding);
|
|
207
|
+
canvas.closePath();
|
|
208
|
+
canvas.fill();
|
|
205
209
|
ctx.restore();
|
|
206
210
|
},
|
|
207
211
|
});
|
|
@@ -224,7 +228,7 @@ const layout = canvas.render(
|
|
|
224
228
|
}),
|
|
225
229
|
|
|
226
230
|
Text({
|
|
227
|
-
content: "
|
|
231
|
+
content: "通过 ctx.canvas 调用原生 Canvas API,同时支持受控 transform 和可选子元素",
|
|
228
232
|
font: { size: 14, family: "unifont" },
|
|
229
233
|
color: "#666666",
|
|
230
234
|
}),
|
|
@@ -235,7 +239,7 @@ const layout = canvas.render(
|
|
|
235
239
|
gap: 10,
|
|
236
240
|
children: [
|
|
237
241
|
Text({
|
|
238
|
-
content: "1. 网格背景 +
|
|
242
|
+
content: "1. 网格背景 + ctx.canvas 绘制",
|
|
239
243
|
font: { size: 16, weight: "bold", family: "unifont" },
|
|
240
244
|
color: "#666666",
|
|
241
245
|
}),
|