canvasframework 0.3.6
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 +554 -0
- package/components/Accordion.js +252 -0
- package/components/AndroidDatePickerDialog.js +398 -0
- package/components/AppBar.js +225 -0
- package/components/Avatar.js +202 -0
- package/components/BottomNavigationBar.js +205 -0
- package/components/BottomSheet.js +374 -0
- package/components/Button.js +225 -0
- package/components/Card.js +193 -0
- package/components/Checkbox.js +180 -0
- package/components/Chip.js +212 -0
- package/components/CircularProgress.js +143 -0
- package/components/ContextMenu.js +116 -0
- package/components/DatePicker.js +257 -0
- package/components/Dialog.js +367 -0
- package/components/Divider.js +125 -0
- package/components/Drawer.js +261 -0
- package/components/FAB.js +270 -0
- package/components/FileUpload.js +315 -0
- package/components/IOSDatePickerWheel.js +268 -0
- package/components/ImageCarousel.js +193 -0
- package/components/ImageComponent.js +223 -0
- package/components/Input.js +309 -0
- package/components/List.js +94 -0
- package/components/ListItem.js +223 -0
- package/components/Modal.js +364 -0
- package/components/MultiSelectDialog.js +206 -0
- package/components/NumberInput.js +271 -0
- package/components/ProgressBar.js +88 -0
- package/components/RadioButton.js +142 -0
- package/components/SearchInput.js +315 -0
- package/components/SegmentedControl.js +202 -0
- package/components/Select.js +199 -0
- package/components/SelectDialog.js +255 -0
- package/components/Slider.js +113 -0
- package/components/Snackbar.js +243 -0
- package/components/Stepper.js +281 -0
- package/components/SwipeableListItem.js +179 -0
- package/components/Switch.js +147 -0
- package/components/Table.js +492 -0
- package/components/Tabs.js +125 -0
- package/components/Text.js +141 -0
- package/components/TextField.js +331 -0
- package/components/Toast.js +236 -0
- package/components/TreeView.js +420 -0
- package/components/Video.js +397 -0
- package/components/View.js +140 -0
- package/components/VirtualList.js +120 -0
- package/core/CanvasFramework.js +1271 -0
- package/core/CanvasWork.js +32 -0
- package/core/Component.js +153 -0
- package/core/LogicWorker.js +25 -0
- package/core/WebGLCanvasAdapter.js +1369 -0
- package/features/Column.js +43 -0
- package/features/Grid.js +47 -0
- package/features/LayoutComponent.js +43 -0
- package/features/OpenStreetMap.js +310 -0
- package/features/Positioned.js +33 -0
- package/features/PullToRefresh.js +328 -0
- package/features/Row.js +40 -0
- package/features/SignaturePad.js +257 -0
- package/features/Skeleton.js +84 -0
- package/features/Stack.js +21 -0
- package/index.js +101 -0
- package/manager/AccessibilityManager.js +107 -0
- package/manager/ErrorHandler.js +59 -0
- package/manager/FeatureFlags.js +60 -0
- package/manager/MemoryManager.js +107 -0
- package/manager/PerformanceMonitor.js +84 -0
- package/manager/SecurityManager.js +54 -0
- package/package.json +28 -0
- package/utils/AnimationEngine.js +428 -0
- package/utils/DataStore.js +403 -0
- package/utils/EventBus.js +407 -0
- package/utils/FetchClient.js +74 -0
- package/utils/FormValidator.js +355 -0
- package/utils/GeoLocationService.js +62 -0
- package/utils/I18n.js +207 -0
- package/utils/IndexedDBManager.js +273 -0
- package/utils/OfflineSyncManager.js +342 -0
- package/utils/QueryBuilder.js +478 -0
- package/utils/SafeArea.js +64 -0
- package/utils/SecureStorage.js +289 -0
- package/utils/StateManager.js +207 -0
- package/utils/WebSocketClient.js +66 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import LayoutComponent from './LayoutComponent.js';
|
|
2
|
+
|
|
3
|
+
class Column extends LayoutComponent {
|
|
4
|
+
constructor(framework, options = {}) {
|
|
5
|
+
super(framework, options);
|
|
6
|
+
this.align = options.align || 'start'; // start | center | end | stretch
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
layout() {
|
|
10
|
+
let y = this.y;
|
|
11
|
+
let maxWidth = 0;
|
|
12
|
+
|
|
13
|
+
for (const child of this.children) {
|
|
14
|
+
child.y = y;
|
|
15
|
+
|
|
16
|
+
if (this.align === 'center') {
|
|
17
|
+
child.x = this.x + (this.width - child.width) / 2;
|
|
18
|
+
} else if (this.align === 'end') {
|
|
19
|
+
child.x = this.x + this.width - child.width;
|
|
20
|
+
} else if (this.align === 'stretch') {
|
|
21
|
+
child.x = this.x;
|
|
22
|
+
child.width = this.width;
|
|
23
|
+
} else {
|
|
24
|
+
child.x = this.x;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
y += child.height + this.spacing;
|
|
28
|
+
maxWidth = Math.max(maxWidth, child.width);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
this.height = y - this.y - this.spacing;
|
|
32
|
+
if (!this.width) this.width = maxWidth;
|
|
33
|
+
|
|
34
|
+
// Layout récursif automatique des enfants
|
|
35
|
+
for (const child of this.children) {
|
|
36
|
+
if (typeof child.layoutRecursive === 'function') {
|
|
37
|
+
child.layoutRecursive();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default Column;
|
package/features/Grid.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import LayoutComponent from './LayoutComponent.js';
|
|
2
|
+
|
|
3
|
+
class Grid extends LayoutComponent {
|
|
4
|
+
constructor(framework, options = {}) {
|
|
5
|
+
super(framework, options);
|
|
6
|
+
this.columns = options.columns || 2;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
layout() {
|
|
10
|
+
const cellWidth =
|
|
11
|
+
(this.width - this.spacing * (this.columns - 1)) / this.columns;
|
|
12
|
+
|
|
13
|
+
let x = this.x;
|
|
14
|
+
let y = this.y;
|
|
15
|
+
let col = 0;
|
|
16
|
+
let rowHeight = 0;
|
|
17
|
+
|
|
18
|
+
for (const child of this.children) {
|
|
19
|
+
child.x = x;
|
|
20
|
+
child.y = y;
|
|
21
|
+
child.width = cellWidth;
|
|
22
|
+
|
|
23
|
+
rowHeight = Math.max(rowHeight, child.height);
|
|
24
|
+
|
|
25
|
+
col++;
|
|
26
|
+
if (col === this.columns) {
|
|
27
|
+
col = 0;
|
|
28
|
+
x = this.x;
|
|
29
|
+
y += rowHeight + this.spacing;
|
|
30
|
+
rowHeight = 0;
|
|
31
|
+
} else {
|
|
32
|
+
x += cellWidth + this.spacing;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.height = y - this.y + rowHeight;
|
|
37
|
+
|
|
38
|
+
// Layout récursif automatique des enfants
|
|
39
|
+
for (const child of this.children) {
|
|
40
|
+
if (typeof child.layoutRecursive === 'function') {
|
|
41
|
+
child.layoutRecursive();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default Grid;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import Component from '../core/Component.js';
|
|
2
|
+
|
|
3
|
+
export function makeConstraints(component) {
|
|
4
|
+
return {
|
|
5
|
+
minWidth: 0,
|
|
6
|
+
minHeight: 0,
|
|
7
|
+
maxWidth: component.width || Infinity,
|
|
8
|
+
maxHeight: component.height || Infinity
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class LayoutComponent extends Component {
|
|
13
|
+
constructor(framework, options = {}) {
|
|
14
|
+
super(framework, options);
|
|
15
|
+
this.children = options.children || [];
|
|
16
|
+
this.spacing = options.spacing || 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
add(child) {
|
|
20
|
+
this.children.push(child);
|
|
21
|
+
return child;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
layout() {
|
|
25
|
+
// À implémenter par Grid / Row / Column
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Layout récursif automatique
|
|
29
|
+
layoutRecursive() {
|
|
30
|
+
this.layout(); // calcul des positions/taille des enfants directs
|
|
31
|
+
for (const child of this.children) {
|
|
32
|
+
if (typeof child.layoutRecursive === 'function') {
|
|
33
|
+
child.layoutRecursive(); // appel récursif
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
draw(ctx) {
|
|
39
|
+
// Ne dessine rien par défaut, chaque enfant dessinera le sien
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default LayoutComponent;
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import Component from '../core/Component.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Composant OpenStreetMap pour Canvas avec pan, zoom, multi-touch et markers.
|
|
5
|
+
* @class
|
|
6
|
+
* @extends Component
|
|
7
|
+
* @property {number} lat - Latitude du centre de la carte
|
|
8
|
+
* @property {number} lng - Longitude du centre de la carte
|
|
9
|
+
* @property {number} zoom - Niveau de zoom initial
|
|
10
|
+
* @property {number} tileSize - Taille des tuiles en pixels
|
|
11
|
+
* @property {Object} tiles - Cache des tuiles { "z_x_y": {img: Image, loaded: boolean} }
|
|
12
|
+
* @property {number} offsetX - Décalage X pour le drag
|
|
13
|
+
* @property {number} offsetY - Décalage Y pour le drag
|
|
14
|
+
* @property {boolean} dragging - Si le drag est actif
|
|
15
|
+
* @property {Object|null} lastTouch - Dernier point de touch/mouse {x, y}
|
|
16
|
+
* @property {number} velocityX - Vitesse de glissement X pour inertie
|
|
17
|
+
* @property {number} velocityY - Vitesse de glissement Y pour inertie
|
|
18
|
+
* @property {number} friction - Friction pour inertie
|
|
19
|
+
* @property {number} inertiaThreshold - Seuil minimal pour l'inertie
|
|
20
|
+
* @property {Array} markers - Liste des markers {lat, lng, icon}
|
|
21
|
+
* @property {number} maxZoom - Zoom max
|
|
22
|
+
* @property {number} minZoom - Zoom min
|
|
23
|
+
* @property {number|null} lastTouchDistance - Distance entre deux touches pour pinch zoom
|
|
24
|
+
*/
|
|
25
|
+
class OpenStreetMap extends Component {
|
|
26
|
+
/**
|
|
27
|
+
* Crée une instance de OpenStreetMap
|
|
28
|
+
* @param {CanvasFramework} framework - Framework parent
|
|
29
|
+
* @param {Object} [options={}] - Options de configuration
|
|
30
|
+
* @param {number} [options.lat=0] - Latitude initiale
|
|
31
|
+
* @param {number} [options.lng=0] - Longitude initiale
|
|
32
|
+
* @param {number} [options.zoom=2] - Niveau de zoom initial
|
|
33
|
+
* @param {number} [options.tileSize=256] - Taille des tuiles
|
|
34
|
+
* @param {Array} [options.markers=[]] - Liste de markers {lat, lng, icon}
|
|
35
|
+
*/
|
|
36
|
+
constructor(framework, options = {}) {
|
|
37
|
+
super(framework, options);
|
|
38
|
+
|
|
39
|
+
this.x = options.x || 0;
|
|
40
|
+
this.y = options.y || 0;
|
|
41
|
+
this.width = options.width || framework.canvas.width;
|
|
42
|
+
this.height = options.height || framework.canvas.height;
|
|
43
|
+
|
|
44
|
+
this.lat = options.lat || 0;
|
|
45
|
+
this.lng = options.lng || 0;
|
|
46
|
+
this.zoom = options.zoom || 2;
|
|
47
|
+
this.tileSize = options.tileSize || 256;
|
|
48
|
+
this.tiles = {};
|
|
49
|
+
|
|
50
|
+
this.offsetX = 0;
|
|
51
|
+
this.offsetY = 0;
|
|
52
|
+
this.dragging = false;
|
|
53
|
+
this.lastTouch = null;
|
|
54
|
+
|
|
55
|
+
this.velocityX = 0;
|
|
56
|
+
this.velocityY = 0;
|
|
57
|
+
this.friction = 0.92;
|
|
58
|
+
this.inertiaThreshold = 0.1;
|
|
59
|
+
|
|
60
|
+
this.markers = options.markers || [];
|
|
61
|
+
this.maxZoom = 19;
|
|
62
|
+
this.minZoom = 1;
|
|
63
|
+
|
|
64
|
+
this.lastTouchDistance = null;
|
|
65
|
+
|
|
66
|
+
// Événements souris
|
|
67
|
+
framework.canvas.addEventListener('mousedown', this.onMouseDown.bind(this));
|
|
68
|
+
framework.canvas.addEventListener('mousemove', this.onMouseMove.bind(this));
|
|
69
|
+
framework.canvas.addEventListener('mouseup', this.onMouseUp.bind(this));
|
|
70
|
+
|
|
71
|
+
// Événements tactiles
|
|
72
|
+
framework.canvas.addEventListener('touchstart', this.onTouchStart.bind(this), { passive: false });
|
|
73
|
+
framework.canvas.addEventListener('touchmove', this.onTouchMove.bind(this), { passive: false });
|
|
74
|
+
framework.canvas.addEventListener('touchend', this.onTouchEnd.bind(this));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Convertit lat/lng en coordonnées de tuiles
|
|
79
|
+
* @param {number} lat - Latitude
|
|
80
|
+
* @param {number} lng - Longitude
|
|
81
|
+
* @param {number} zoom - Niveau de zoom
|
|
82
|
+
* @returns {Object} {x, y} coordonnées de la tuile
|
|
83
|
+
*/
|
|
84
|
+
latLngToTileXY(lat, lng, zoom) {
|
|
85
|
+
const n = Math.pow(2, zoom);
|
|
86
|
+
const x = n * ((lng + 180) / 360);
|
|
87
|
+
const latRad = lat * Math.PI / 180;
|
|
88
|
+
const y = n * (1 - (Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI)) / 2;
|
|
89
|
+
return { x, y };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Convertit coordonnées de tuiles en lat/lng
|
|
94
|
+
* @param {number} x - Coordonnée X de la tuile
|
|
95
|
+
* @param {number} y - Coordonnée Y de la tuile
|
|
96
|
+
* @param {number} zoom - Niveau de zoom
|
|
97
|
+
* @returns {Object} {lat, lng}
|
|
98
|
+
*/
|
|
99
|
+
tileXYToLatLng(x, y, zoom) {
|
|
100
|
+
const n = Math.pow(2, zoom);
|
|
101
|
+
const lng = x / n * 360 - 180;
|
|
102
|
+
const latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * y / n)));
|
|
103
|
+
const lat = latRad * 180 / Math.PI;
|
|
104
|
+
return { lat, lng };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Charge une tuile OpenStreetMap
|
|
109
|
+
* @param {number} z - Zoom
|
|
110
|
+
* @param {number} x - Coordonnée X
|
|
111
|
+
* @param {number} y - Coordonnée Y
|
|
112
|
+
* @returns {Object} {img: Image, loaded: boolean}
|
|
113
|
+
*/
|
|
114
|
+
loadTile(z, x, y) {
|
|
115
|
+
const key = `${z}_${x}_${y}`;
|
|
116
|
+
if (this.tiles[key]) return this.tiles[key];
|
|
117
|
+
const img = new Image();
|
|
118
|
+
img.crossOrigin = 'anonymous';
|
|
119
|
+
img.src = `https://tile.openstreetmap.org/${z}/${x}/${y}.png`;
|
|
120
|
+
this.tiles[key] = { img, loaded: false };
|
|
121
|
+
img.onload = () => { this.tiles[key].loaded = true; this.framework.markComponentDirty(this); };
|
|
122
|
+
return this.tiles[key];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Dessine la carte et les markers
|
|
127
|
+
* @param {CanvasRenderingContext2D} ctx - Contexte Canvas
|
|
128
|
+
*/
|
|
129
|
+
draw(ctx) {
|
|
130
|
+
ctx.save();
|
|
131
|
+
|
|
132
|
+
// CORRECTION : Arrondir le zoom pour les tuiles
|
|
133
|
+
const zoomLevel = Math.floor(this.zoom);
|
|
134
|
+
|
|
135
|
+
const { x: tileX, y: tileY } = this.latLngToTileXY(this.lat, this.lng, zoomLevel);
|
|
136
|
+
const tilesX = Math.ceil(this.width / this.tileSize) + 2;
|
|
137
|
+
const tilesY = Math.ceil(this.height / this.tileSize) + 2;
|
|
138
|
+
const startX = Math.floor(tileX - tilesX / 2);
|
|
139
|
+
const startY = Math.floor(tileY - tilesY / 2);
|
|
140
|
+
|
|
141
|
+
for (let dx = 0; dx < tilesX; dx++) {
|
|
142
|
+
for (let dy = 0; dy < tilesY; dy++) {
|
|
143
|
+
const tx = startX + dx;
|
|
144
|
+
const ty = startY + dy;
|
|
145
|
+
|
|
146
|
+
// Utiliser zoomLevel arrondi
|
|
147
|
+
const tile = this.loadTile(zoomLevel, tx, ty);
|
|
148
|
+
const px = (tx - tileX) * this.tileSize + this.width / 2 + this.offsetX;
|
|
149
|
+
const py = (ty - tileY) * this.tileSize + this.height / 2 + this.offsetY;
|
|
150
|
+
|
|
151
|
+
if (tile.loaded) {
|
|
152
|
+
ctx.drawImage(tile.img, px, py, this.tileSize, this.tileSize);
|
|
153
|
+
} else {
|
|
154
|
+
ctx.fillStyle = '#E0E0E0';
|
|
155
|
+
ctx.fillRect(px, py, this.tileSize, this.tileSize);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Dessiner les markers avec le zoom arrondi aussi
|
|
161
|
+
for (let marker of this.markers) {
|
|
162
|
+
const { x, y } = this.latLngToTileXY(marker.lat, marker.lng, zoomLevel);
|
|
163
|
+
const screenX = (x - tileX) * this.tileSize + this.width / 2 + this.offsetX;
|
|
164
|
+
const screenY = (y - tileY) * this.tileSize + this.height / 2 + this.offsetY;
|
|
165
|
+
|
|
166
|
+
if (marker.icon) {
|
|
167
|
+
ctx.drawImage(marker.icon, screenX - marker.icon.width / 2, screenY - marker.icon.height, marker.icon.width, marker.icon.height);
|
|
168
|
+
} else {
|
|
169
|
+
ctx.fillStyle = 'red';
|
|
170
|
+
ctx.beginPath();
|
|
171
|
+
ctx.arc(screenX, screenY, 6, 0, 2 * Math.PI);
|
|
172
|
+
ctx.fill();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
ctx.restore();
|
|
177
|
+
this.updateInertia();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** @private */
|
|
181
|
+
handleDragStart(x, y) {
|
|
182
|
+
this.dragging = true;
|
|
183
|
+
this.lastTouch = { x, y };
|
|
184
|
+
this.velocityX = 0;
|
|
185
|
+
this.velocityY = 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** @private */
|
|
189
|
+
handleDragMove(x, y) {
|
|
190
|
+
if (!this.dragging) return;
|
|
191
|
+
const dx = x - this.lastTouch.x;
|
|
192
|
+
const dy = y - this.lastTouch.y;
|
|
193
|
+
this.offsetX += dx;
|
|
194
|
+
this.offsetY += dy;
|
|
195
|
+
this.velocityX = dx;
|
|
196
|
+
this.velocityY = dy;
|
|
197
|
+
this.lastTouch = { x, y };
|
|
198
|
+
this.framework.markComponentDirty(this);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** @private */
|
|
202
|
+
handleDragEnd() {
|
|
203
|
+
this.dragging = false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** @private */
|
|
207
|
+
updateInertia() {
|
|
208
|
+
if (!this.dragging && (Math.abs(this.velocityX) > this.inertiaThreshold || Math.abs(this.velocityY) > this.inertiaThreshold)) {
|
|
209
|
+
this.offsetX += this.velocityX;
|
|
210
|
+
this.offsetY += this.velocityY;
|
|
211
|
+
this.velocityX *= this.friction;
|
|
212
|
+
this.velocityY *= this.friction;
|
|
213
|
+
this.framework.markComponentDirty(this);
|
|
214
|
+
} else {
|
|
215
|
+
this.velocityX = 0;
|
|
216
|
+
this.velocityY = 0;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** @private */
|
|
221
|
+
updateCenter() {
|
|
222
|
+
const { x: tileX, y: tileY } = this.latLngToTileXY(this.lat, this.lng, this.zoom);
|
|
223
|
+
const newTileX = tileX - this.offsetX / this.tileSize;
|
|
224
|
+
const newTileY = tileY - this.offsetY / this.tileSize;
|
|
225
|
+
const { lat, lng } = this.tileXYToLatLng(newTileX, newTileY, this.zoom);
|
|
226
|
+
this.lat = lat;
|
|
227
|
+
this.lng = lng;
|
|
228
|
+
this.offsetX = 0;
|
|
229
|
+
this.offsetY = 0;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/** @param {MouseEvent} e */
|
|
233
|
+
onMouseDown(e) { this.handleDragStart(e.clientX, e.clientY); }
|
|
234
|
+
|
|
235
|
+
/** @param {MouseEvent} e */
|
|
236
|
+
onMouseMove(e) { this.handleDragMove(e.clientX, e.clientY); }
|
|
237
|
+
|
|
238
|
+
/** @param {MouseEvent} e */
|
|
239
|
+
onMouseUp(e) { this.handleDragEnd(); this.updateCenter(); }
|
|
240
|
+
|
|
241
|
+
/** @param {TouchEvent} e */
|
|
242
|
+
onTouchStart(e) {
|
|
243
|
+
e.preventDefault();
|
|
244
|
+
if (e.touches.length === 1) {
|
|
245
|
+
const t = e.touches[0];
|
|
246
|
+
this.handleDragStart(t.clientX, t.clientY);
|
|
247
|
+
} else if (e.touches.length === 2) {
|
|
248
|
+
this.lastTouchDistance = this.getTouchDistance(e.touches[0], e.touches[1]);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** @param {TouchEvent} e */
|
|
253
|
+
onTouchMove(e) {
|
|
254
|
+
e.preventDefault();
|
|
255
|
+
if (e.touches.length === 1) {
|
|
256
|
+
const t = e.touches[0];
|
|
257
|
+
this.handleDragMove(t.clientX, t.clientY);
|
|
258
|
+
} else if (e.touches.length === 2) {
|
|
259
|
+
const newDistance = this.getTouchDistance(e.touches[0], e.touches[1]);
|
|
260
|
+
if (this.lastTouchDistance) {
|
|
261
|
+
const zoomChange = Math.log2(newDistance / this.lastTouchDistance);
|
|
262
|
+
this.zoom += zoomChange;
|
|
263
|
+
this.zoom = Math.min(this.maxZoom, Math.max(this.minZoom, this.zoom));
|
|
264
|
+
this.lastTouchDistance = newDistance;
|
|
265
|
+
this.framework.markComponentDirty(this);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** @param {TouchEvent} e */
|
|
271
|
+
onTouchEnd(e) {
|
|
272
|
+
e.preventDefault();
|
|
273
|
+
this.handleDragEnd();
|
|
274
|
+
this.updateCenter();
|
|
275
|
+
this.lastTouchDistance = null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Calcule la distance entre deux touches
|
|
280
|
+
* @param {Touch} t1
|
|
281
|
+
* @param {Touch} t2
|
|
282
|
+
* @returns {number} distance en pixels
|
|
283
|
+
*/
|
|
284
|
+
getTouchDistance(t1, t2) {
|
|
285
|
+
const dx = t2.clientX - t1.clientX;
|
|
286
|
+
const dy = t2.clientY - t1.clientY;
|
|
287
|
+
return Math.sqrt(dx*dx + dy*dy);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Vérifie si un point est dans le composant
|
|
292
|
+
* @param {number} x
|
|
293
|
+
* @param {number} y
|
|
294
|
+
* @returns {boolean}
|
|
295
|
+
*/
|
|
296
|
+
isPointInside(x, y) {
|
|
297
|
+
return x >= this.x && x <= this.x + this.width && y >= this.y && y <= this.y + this.height;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Ajoute un marker à la carte
|
|
302
|
+
* @param {Object} marker - {lat, lng, icon}
|
|
303
|
+
*/
|
|
304
|
+
addMarker(marker) {
|
|
305
|
+
this.markers.push(marker);
|
|
306
|
+
this.framework.markComponentDirty(this);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export default OpenStreetMap;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import Component from '../core/Component.js';
|
|
2
|
+
|
|
3
|
+
class Positioned extends Component {
|
|
4
|
+
constructor(framework, options = {}) {
|
|
5
|
+
super(framework, options);
|
|
6
|
+
this.child = options.child;
|
|
7
|
+
this.left = options.left;
|
|
8
|
+
this.top = options.top;
|
|
9
|
+
this.right = options.right;
|
|
10
|
+
this.bottom = options.bottom;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
layout() {
|
|
14
|
+
if (!this.child) return;
|
|
15
|
+
|
|
16
|
+
// Position selon les contraintes
|
|
17
|
+
if (this.left !== undefined) this.child.x = this.left;
|
|
18
|
+
else if (this.right !== undefined) this.child.x = this.width - this.child.width - this.right;
|
|
19
|
+
else this.child.x = 0;
|
|
20
|
+
|
|
21
|
+
if (this.top !== undefined) this.child.y = this.top;
|
|
22
|
+
else if (this.bottom !== undefined) this.child.y = this.height - this.child.height - this.bottom;
|
|
23
|
+
else this.child.y = 0;
|
|
24
|
+
|
|
25
|
+
this.child.layout && this.child.layout();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
draw(ctx) {
|
|
29
|
+
this.child && this.child.draw(ctx);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default Positioned;
|