@h4md1/visual-image-tool 0.1.5 → 0.2.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 +95 -86
- package/dist/visual-image-tool.esm.js +50 -7
- package/dist/visual-image-tool.esm.js.map +1 -1
- package/dist/visual-image-tool.js +50 -7
- package/dist/visual-image-tool.js.map +1 -1
- package/dist/visual-image-tool.umd.js +1 -1
- package/dist/visual-image-tool.umd.js.map +1 -1
- package/package.json +5 -3
- package/src/visual-image-tool.js +50 -7
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
#
|
|
1
|
+
# <img src="demo/android-chrome-192x192.png" alt="Visual Image Tool logo" width="48" height="48" style="vertical-align:middle; margin-right: 0.5em; border-radius: 8px;"> Visual Image Tool
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A lightweight vanilla JavaScript tool to define focus points and crop zones on images.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **API
|
|
11
|
-
- **
|
|
12
|
-
- **Responsive
|
|
7
|
+
- **Focus point**: Set a point of interest on the image with a visual marker
|
|
8
|
+
- **Crop zone**: Define a crop zone with resize handles
|
|
9
|
+
- **No dependencies**: Works without external libraries
|
|
10
|
+
- **Simple API**: Clear and easy-to-use interface
|
|
11
|
+
- **Customizable**: Flexible configuration options
|
|
12
|
+
- **Responsive**: Adapts to screen resizing
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
@@ -17,64 +17,68 @@ Un outil léger en JavaScript vanilla pour définir des points focaux et zones d
|
|
|
17
17
|
npm install @h4md1/visual-image-tool
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## Quick Start Guide
|
|
21
21
|
|
|
22
|
-
### 1.
|
|
22
|
+
### 1. Import
|
|
23
23
|
|
|
24
24
|
```javascript
|
|
25
|
-
//
|
|
25
|
+
// ES modules import (recommended)
|
|
26
26
|
import VisualImageTool from '@h4md1/visual-image-tool';
|
|
27
27
|
|
|
28
|
-
//
|
|
28
|
+
// OR CommonJS import
|
|
29
29
|
const VisualImageTool = require('@h4md1/visual-image-tool');
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// OR direct usage via script tag (UMD)
|
|
32
32
|
// <script src="node_modules/image-tool/dist/image-tool.umd.js"></script>
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
### 2.
|
|
35
|
+
### 2. Initialization
|
|
36
36
|
|
|
37
37
|
```javascript
|
|
38
|
-
//
|
|
38
|
+
// Create an instance with an image
|
|
39
39
|
const imageTool = new VisualImageTool.VisualImageTool({
|
|
40
40
|
imageElement: document.getElementById('myImage'),
|
|
41
|
+
debug: true, // Enable debug logs for overlay positioning (optional)
|
|
41
42
|
onChange: (data) => {
|
|
42
|
-
console.log('
|
|
43
|
-
console.log('
|
|
43
|
+
console.log('Focus point:', data.focusPoint);
|
|
44
|
+
console.log('Crop zone:', data.cropZone);
|
|
44
45
|
}
|
|
45
46
|
});
|
|
46
47
|
```
|
|
47
48
|
|
|
48
|
-
### 3.
|
|
49
|
+
### 3. Using the Features
|
|
49
50
|
|
|
50
51
|
```javascript
|
|
51
|
-
//
|
|
52
|
+
// Enable the focus point
|
|
52
53
|
imageTool.toggleFocusPoint(true);
|
|
53
54
|
|
|
54
|
-
//
|
|
55
|
+
// Enable the crop zone
|
|
55
56
|
imageTool.toggleCropZone(true);
|
|
56
57
|
|
|
57
|
-
//
|
|
58
|
+
// Manually set a focus point
|
|
58
59
|
imageTool.setFocusPoint(x, y);
|
|
59
60
|
|
|
60
|
-
//
|
|
61
|
+
// Manually set a crop zone
|
|
61
62
|
imageTool.setCropZone(x, y, width, height);
|
|
62
63
|
|
|
63
|
-
//
|
|
64
|
+
// Get current values
|
|
64
65
|
const focusPoint = imageTool.getFocusPoint();
|
|
65
66
|
const cropZone = imageTool.getCropZone();
|
|
66
67
|
```
|
|
67
68
|
|
|
68
|
-
## Options
|
|
69
|
+
## Configuration Options
|
|
69
70
|
|
|
70
71
|
```javascript
|
|
71
72
|
const imageTool = new VisualImageTool.VisualImageTool({
|
|
72
|
-
//
|
|
73
|
+
// Image element (required) - can be a CSS selector or a DOM element
|
|
73
74
|
imageElement: '#myImage',
|
|
74
|
-
|
|
75
|
-
//
|
|
75
|
+
|
|
76
|
+
// Enable debug logs for overlay positioning (optional)
|
|
77
|
+
debug: true, // Set to true to see overlay positioning logs in the console
|
|
78
|
+
|
|
79
|
+
// Focus point configuration (optional)
|
|
76
80
|
focusPoint: {
|
|
77
|
-
enabled: true, //
|
|
81
|
+
enabled: true, // Enable/disable the feature
|
|
78
82
|
style: {
|
|
79
83
|
width: '30px',
|
|
80
84
|
height: '30px',
|
|
@@ -83,10 +87,10 @@ const imageTool = new VisualImageTool.VisualImageTool({
|
|
|
83
87
|
backgroundColor: 'rgba(255, 0, 0, 0.5)'
|
|
84
88
|
}
|
|
85
89
|
},
|
|
86
|
-
|
|
87
|
-
//
|
|
90
|
+
|
|
91
|
+
// Crop zone configuration (optional)
|
|
88
92
|
cropZone: {
|
|
89
|
-
enabled: true, //
|
|
93
|
+
enabled: true, // Enable/disable the feature
|
|
90
94
|
style: {
|
|
91
95
|
border: '1px dashed #fff',
|
|
92
96
|
backgroundColor: 'rgba(0, 0, 0, 0.4)'
|
|
@@ -99,71 +103,71 @@ const imageTool = new VisualImageTool.VisualImageTool({
|
|
|
99
103
|
boxShadow: '0 0 3px rgba(0,0,0,0.5)'
|
|
100
104
|
}
|
|
101
105
|
},
|
|
102
|
-
|
|
103
|
-
// Callback
|
|
106
|
+
|
|
107
|
+
// Callback called on changes (optional)
|
|
104
108
|
onChange: function(data) {
|
|
105
|
-
// data
|
|
109
|
+
// data contains focusPoint, cropZone, focusActive, cropActive
|
|
106
110
|
}
|
|
107
111
|
});
|
|
108
112
|
```
|
|
109
113
|
|
|
110
|
-
## API
|
|
114
|
+
## Full API
|
|
111
115
|
|
|
112
|
-
###
|
|
116
|
+
### Methods
|
|
113
117
|
|
|
114
118
|
#### `toggleFocusPoint(active)`
|
|
115
|
-
|
|
116
|
-
- `active` (boolean,
|
|
117
|
-
-
|
|
119
|
+
Enables or disables the focus point.
|
|
120
|
+
- `active` (boolean, optional): If set, forces the state to this value. If omitted, toggles the current state.
|
|
121
|
+
- Returns: The VisualImageTool instance for chaining.
|
|
118
122
|
|
|
119
123
|
#### `toggleCropZone(active)`
|
|
120
|
-
|
|
121
|
-
- `active` (boolean,
|
|
122
|
-
-
|
|
124
|
+
Enables or disables the crop zone.
|
|
125
|
+
- `active` (boolean, optional): If set, forces the state to this value. If omitted, toggles the current state.
|
|
126
|
+
- Returns: The VisualImageTool instance for chaining.
|
|
123
127
|
|
|
124
128
|
#### `setFocusPoint(x, y)`
|
|
125
|
-
|
|
126
|
-
- `x` (number):
|
|
127
|
-
- `y` (number):
|
|
128
|
-
-
|
|
129
|
+
Sets the position of the focus point.
|
|
130
|
+
- `x` (number): X coordinate in original pixels.
|
|
131
|
+
- `y` (number): Y coordinate in original pixels.
|
|
132
|
+
- Returns: The VisualImageTool instance for chaining.
|
|
129
133
|
|
|
130
134
|
#### `setCropZone(x, y, width, height)`
|
|
131
|
-
|
|
132
|
-
- `x` (number):
|
|
133
|
-
- `y` (number):
|
|
134
|
-
- `width` (number):
|
|
135
|
-
- `height` (number):
|
|
136
|
-
-
|
|
135
|
+
Sets the position and dimensions of the crop zone.
|
|
136
|
+
- `x` (number): X coordinate in original pixels.
|
|
137
|
+
- `y` (number): Y coordinate in original pixels.
|
|
138
|
+
- `width` (number): Width in original pixels.
|
|
139
|
+
- `height` (number): Height in original pixels.
|
|
140
|
+
- Returns: The VisualImageTool instance for chaining.
|
|
137
141
|
|
|
138
142
|
#### `getFocusPoint()`
|
|
139
|
-
|
|
140
|
-
-
|
|
143
|
+
Gets the current position of the focus point.
|
|
144
|
+
- Returns: An object `{x, y}` with coordinates in original pixels.
|
|
141
145
|
|
|
142
146
|
#### `getCropZone()`
|
|
143
|
-
|
|
144
|
-
-
|
|
147
|
+
Gets the current position and dimensions of the crop zone.
|
|
148
|
+
- Returns: An object `{x, y, width, height}` with values in original pixels.
|
|
145
149
|
|
|
146
150
|
#### `getImageDimensions()`
|
|
147
|
-
|
|
148
|
-
-
|
|
151
|
+
Gets the original dimensions of the image.
|
|
152
|
+
- Returns: An object `{width, height}` with dimensions in original pixels.
|
|
149
153
|
|
|
150
154
|
#### `destroy()`
|
|
151
|
-
|
|
155
|
+
Destroys the instance and cleans up resources.
|
|
152
156
|
|
|
153
|
-
###
|
|
157
|
+
### Events
|
|
154
158
|
|
|
155
|
-
|
|
159
|
+
The tool uses the `onChange` callback to notify about changes. This callback receives an object with the following properties:
|
|
156
160
|
|
|
157
161
|
```javascript
|
|
158
162
|
{
|
|
159
|
-
focusPoint: {x, y},
|
|
160
|
-
cropZone: {x, y, width, height},
|
|
161
|
-
focusActive: true|false,
|
|
162
|
-
cropActive: true|false
|
|
163
|
+
focusPoint: {x, y}, // Position of the focus point
|
|
164
|
+
cropZone: {x, y, width, height}, // Position and dimensions of the crop zone
|
|
165
|
+
focusActive: true|false, // Activation state of the focus point
|
|
166
|
+
cropActive: true|false // Activation state of the crop zone
|
|
163
167
|
}
|
|
164
168
|
```
|
|
165
169
|
|
|
166
|
-
##
|
|
170
|
+
## Integration Examples with Frameworks
|
|
167
171
|
|
|
168
172
|
### React
|
|
169
173
|
|
|
@@ -180,16 +184,16 @@ function ImageEditor() {
|
|
|
180
184
|
toolRef.current = new VisualImageTool({
|
|
181
185
|
imageElement: imageRef.current,
|
|
182
186
|
onChange: (data) => {
|
|
183
|
-
console.log('
|
|
187
|
+
console.log('Updated data:', data);
|
|
184
188
|
}
|
|
185
189
|
});
|
|
186
190
|
|
|
187
|
-
//
|
|
191
|
+
// Enable features
|
|
188
192
|
toolRef.current.toggleFocusPoint(true);
|
|
189
193
|
toolRef.current.toggleCropZone(true);
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
//
|
|
196
|
+
// Cleanup
|
|
193
197
|
return () => {
|
|
194
198
|
if (toolRef.current) {
|
|
195
199
|
toolRef.current.destroy();
|
|
@@ -200,7 +204,7 @@ function ImageEditor() {
|
|
|
200
204
|
|
|
201
205
|
return (
|
|
202
206
|
<div>
|
|
203
|
-
<img ref={imageRef} src="path/to/image.jpg" alt="
|
|
207
|
+
<img ref={imageRef} src="path/to/image.jpg" alt="Editable" />
|
|
204
208
|
</div>
|
|
205
209
|
);
|
|
206
210
|
}
|
|
@@ -211,7 +215,7 @@ function ImageEditor() {
|
|
|
211
215
|
```vue
|
|
212
216
|
<template>
|
|
213
217
|
<div>
|
|
214
|
-
<img ref="editableImage" src="path/to/image.jpg" alt="
|
|
218
|
+
<img ref="editableImage" src="path/to/image.jpg" alt="Editable" />
|
|
215
219
|
</div>
|
|
216
220
|
</template>
|
|
217
221
|
|
|
@@ -228,11 +232,11 @@ export default {
|
|
|
228
232
|
this.imageTool = new VisualImageTool({
|
|
229
233
|
imageElement: this.$refs.editableImage,
|
|
230
234
|
onChange: (data) => {
|
|
231
|
-
console.log('
|
|
235
|
+
console.log('Updated data:', data);
|
|
232
236
|
}
|
|
233
237
|
});
|
|
234
238
|
|
|
235
|
-
//
|
|
239
|
+
// Enable features
|
|
236
240
|
this.imageTool.toggleFocusPoint(true);
|
|
237
241
|
this.imageTool.toggleCropZone(true);
|
|
238
242
|
},
|
|
@@ -246,22 +250,27 @@ export default {
|
|
|
246
250
|
</script>
|
|
247
251
|
```
|
|
248
252
|
|
|
249
|
-
##
|
|
253
|
+
## Demos
|
|
250
254
|
|
|
251
|
-
|
|
255
|
+
The `demo/` folder contains the following examples:
|
|
252
256
|
|
|
253
|
-
- `basic-usage.html
|
|
254
|
-
- `custom-config.html
|
|
255
|
-
- `
|
|
256
|
-
- `
|
|
257
|
+
- `basic-usage.html`: Basic usage example
|
|
258
|
+
- `custom-config.html`: Custom configuration demo with live controls
|
|
259
|
+
- `demo-esm.html`: ESM (ECMAScript Module) integration demo
|
|
260
|
+
- `demo-umd.html`: UMD (Universal Module Definition) integration demo
|
|
261
|
+
- `index2.html`: Alternate or experimental demo page
|
|
262
|
+
- `preact-importmap-demo.html`: Preact integration using importmap
|
|
263
|
+
- `react-integration.jsx`: React integration example
|
|
264
|
+
- `vue-importmap-demo.html`: Vue integration using importmap
|
|
265
|
+
- `vue-integration.js`: Vue integration script
|
|
257
266
|
|
|
258
|
-
##
|
|
267
|
+
## Browser Compatibility
|
|
259
268
|
|
|
260
|
-
- Chrome (
|
|
261
|
-
- Firefox (
|
|
262
|
-
- Safari (
|
|
263
|
-
- Edge (
|
|
269
|
+
- Chrome (latest versions)
|
|
270
|
+
- Firefox (latest versions)
|
|
271
|
+
- Safari (latest versions)
|
|
272
|
+
- Edge (latest versions)
|
|
264
273
|
|
|
265
|
-
##
|
|
274
|
+
## License
|
|
266
275
|
|
|
267
276
|
MIT
|
|
@@ -59,7 +59,8 @@ class VisualImageTool {
|
|
|
59
59
|
},
|
|
60
60
|
...options.cropZone
|
|
61
61
|
},
|
|
62
|
-
onChange: options.onChange || (() => {})
|
|
62
|
+
onChange: options.onChange || (() => {}),
|
|
63
|
+
debug: options.debug || false
|
|
63
64
|
};
|
|
64
65
|
|
|
65
66
|
// État interne
|
|
@@ -536,10 +537,31 @@ class VisualImageTool {
|
|
|
536
537
|
}
|
|
537
538
|
|
|
538
539
|
const scaled = this._toScaledCoords(clampedX, clampedY);
|
|
539
|
-
|
|
540
|
+
|
|
541
|
+
// LOGGING: Validate padding offset
|
|
542
|
+
if (this.options.debug) {
|
|
543
|
+
const container = this.imageElement.parentNode;
|
|
544
|
+
const computedStyle = window.getComputedStyle(container);
|
|
545
|
+
const paddingLeft = parseFloat(computedStyle.paddingLeft);
|
|
546
|
+
const paddingTop = parseFloat(computedStyle.paddingTop);
|
|
547
|
+
console.log('[FocusMarker] scaled:', scaled, 'paddingLeft:', paddingLeft, 'paddingTop:', paddingTop, 'containerRect:', container.getBoundingClientRect());
|
|
548
|
+
}
|
|
549
|
+
|
|
540
550
|
// Ajuster pour centrer le marqueur
|
|
541
|
-
|
|
542
|
-
|
|
551
|
+
// Adjust for container padding (use unique variable names)
|
|
552
|
+
let focusPaddingLeft = 0, focusPaddingTop = 0;
|
|
553
|
+
{
|
|
554
|
+
const container = this.imageElement.parentNode;
|
|
555
|
+
const computedStyle = window.getComputedStyle(container);
|
|
556
|
+
focusPaddingLeft = parseFloat(computedStyle.paddingLeft);
|
|
557
|
+
focusPaddingTop = parseFloat(computedStyle.paddingTop);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
this.state.focusMarker.style.left = (scaled.x + focusPaddingLeft - this.state.focusMarker.offsetWidth / 2) + 'px';
|
|
561
|
+
this.state.focusMarker.style.top = (scaled.y + focusPaddingTop - this.state.focusMarker.offsetHeight / 2) + 'px';
|
|
562
|
+
if (this.options.debug) {
|
|
563
|
+
console.log('[FocusMarker] style.left:', this.state.focusMarker.style.left, 'style.top:', this.state.focusMarker.style.top);
|
|
564
|
+
}
|
|
543
565
|
}
|
|
544
566
|
|
|
545
567
|
/**
|
|
@@ -566,12 +588,33 @@ class VisualImageTool {
|
|
|
566
588
|
const scaled = this._toScaledCoords(clampedX, clampedY);
|
|
567
589
|
const scaledWidth = clampedWidth * this.state.scaleX;
|
|
568
590
|
const scaledHeight = clampedHeight * this.state.scaleY;
|
|
569
|
-
|
|
591
|
+
|
|
592
|
+
// LOGGING: Validate padding offset
|
|
593
|
+
if (this.options.debug) {
|
|
594
|
+
const container = this.imageElement.parentNode;
|
|
595
|
+
const computedStyle = window.getComputedStyle(container);
|
|
596
|
+
const paddingLeft = parseFloat(computedStyle.paddingLeft);
|
|
597
|
+
const paddingTop = parseFloat(computedStyle.paddingTop);
|
|
598
|
+
console.log('[CropOverlay] scaled:', scaled, 'paddingLeft:', paddingLeft, 'paddingTop:', paddingTop, 'containerRect:', container.getBoundingClientRect());
|
|
599
|
+
}
|
|
600
|
+
|
|
570
601
|
// Mettre à jour l'overlay
|
|
571
|
-
|
|
572
|
-
|
|
602
|
+
// Adjust for container padding (use unique variable names)
|
|
603
|
+
let cropPaddingLeft = 0, cropPaddingTop = 0;
|
|
604
|
+
{
|
|
605
|
+
const container = this.imageElement.parentNode;
|
|
606
|
+
const computedStyle = window.getComputedStyle(container);
|
|
607
|
+
cropPaddingLeft = parseFloat(computedStyle.paddingLeft);
|
|
608
|
+
cropPaddingTop = parseFloat(computedStyle.paddingTop);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
this.state.cropOverlay.style.left = (scaled.x + cropPaddingLeft) + 'px';
|
|
612
|
+
this.state.cropOverlay.style.top = (scaled.y + cropPaddingTop) + 'px';
|
|
573
613
|
this.state.cropOverlay.style.width = scaledWidth + 'px';
|
|
574
614
|
this.state.cropOverlay.style.height = scaledHeight + 'px';
|
|
615
|
+
if (this.options.debug) {
|
|
616
|
+
console.log('[CropOverlay] style.left:', this.state.cropOverlay.style.left, 'style.top:', this.state.cropOverlay.style.top);
|
|
617
|
+
}
|
|
575
618
|
}
|
|
576
619
|
|
|
577
620
|
/**
|