@umituz/web-canvas 1.0.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.
Files changed (55) hide show
  1. package/dist/CanvasConfig-pzoE0RHD.d.mts +310 -0
  2. package/dist/CanvasConfig-pzoE0RHD.d.ts +310 -0
  3. package/dist/application/index.d.mts +78 -0
  4. package/dist/application/index.d.ts +78 -0
  5. package/dist/application/index.js +1083 -0
  6. package/dist/application/index.mjs +11 -0
  7. package/dist/chunk-BN7JTXL7.mjs +346 -0
  8. package/dist/chunk-MC6PNV2F.mjs +34 -0
  9. package/dist/chunk-SF6WND7M.mjs +547 -0
  10. package/dist/chunk-TZGTTYTZ.mjs +0 -0
  11. package/dist/chunk-WMKPCJFI.mjs +661 -0
  12. package/dist/chunk-Z6IB4BQV.mjs +31 -0
  13. package/dist/domain/config/index.d.mts +87 -0
  14. package/dist/domain/config/index.d.ts +87 -0
  15. package/dist/domain/config/index.js +92 -0
  16. package/dist/domain/config/index.mjs +15 -0
  17. package/dist/domain/index.d.mts +79 -0
  18. package/dist/domain/index.d.ts +79 -0
  19. package/dist/domain/index.js +137 -0
  20. package/dist/domain/index.mjs +55 -0
  21. package/dist/index.d.mts +6 -0
  22. package/dist/index.d.ts +6 -0
  23. package/dist/index.js +1633 -0
  24. package/dist/index.mjs +38 -0
  25. package/dist/infrastructure/index.d.mts +174 -0
  26. package/dist/infrastructure/index.d.ts +174 -0
  27. package/dist/infrastructure/index.js +714 -0
  28. package/dist/infrastructure/index.mjs +13 -0
  29. package/dist/presentation/index.d.mts +108 -0
  30. package/dist/presentation/index.d.ts +108 -0
  31. package/dist/presentation/index.js +1617 -0
  32. package/dist/presentation/index.mjs +14 -0
  33. package/package.json +86 -0
  34. package/src/application/index.ts +7 -0
  35. package/src/application/services/ReportService.ts +200 -0
  36. package/src/application/services/ScreenshotStudioService.ts +243 -0
  37. package/src/domain/config/CanvasConfig.ts +36 -0
  38. package/src/domain/config/DeviceConfig.ts +47 -0
  39. package/src/domain/config/ScreenshotStudioConfig.ts +82 -0
  40. package/src/domain/config/index.ts +8 -0
  41. package/src/domain/index.ts +7 -0
  42. package/src/domain/types/canvas.types.ts +85 -0
  43. package/src/domain/types/index.ts +8 -0
  44. package/src/domain/types/report.types.ts +83 -0
  45. package/src/domain/types/screenshot.types.ts +238 -0
  46. package/src/index.ts +102 -0
  47. package/src/infrastructure/index.ts +9 -0
  48. package/src/infrastructure/renderers/CanvasRenderer.ts +287 -0
  49. package/src/infrastructure/renderers/PatternRenderer.ts +160 -0
  50. package/src/infrastructure/renderers/ScreenshotRenderer.ts +331 -0
  51. package/src/infrastructure/services/DeviceConfigService.ts +144 -0
  52. package/src/presentation/hooks/useCanvasExport.ts +162 -0
  53. package/src/presentation/hooks/useReportExport.ts +161 -0
  54. package/src/presentation/hooks/useScreenshotStudio.ts +480 -0
  55. package/src/presentation/index.ts +8 -0
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Canvas Renderer
3
+ * Core canvas rendering operations
4
+ */
5
+
6
+ import type {
7
+ CanvasDimensions,
8
+ CanvasContextConfig,
9
+ CanvasExportOptions,
10
+ CanvasResult,
11
+ } from '../../domain/types/canvas.types';
12
+ import { DEFAULT_CANVAS_CONFIG } from '../../domain/config/CanvasConfig';
13
+
14
+ export class CanvasRenderer {
15
+ private config: typeof DEFAULT_CANVAS_CONFIG;
16
+
17
+ constructor(config = DEFAULT_CANVAS_CONFIG) {
18
+ this.config = { ...DEFAULT_CANVAS_CONFIG, ...config };
19
+ }
20
+
21
+ /**
22
+ * Create a new canvas element
23
+ */
24
+ public createCanvas(dimensions: CanvasDimensions): HTMLCanvasElement {
25
+ const canvas = document.createElement('canvas');
26
+ canvas.width = dimensions.width;
27
+ canvas.height = dimensions.height;
28
+ return canvas;
29
+ }
30
+
31
+ /**
32
+ * Get canvas 2D context
33
+ */
34
+ public getContext(
35
+ canvas: HTMLCanvasElement
36
+ ): CanvasRenderingContext2D | null {
37
+ return canvas.getContext('2d');
38
+ }
39
+
40
+ /**
41
+ * Apply context configuration
42
+ */
43
+ public configureContext(
44
+ ctx: CanvasRenderingContext2D,
45
+ config: CanvasContextConfig
46
+ ): void {
47
+ if (config.fillStyle !== undefined) {
48
+ ctx.fillStyle = config.fillStyle;
49
+ }
50
+ if (config.strokeStyle !== undefined) {
51
+ ctx.strokeStyle = config.strokeStyle;
52
+ }
53
+ if (config.lineWidth !== undefined) {
54
+ ctx.lineWidth = config.lineWidth;
55
+ }
56
+ if (config.lineCap !== undefined) {
57
+ ctx.lineCap = config.lineCap;
58
+ }
59
+ if (config.lineJoin !== undefined) {
60
+ ctx.lineJoin = config.lineJoin;
61
+ }
62
+ if (config.globalAlpha !== undefined) {
63
+ ctx.globalAlpha = config.globalAlpha;
64
+ }
65
+ if (config.font !== undefined) {
66
+ ctx.font = config.font;
67
+ }
68
+ if (config.textAlign !== undefined) {
69
+ ctx.textAlign = config.textAlign;
70
+ }
71
+ if (config.textBaseline !== undefined) {
72
+ ctx.textBaseline = config.textBaseline;
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Clear canvas
78
+ */
79
+ public clearCanvas(
80
+ ctx: CanvasRenderingContext2D,
81
+ dimensions: CanvasDimensions
82
+ ): void {
83
+ ctx.clearRect(0, 0, dimensions.width, dimensions.height);
84
+ }
85
+
86
+ /**
87
+ * Fill rectangle
88
+ */
89
+ public fillRect(
90
+ ctx: CanvasRenderingContext2D,
91
+ x: number,
92
+ y: number,
93
+ width: number,
94
+ height: number,
95
+ color?: string
96
+ ): void {
97
+ if (color) {
98
+ ctx.fillStyle = color;
99
+ }
100
+ ctx.fillRect(x, y, width, height);
101
+ }
102
+
103
+ /**
104
+ * Stroke rectangle
105
+ */
106
+ public strokeRect(
107
+ ctx: CanvasRenderingContext2D,
108
+ x: number,
109
+ y: number,
110
+ width: number,
111
+ height: number,
112
+ color?: string,
113
+ lineWidth?: number
114
+ ): void {
115
+ if (color) {
116
+ ctx.strokeStyle = color;
117
+ }
118
+ if (lineWidth !== undefined) {
119
+ ctx.lineWidth = lineWidth;
120
+ }
121
+ ctx.strokeRect(x, y, width, height);
122
+ }
123
+
124
+ /**
125
+ * Draw text
126
+ */
127
+ public drawText(
128
+ ctx: CanvasRenderingContext2D,
129
+ text: string,
130
+ x: number,
131
+ y: number,
132
+ config?: CanvasContextConfig
133
+ ): void {
134
+ if (config) {
135
+ this.configureContext(ctx, config);
136
+ }
137
+ ctx.fillText(text, x, y);
138
+ }
139
+
140
+ /**
141
+ * Draw image
142
+ */
143
+ public async drawImage(
144
+ ctx: CanvasRenderingContext2D,
145
+ imageSource: string | HTMLImageElement,
146
+ x: number,
147
+ y: number,
148
+ width?: number,
149
+ height?: number
150
+ ): Promise<void> {
151
+ let img: HTMLImageElement;
152
+
153
+ if (typeof imageSource === 'string') {
154
+ img = await this.loadImage(imageSource);
155
+ } else {
156
+ img = imageSource;
157
+ }
158
+
159
+ if (width && height) {
160
+ ctx.drawImage(img, x, y, width, height);
161
+ } else {
162
+ ctx.drawImage(img, x, y);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Load image from URL
168
+ */
169
+ private loadImage(url: string): Promise<HTMLImageElement> {
170
+ return new Promise((resolve, reject) => {
171
+ const img = new Image();
172
+ img.crossOrigin = this.config.useCORS ? 'anonymous' : (undefined as any);
173
+ img.onload = () => resolve(img);
174
+ img.onerror = () => reject(new Error(`Failed to load image: ${url}`));
175
+ img.src = url;
176
+ });
177
+ }
178
+
179
+ /**
180
+ * Create gradient
181
+ */
182
+ public createLinearGradient(
183
+ ctx: CanvasRenderingContext2D,
184
+ x1: number,
185
+ y1: number,
186
+ x2: number,
187
+ y2: number,
188
+ colorStops: Array<{ offset: number; color: string }>
189
+ ): CanvasGradient {
190
+ const gradient = ctx.createLinearGradient(x1, y1, x2, y2);
191
+ colorStops.forEach(({ offset, color }) => {
192
+ gradient.addColorStop(offset, color);
193
+ });
194
+ return gradient;
195
+ }
196
+
197
+ /**
198
+ * Export canvas to blob
199
+ */
200
+ public async exportToBlob(
201
+ canvas: HTMLCanvasElement,
202
+ options: CanvasExportOptions = {}
203
+ ): Promise<CanvasResult> {
204
+ const format = options.format || this.config.defaultFormat;
205
+ const quality = options.quality || this.config.defaultQuality;
206
+ const scale = options.scale || this.config.scale;
207
+
208
+ // Scale canvas if needed
209
+ const scaledCanvas = (scale || 1) !== 1 ? this.scaleCanvas(canvas, scale || 1) : canvas;
210
+
211
+ const mimeType = `image/${format || 'png'}`;
212
+ const blob = await new Promise<Blob | null>((resolve) => {
213
+ scaledCanvas.toBlob(
214
+ (result) => resolve(result),
215
+ mimeType,
216
+ quality
217
+ );
218
+ });
219
+
220
+ if (!blob) {
221
+ throw new Error('Failed to export canvas to blob');
222
+ }
223
+
224
+ const url = URL.createObjectURL(blob);
225
+
226
+ return {
227
+ blob,
228
+ url,
229
+ dimensions: {
230
+ width: scaledCanvas.width,
231
+ height: scaledCanvas.height,
232
+ },
233
+ format: format || 'png',
234
+ size: blob.size,
235
+ };
236
+ }
237
+
238
+ /**
239
+ * Export canvas to data URL
240
+ */
241
+ public exportToDataURL(
242
+ canvas: HTMLCanvasElement,
243
+ format?: string,
244
+ quality?: number
245
+ ): string {
246
+ const mimeType = format || `image/${this.config.defaultFormat}`;
247
+ const q = quality || this.config.defaultQuality;
248
+ return canvas.toDataURL(mimeType, q);
249
+ }
250
+
251
+ /**
252
+ * Scale canvas
253
+ */
254
+ private scaleCanvas(
255
+ canvas: HTMLCanvasElement,
256
+ scale: number
257
+ ): HTMLCanvasElement {
258
+ const scaled = document.createElement('canvas');
259
+ scaled.width = canvas.width * scale;
260
+ scaled.height = canvas.height * scale;
261
+
262
+ const ctx = scaled.getContext('2d');
263
+ if (ctx) {
264
+ ctx.imageSmoothingEnabled = true;
265
+ ctx.imageSmoothingQuality = 'high';
266
+ ctx.drawImage(canvas, 0, 0, scaled.width, scaled.height);
267
+ }
268
+
269
+ return scaled;
270
+ }
271
+
272
+ /**
273
+ * Clone canvas
274
+ */
275
+ public cloneCanvas(canvas: HTMLCanvasElement): HTMLCanvasElement {
276
+ const clone = document.createElement('canvas');
277
+ clone.width = canvas.width;
278
+ clone.height = canvas.height;
279
+
280
+ const ctx = clone.getContext('2d');
281
+ if (ctx) {
282
+ ctx.drawImage(canvas, 0, 0);
283
+ }
284
+
285
+ return clone;
286
+ }
287
+ }
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Pattern Renderer
3
+ * Background pattern rendering operations
4
+ */
5
+
6
+ import type { CanvasDimensions } from '../../domain/types/canvas.types';
7
+ import type { BackgroundPattern } from '../../domain/types/screenshot.types';
8
+
9
+ export class PatternRenderer {
10
+ /**
11
+ * Render pattern on canvas
12
+ */
13
+ public renderPattern(
14
+ ctx: CanvasRenderingContext2D,
15
+ pattern: BackgroundPattern,
16
+ dimensions: CanvasDimensions,
17
+ color: string = '#000000'
18
+ ): void {
19
+ ctx.save();
20
+ ctx.globalAlpha = 0.1;
21
+
22
+ switch (pattern) {
23
+ case 'dots':
24
+ this.renderDotPattern(ctx, dimensions, color);
25
+ break;
26
+ case 'grid':
27
+ this.renderGridPattern(ctx, dimensions, color);
28
+ break;
29
+ case 'lines':
30
+ this.renderLinePattern(ctx, dimensions, color);
31
+ break;
32
+ case 'waves':
33
+ this.renderWavePattern(ctx, dimensions, color);
34
+ break;
35
+ case 'checkers':
36
+ this.renderCheckerPattern(ctx, dimensions, color);
37
+ break;
38
+ case 'none':
39
+ default:
40
+ // Do nothing
41
+ break;
42
+ }
43
+
44
+ ctx.restore();
45
+ }
46
+
47
+ /**
48
+ * Render dot pattern
49
+ */
50
+ private renderDotPattern(
51
+ ctx: CanvasRenderingContext2D,
52
+ dimensions: CanvasDimensions,
53
+ color: string
54
+ ): void {
55
+ ctx.fillStyle = color;
56
+ const spacing = 30;
57
+
58
+ for (let x = spacing; x < dimensions.width; x += spacing) {
59
+ for (let y = spacing; y < dimensions.height; y += spacing) {
60
+ ctx.beginPath();
61
+ ctx.arc(x, y, 2, 0, Math.PI * 2);
62
+ ctx.fill();
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Render grid pattern
69
+ */
70
+ private renderGridPattern(
71
+ ctx: CanvasRenderingContext2D,
72
+ dimensions: CanvasDimensions,
73
+ color: string
74
+ ): void {
75
+ ctx.strokeStyle = color;
76
+ ctx.lineWidth = 1;
77
+ const spacing = 30;
78
+
79
+ for (let x = 0; x <= dimensions.width; x += spacing) {
80
+ ctx.beginPath();
81
+ ctx.moveTo(x, 0);
82
+ ctx.lineTo(x, dimensions.height);
83
+ ctx.stroke();
84
+ }
85
+
86
+ for (let y = 0; y <= dimensions.height; y += spacing) {
87
+ ctx.beginPath();
88
+ ctx.moveTo(0, y);
89
+ ctx.lineTo(dimensions.width, y);
90
+ ctx.stroke();
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Render line pattern
96
+ */
97
+ private renderLinePattern(
98
+ ctx: CanvasRenderingContext2D,
99
+ dimensions: CanvasDimensions,
100
+ color: string
101
+ ): void {
102
+ ctx.strokeStyle = color;
103
+ ctx.lineWidth = 2;
104
+ const spacing = 20;
105
+
106
+ for (let y = spacing; y < dimensions.height; y += spacing) {
107
+ ctx.beginPath();
108
+ ctx.moveTo(0, y);
109
+ ctx.lineTo(dimensions.width, y);
110
+ ctx.stroke();
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Render wave pattern
116
+ */
117
+ private renderWavePattern(
118
+ ctx: CanvasRenderingContext2D,
119
+ dimensions: CanvasDimensions,
120
+ color: string
121
+ ): void {
122
+ ctx.strokeStyle = color;
123
+ ctx.lineWidth = 2;
124
+ const amplitude = 10;
125
+ const frequency = 0.1;
126
+ const spacing = 40;
127
+
128
+ for (let y = spacing; y < dimensions.height; y += spacing) {
129
+ ctx.beginPath();
130
+ for (let x = 0; x <= dimensions.width; x += 5) {
131
+ const waveY = y + Math.sin(x * frequency) * amplitude;
132
+ if (x === 0) {
133
+ ctx.moveTo(x, waveY);
134
+ } else {
135
+ ctx.lineTo(x, waveY);
136
+ }
137
+ }
138
+ ctx.stroke();
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Render checker pattern
144
+ */
145
+ private renderCheckerPattern(
146
+ ctx: CanvasRenderingContext2D,
147
+ dimensions: CanvasDimensions,
148
+ color: string
149
+ ): void {
150
+ ctx.fillStyle = color;
151
+ const size = 20;
152
+
153
+ for (let x = 0; x < dimensions.width; x += size * 2) {
154
+ for (let y = 0; y < dimensions.height; y += size * 2) {
155
+ ctx.fillRect(x, y, size, size);
156
+ ctx.fillRect(x + size, y + size, size, size);
157
+ }
158
+ }
159
+ }
160
+ }