@umituz/react-native-image 1.2.5 → 1.3.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-image",
3
- "version": "1.2.5",
3
+ "version": "1.3.2",
4
4
  "description": "Image manipulation and viewing for React Native apps - resize, crop, rotate, flip, compress, gallery viewer",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -29,12 +29,13 @@
29
29
  "type": "git",
30
30
  "url": "https://github.com/umituz/react-native-image"
31
31
  },
32
+ "dependencies": {
33
+ "expo-image-manipulator": ">=12.0.0"
34
+ },
32
35
  "peerDependencies": {
33
36
  "@umituz/react-native-animation": "latest",
34
- "@umituz/react-native-design-system": "latest",
35
- "@umituz/react-native-icons": "latest",
37
+ "@umituz/react-native-design-system": "^2.1.0",
36
38
  "expo-image": ">=1.0.0",
37
- "expo-image-manipulator": ">=12.0.0",
38
39
  "expo-media-library": ">=15.0.0",
39
40
  "expo-sharing": ">=12.0.0",
40
41
  "react": ">=18.2.0",
@@ -44,8 +45,7 @@
44
45
  },
45
46
  "devDependencies": {
46
47
  "@umituz/react-native-animation": "latest",
47
- "@umituz/react-native-design-system": "latest",
48
- "@umituz/react-native-icons": "latest",
48
+ "@umituz/react-native-design-system": "^2.1.0",
49
49
  "expo-image": "~2.0.0",
50
50
  "expo-image-manipulator": "~13.0.0",
51
51
  "expo-media-library": "~17.0.0",
package/src/index.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * @umituz/react-native-image - Public API
3
3
  *
4
- * Image manipulation and viewing for React Native apps
4
+ * React Native image manipulation and viewing
5
5
  * Resize, crop, rotate, flip, compress, gallery viewer
6
6
  *
7
7
  * Usage:
8
- * import { useImage, ImageGallery, ImageService, ImageUtils } from '@umituz/react-native-image';
8
+ * import { useImage, ImageGallery, ImageUtils } from '@umituz/react-native-image';
9
9
  */
10
10
 
11
11
  // =============================================================================
@@ -35,11 +35,10 @@ export { IMAGE_CONSTANTS } from './domain/entities/ImageConstants';
35
35
  export { ImageUtils } from './domain/utils/ImageUtils';
36
36
 
37
37
  // =============================================================================
38
- // DOMAIN LAYER - Filter Types
38
+ // DOMAIN LAYER - Filter Types (React Native Compatible Only)
39
39
  // =============================================================================
40
40
 
41
41
  export type {
42
- ImageFilter,
43
42
  ImageFilterOptions,
44
43
  ImageColorAdjustment,
45
44
  ImageQualityMetrics,
@@ -52,7 +51,7 @@ export {
52
51
  } from './domain/entities/ImageFilterTypes';
53
52
 
54
53
  // =============================================================================
55
- // INFRASTRUCTURE LAYER - Services
54
+ // INFRASTRUCTURE LAYER - React Native Compatible Services
56
55
  // =============================================================================
57
56
 
58
57
  export { ImageTransformService } from './infrastructure/services/ImageTransformService';
@@ -64,57 +63,29 @@ export {
64
63
  type ImageViewerConfig,
65
64
  } from './infrastructure/services/ImageViewerService';
66
65
 
67
- // =============================================================================
68
- // INFRASTRUCTURE LAYER - Advanced Services
69
- // =============================================================================
70
-
71
- export { ImageFilterService } from './infrastructure/services/ImageFilterService';
72
66
  export { ImageBatchService, type BatchOperation, type BatchProcessingOptions, type BatchProcessingResult } from './infrastructure/services/ImageBatchService';
73
67
  export { ImageAIEnhancementService, type AutoEnhancementOptions, type EnhancementResult } from './infrastructure/services/ImageAIEnhancementService';
74
- export { ImageAnnotationService, type ImageAnnotation, type TextOverlay, type DrawingElement, type WatermarkOptions } from './infrastructure/services/ImageAnnotationService';
75
68
  export { ImageMetadataService, type ImageMetadataExtractionOptions } from './infrastructure/services/ImageMetadataService';
76
69
  export { ImageQualityPresetService, type QualityPreset, type QualityPresets, IMAGE_QUALITY_PRESETS } from './infrastructure/utils/ImageQualityPresets';
77
70
  export { ImageSpecializedEnhancementService } from './infrastructure/services/ImageSpecializedEnhancementService';
78
71
 
79
72
  // =============================================================================
80
- // PRESENTATION LAYER - Components & Hooks
73
+ // PRESENTATION LAYER - React Native Components & Hooks
81
74
  // =============================================================================
82
75
 
83
76
  export { ImageGallery, type ImageGalleryProps } from './presentation/components/ImageGallery';
84
77
 
85
- // =============================================================================
86
- // PRESENTATION LAYER - Core Hooks
87
- // =============================================================================
88
-
89
78
  export { useImage } from './presentation/hooks/useImage';
90
79
  export { useImageTransform } from './presentation/hooks/useImageTransform';
91
80
  export { useImageConversion } from './presentation/hooks/useImageConversion';
92
- // =============================================================================
93
- // PRESENTATION LAYER - Editor Components & Hooks
94
- // =============================================================================
95
-
96
- export { useImageEditor } from './presentation/hooks/useImageEditor';
97
- export { useEditorTools } from './presentation/hooks/useEditorTools';
98
- export { Editor } from './presentation/components/Editor';
99
- export { EditorCanvas } from './presentation/components/EditorCanvas';
100
- export { EditorToolbar } from './presentation/components/EditorToolbar';
101
- export { EditorPanel } from './presentation/components/EditorPanel';
102
- export { CropComponent } from './presentation/components/CropComponent';
103
- export { FilterSlider } from './presentation/components/FilterSlider';
104
81
 
105
82
  export {
106
83
  useImageGallery,
107
84
  type UseImageGalleryReturn,
108
85
  } from './presentation/hooks/useImageGallery';
109
86
 
110
- // =============================================================================
111
- // PRESENTATION LAYER - Advanced Hooks
112
- // =============================================================================
113
-
114
- export { useImageFilter } from './presentation/hooks/useImageFilter';
115
87
  export { useImageBatch } from './presentation/hooks/useImageBatch';
116
88
  export { useImageAIEnhancement } from './presentation/hooks/useImageAIEnhancement';
117
- export { useImageAnnotation } from './presentation/hooks/useImageAnnotation';
118
89
  export { useImageMetadata } from './presentation/hooks/useImageMetadata';
119
90
 
120
91
 
@@ -1,189 +0,0 @@
1
- /**
2
- * Image Infrastructure - Annotation Service
3
- *
4
- * Handles text overlay, drawing, and annotation features
5
- */
6
-
7
- import type { ImageManipulationResult } from '../../domain/entities/ImageTypes';
8
- import { ImageValidator } from '../utils/ImageValidator';
9
- import { ImageErrorHandler, IMAGE_ERROR_CODES } from '../utils/ImageErrorHandler';
10
- import { CanvasRenderingService } from '../utils/CanvasRenderingService';
11
-
12
- export interface TextOverlay {
13
- text: string;
14
- x: number;
15
- y: number;
16
- fontSize?: number;
17
- fontFamily?: string;
18
- color?: string;
19
- backgroundColor?: string;
20
- maxWidth?: number;
21
- rotation?: number;
22
- }
23
-
24
- export interface DrawingElement {
25
- type: 'line' | 'rectangle' | 'circle' | 'arrow' | 'freehand';
26
- points: Array<{ x: number; y: number }>;
27
- color?: string;
28
- strokeWidth?: number;
29
- fillColor?: string;
30
- }
31
-
32
- export interface WatermarkOptions {
33
- text?: string;
34
- imageUri?: string;
35
- position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'center';
36
- opacity?: number;
37
- size?: number;
38
- margin?: number;
39
- }
40
-
41
- export interface ImageAnnotation {
42
- textOverlays?: TextOverlay[];
43
- drawings?: DrawingElement[];
44
- watermark?: WatermarkOptions;
45
- }
46
-
47
- export class ImageAnnotationService {
48
- private static getPositionCoordinates(
49
- position: string,
50
- imageWidth: number,
51
- imageHeight: number,
52
- elementWidth: number,
53
- elementHeight: number,
54
- margin: number = 10
55
- ): { x: number; y: number } {
56
- switch (position) {
57
- case 'top-left':
58
- return { x: margin, y: margin };
59
- case 'top-right':
60
- return { x: imageWidth - elementWidth - margin, y: margin };
61
- case 'bottom-left':
62
- return { x: margin, y: imageHeight - elementHeight - margin };
63
- case 'bottom-right':
64
- return { x: imageWidth - elementWidth - margin, y: imageHeight - elementHeight - margin };
65
- case 'center':
66
- return {
67
- x: (imageWidth - elementWidth) / 2,
68
- y: (imageHeight - elementHeight) / 2
69
- };
70
- default:
71
- return { x: margin, y: margin };
72
- }
73
- }
74
-
75
- static async addTextOverlay(
76
- uri: string,
77
- overlay: TextOverlay
78
- ): Promise<ImageManipulationResult> {
79
- try {
80
- const uriValidation = ImageValidator.validateUri(uri);
81
- if (!uriValidation.isValid) {
82
- throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'addTextOverlay');
83
- }
84
-
85
- // In a real implementation, we would:
86
- // 1. Load image into canvas
87
- // 2. Apply text overlay using canvas rendering
88
- // 3. Export canvas to new URI
89
-
90
- return {
91
- uri, // Would be processed URI
92
- width: 0,
93
- height: 0,
94
- };
95
- } catch (error) {
96
- throw ImageErrorHandler.handleUnknownError(error, 'addTextOverlay');
97
- }
98
- }
99
-
100
- static async addDrawingElements(
101
- uri: string,
102
- elements: DrawingElement[]
103
- ): Promise<ImageManipulationResult> {
104
- try {
105
- const uriValidation = ImageValidator.validateUri(uri);
106
- if (!uriValidation.isValid) {
107
- throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'addDrawingElements');
108
- }
109
-
110
- // Mock implementation
111
- return {
112
- uri,
113
- width: 0,
114
- height: 0,
115
- };
116
- } catch (error) {
117
- throw ImageErrorHandler.handleUnknownError(error, 'addDrawingElements');
118
- }
119
- }
120
-
121
- static async addWatermark(
122
- uri: string,
123
- options: WatermarkOptions
124
- ): Promise<ImageManipulationResult> {
125
- try {
126
- const uriValidation = ImageValidator.validateUri(uri);
127
- if (!uriValidation.isValid) {
128
- throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'addWatermark');
129
- }
130
-
131
- if (!options.text && !options.imageUri) {
132
- throw ImageErrorHandler.createError(
133
- 'Either text or imageUri must be provided for watermark',
134
- IMAGE_ERROR_CODES.VALIDATION_ERROR,
135
- 'addWatermark'
136
- );
137
- }
138
-
139
- // Mock implementation
140
- return {
141
- uri,
142
- width: 0,
143
- height: 0,
144
- };
145
- } catch (error) {
146
- throw ImageErrorHandler.handleUnknownError(error, 'addWatermark');
147
- }
148
- }
149
-
150
- static async applyAnnotation(
151
- uri: string,
152
- annotation: ImageAnnotation
153
- ): Promise<ImageManipulationResult> {
154
- try {
155
- const uriValidation = ImageValidator.validateUri(uri);
156
- if (!uriValidation.isValid) {
157
- throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'applyAnnotation');
158
- }
159
-
160
- // Apply all annotations in order
161
- let resultUri = uri;
162
-
163
- if (annotation.textOverlays) {
164
- for (const overlay of annotation.textOverlays) {
165
- const result = await ImageAnnotationService.addTextOverlay(resultUri, overlay);
166
- resultUri = result.uri;
167
- }
168
- }
169
-
170
- if (annotation.drawings) {
171
- const result = await ImageAnnotationService.addDrawingElements(resultUri, annotation.drawings);
172
- resultUri = result.uri;
173
- }
174
-
175
- if (annotation.watermark) {
176
- const result = await ImageAnnotationService.addWatermark(resultUri, annotation.watermark);
177
- resultUri = result.uri;
178
- }
179
-
180
- return {
181
- uri: resultUri,
182
- width: 0,
183
- height: 0,
184
- };
185
- } catch (error) {
186
- throw ImageErrorHandler.handleUnknownError(error, 'applyAnnotation');
187
- }
188
- }
189
- }
@@ -1,168 +0,0 @@
1
- /**
2
- * Image Infrastructure - Filter Service
3
- *
4
- * Advanced image filtering and effects using canvas and image processing
5
- */
6
-
7
- import type {
8
- ImageFilter,
9
- ImageFilterType,
10
- ImageColorAdjustment,
11
- ImageQualityMetrics,
12
- ImageColorPalette,
13
- } from '../../domain/entities/ImageFilterTypes';
14
- import type { ImageManipulationResult } from '../../domain/entities/ImageTypes';
15
- import { ImageValidator } from '../utils/ImageValidator';
16
- import { ImageErrorHandler, IMAGE_ERROR_CODES } from '../utils/ImageErrorHandler';
17
- import { FilterEffects } from '../utils/FilterEffects';
18
-
19
- export class ImageFilterService {
20
- private static createCanvasImageData(
21
- width: number,
22
- height: number,
23
- data: Uint8ClampedArray
24
- ): ImageData {
25
- return { data, width, height } as ImageData;
26
- }
27
-
28
- private static applyBrightness(
29
- imageData: ImageData,
30
- intensity: number
31
- ): ImageData {
32
- const data = new Uint8ClampedArray(imageData.data);
33
- for (let i = 0; i < data.length; i += 4) {
34
- data[i] = Math.min(255, Math.max(0, data[i] + intensity * 255));
35
- data[i + 1] = Math.min(255, Math.max(0, data[i + 1] + intensity * 255));
36
- data[i + 2] = Math.min(255, Math.max(0, data[i + 2] + intensity * 255));
37
- }
38
- return ImageFilterService.createCanvasImageData(imageData.width, imageData.height, data);
39
- }
40
-
41
- private static applyContrast(
42
- imageData: ImageData,
43
- intensity: number
44
- ): ImageData {
45
- const data = new Uint8ClampedArray(imageData.data);
46
- const factor = (259 * (intensity * 255 + 255)) / (255 * (259 - intensity * 255));
47
-
48
- for (let i = 0; i < data.length; i += 4) {
49
- data[i] = Math.min(255, Math.max(0, factor * (data[i] - 128) + 128));
50
- data[i + 1] = Math.min(255, Math.max(0, factor * (data[i + 1] - 128) + 128));
51
- data[i + 2] = Math.min(255, Math.max(0, factor * (data[i + 2] - 128) + 128));
52
- }
53
- return ImageFilterService.createCanvasImageData(imageData.width, imageData.height, data);
54
- }
55
-
56
- private static applyGrayscale(imageData: ImageData): ImageData {
57
- const data = new Uint8ClampedArray(imageData.data);
58
- for (let i = 0; i < data.length; i += 4) {
59
- const gray = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;
60
- data[i] = gray;
61
- data[i + 1] = gray;
62
- data[i + 2] = gray;
63
- }
64
- return ImageFilterService.createCanvasImageData(imageData.width, imageData.height, data);
65
- }
66
-
67
- private static applySepia(imageData: ImageData, intensity: number = 1): ImageData {
68
- return FilterEffects.applySepia(imageData, intensity);
69
- }
70
-
71
-
72
-
73
- static async applyFilter(
74
- uri: string,
75
- filter: ImageFilter
76
- ): Promise<ImageManipulationResult> {
77
- try {
78
- const uriValidation = ImageValidator.validateUri(uri);
79
- if (!uriValidation.isValid) {
80
- throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'applyFilter');
81
- }
82
-
83
- // In a real implementation, we would:
84
- // 1. Load the image into a canvas
85
- // 2. Apply the filter to the pixel data
86
- // 3. Export the canvas to a new URI
87
-
88
- // For now, return a mock implementation
89
- return {
90
- uri, // Would be the processed URI
91
- width: 0,
92
- height: 0,
93
- base64: undefined,
94
- };
95
- } catch (error) {
96
- throw ImageErrorHandler.handleUnknownError(error, 'applyFilter');
97
- }
98
- }
99
-
100
- static async applyColorAdjustment(
101
- uri: string,
102
- adjustment: ImageColorAdjustment
103
- ): Promise<ImageManipulationResult> {
104
- try {
105
- const uriValidation = ImageValidator.validateUri(uri);
106
- if (!uriValidation.isValid) {
107
- throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'applyColorAdjustment');
108
- }
109
-
110
- // Apply brightness, contrast, saturation adjustments
111
- return {
112
- uri,
113
- width: 0,
114
- height: 0,
115
- };
116
- } catch (error) {
117
- throw ImageErrorHandler.handleUnknownError(error, 'applyColorAdjustment');
118
- }
119
- }
120
-
121
- static async analyzeQuality(uri: string): Promise<ImageQualityMetrics> {
122
- try {
123
- const uriValidation = ImageValidator.validateUri(uri);
124
- if (!uriValidation.isValid) {
125
- throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'analyzeQuality');
126
- }
127
-
128
- // Mock implementation - would analyze actual image data
129
- return {
130
- sharpness: Math.random() * 100,
131
- brightness: Math.random() * 100,
132
- contrast: Math.random() * 100,
133
- colorfulness: Math.random() * 100,
134
- overallQuality: Math.random() * 100,
135
- };
136
- } catch (error) {
137
- throw ImageErrorHandler.handleUnknownError(error, 'analyzeQuality');
138
- }
139
- }
140
-
141
- static async extractColorPalette(
142
- uri: string,
143
- colorCount: number = 5
144
- ): Promise<ImageColorPalette> {
145
- try {
146
- const uriValidation = ImageValidator.validateUri(uri);
147
- if (!uriValidation.isValid) {
148
- throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'extractColorPalette');
149
- }
150
-
151
- // Mock implementation - would extract actual colors
152
- const colors = Array.from({ length: colorCount }, () =>
153
- `#${Math.floor(Math.random()*16777215).toString(16).padStart(6, '0')}`
154
- );
155
-
156
- return {
157
- dominant: colors.slice(0, 3),
158
- palette: colors.map((color, index) => ({
159
- color,
160
- percentage: Math.random() * 30 + 10,
161
- population: Math.floor(Math.random() * 1000) + 100,
162
- })),
163
- };
164
- } catch (error) {
165
- throw ImageErrorHandler.handleUnknownError(error, 'extractColorPalette');
166
- }
167
- }
168
- }
@@ -1,134 +0,0 @@
1
- /**
2
- * Image Infrastructure - Canvas Rendering Service
3
- *
4
- * Canvas-based rendering utilities for annotations and filters
5
- */
6
-
7
- export class CanvasRenderingService {
8
- static renderTextOnCanvas(
9
- ctx: CanvasRenderingContext2D,
10
- overlay: import('../services/ImageAnnotationService').TextOverlay
11
- ): void {
12
- ctx.save();
13
-
14
- ctx.font = `${overlay.fontSize || 16}px ${overlay.fontFamily || 'Arial'}`;
15
- ctx.fillStyle = overlay.color || '#000000';
16
-
17
- if (overlay.backgroundColor) {
18
- const metrics = ctx.measureText(overlay.text);
19
- const padding = 4;
20
- ctx.fillStyle = overlay.backgroundColor;
21
- ctx.fillRect(
22
- overlay.x - padding,
23
- overlay.y - (overlay.fontSize || 16) - padding,
24
- metrics.width + padding * 2,
25
- (overlay.fontSize || 16) + padding * 2
26
- );
27
- ctx.fillStyle = overlay.color || '#000000';
28
- }
29
-
30
- if (overlay.rotation) {
31
- ctx.translate(overlay.x, overlay.y);
32
- ctx.rotate((overlay.rotation * Math.PI) / 180);
33
- ctx.fillText(overlay.text, 0, 0);
34
- } else {
35
- ctx.fillText(overlay.text, overlay.x, overlay.y);
36
- }
37
-
38
- ctx.restore();
39
- }
40
-
41
- static renderDrawingOnCanvas(
42
- ctx: CanvasRenderingContext2D,
43
- drawing: import('../services/ImageAnnotationService').DrawingElement
44
- ): void {
45
- ctx.save();
46
- ctx.strokeStyle = drawing.color || '#000000';
47
- ctx.lineWidth = drawing.strokeWidth || 2;
48
-
49
- if (drawing.fillColor) {
50
- ctx.fillStyle = drawing.fillColor;
51
- }
52
-
53
- switch (drawing.type) {
54
- case 'line':
55
- if (drawing.points.length >= 2) {
56
- ctx.beginPath();
57
- ctx.moveTo(drawing.points[0].x, drawing.points[0].y);
58
- for (let i = 1; i < drawing.points.length; i++) {
59
- ctx.lineTo(drawing.points[i].x, drawing.points[i].y);
60
- }
61
- ctx.stroke();
62
- }
63
- break;
64
-
65
- case 'rectangle':
66
- if (drawing.points.length >= 2) {
67
- const width = drawing.points[1].x - drawing.points[0].x;
68
- const height = drawing.points[1].y - drawing.points[0].y;
69
- if (drawing.fillColor) {
70
- ctx.fillRect(drawing.points[0].x, drawing.points[0].y, width, height);
71
- }
72
- ctx.strokeRect(drawing.points[0].x, drawing.points[0].y, width, height);
73
- }
74
- break;
75
-
76
- case 'circle':
77
- if (drawing.points.length >= 2) {
78
- const radius = Math.sqrt(
79
- Math.pow(drawing.points[1].x - drawing.points[0].x, 2) +
80
- Math.pow(drawing.points[1].y - drawing.points[0].y, 2)
81
- );
82
- ctx.beginPath();
83
- ctx.arc(drawing.points[0].x, drawing.points[0].y, radius, 0, 2 * Math.PI);
84
- if (drawing.fillColor) {
85
- ctx.fill();
86
- }
87
- ctx.stroke();
88
- }
89
- break;
90
-
91
- case 'arrow':
92
- if (drawing.points.length >= 2) {
93
- // Draw line
94
- ctx.beginPath();
95
- ctx.moveTo(drawing.points[0].x, drawing.points[0].y);
96
- ctx.lineTo(drawing.points[1].x, drawing.points[1].y);
97
- ctx.stroke();
98
-
99
- // Draw arrowhead
100
- const angle = Math.atan2(
101
- drawing.points[1].y - drawing.points[0].y,
102
- drawing.points[1].x - drawing.points[0].x
103
- );
104
- const arrowLength = 10;
105
- ctx.beginPath();
106
- ctx.moveTo(drawing.points[1].x, drawing.points[1].y);
107
- ctx.lineTo(
108
- drawing.points[1].x - arrowLength * Math.cos(angle - Math.PI / 6),
109
- drawing.points[1].y - arrowLength * Math.sin(angle - Math.PI / 6)
110
- );
111
- ctx.moveTo(drawing.points[1].x, drawing.points[1].y);
112
- ctx.lineTo(
113
- drawing.points[1].x - arrowLength * Math.cos(angle + Math.PI / 6),
114
- drawing.points[1].y - arrowLength * Math.sin(angle + Math.PI / 6)
115
- );
116
- ctx.stroke();
117
- }
118
- break;
119
-
120
- case 'freehand':
121
- if (drawing.points.length >= 2) {
122
- ctx.beginPath();
123
- ctx.moveTo(drawing.points[0].x, drawing.points[0].y);
124
- for (let i = 1; i < drawing.points.length; i++) {
125
- ctx.lineTo(drawing.points[i].x, drawing.points[i].y);
126
- }
127
- ctx.stroke();
128
- }
129
- break;
130
- }
131
-
132
- ctx.restore();
133
- }
134
- }