@sequent-org/moodboard 1.2.85 → 1.2.87
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,21 +1,4 @@
|
|
|
1
1
|
import { BaseTool } from '../BaseTool.js';
|
|
2
|
-
import iCursorSvg from '../../assets/icons/i-cursor.svg?raw';
|
|
3
|
-
|
|
4
|
-
// Масштабируем I-курсор в 2 раза меньше (примерно 16x32 с сохранением пропорций 1:2)
|
|
5
|
-
const _scaledICursorSvg = (() => {
|
|
6
|
-
try {
|
|
7
|
-
if (!/\bwidth="/i.test(iCursorSvg)) {
|
|
8
|
-
return iCursorSvg.replace('<svg ', '<svg width="16px" height="32px" ');
|
|
9
|
-
}
|
|
10
|
-
return iCursorSvg
|
|
11
|
-
.replace(/width="[^"]+"/i, 'width="16px"')
|
|
12
|
-
.replace(/height="[^"]+"/i, 'height="32px"');
|
|
13
|
-
} catch (_) {
|
|
14
|
-
return iCursorSvg;
|
|
15
|
-
}
|
|
16
|
-
})();
|
|
17
|
-
|
|
18
|
-
const TEXT_CURSOR = `url("data:image/svg+xml;charset=utf-8,${encodeURIComponent(_scaledICursorSvg)}") 0 0, text`;
|
|
19
2
|
|
|
20
3
|
/**
|
|
21
4
|
* Инструмент для создания и редактирования текстовых объектов
|
|
@@ -23,7 +6,7 @@ const TEXT_CURSOR = `url("data:image/svg+xml;charset=utf-8,${encodeURIComponent(
|
|
|
23
6
|
export class TextTool extends BaseTool {
|
|
24
7
|
constructor(eventBus) {
|
|
25
8
|
super('text', eventBus);
|
|
26
|
-
|
|
9
|
+
this.cursor = 'text';
|
|
27
10
|
this.hotkey = 't';
|
|
28
11
|
this.container = null;
|
|
29
12
|
|
|
@@ -49,13 +32,35 @@ export class TextTool extends BaseTool {
|
|
|
49
32
|
onMouseDown(event) {
|
|
50
33
|
super.onMouseDown(event);
|
|
51
34
|
|
|
35
|
+
// Учитываем масштаб браузера для корректных координат
|
|
36
|
+
const zoomFactor = this._getBrowserZoomFactor();
|
|
37
|
+
const correctedX = event.x * zoomFactor;
|
|
38
|
+
const correctedY = event.y * zoomFactor;
|
|
39
|
+
|
|
40
|
+
// Диагностика координат для решения проблемы с масштабом браузера
|
|
41
|
+
console.log('🔍 TextTool onMouseDown диагностика:', {
|
|
42
|
+
'исходные event.x/y': { x: event.x, y: event.y },
|
|
43
|
+
'zoomFactor': zoomFactor,
|
|
44
|
+
'исправленные x/y': { x: correctedX, y: correctedY },
|
|
45
|
+
'window.devicePixelRatio': window.devicePixelRatio,
|
|
46
|
+
'масштаб браузера': window.outerWidth / window.innerWidth
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Преобразуем экранные координаты в мировые координаты PIXI
|
|
50
|
+
const worldPoint = this._toWorld(correctedX, correctedY);
|
|
51
|
+
|
|
52
|
+
console.log('🔍 Преобразование координат:', {
|
|
53
|
+
'исправленные': { x: correctedX, y: correctedY },
|
|
54
|
+
'мировые': worldPoint
|
|
55
|
+
});
|
|
56
|
+
|
|
52
57
|
// Проверяем, кликнули ли на существующий текстовый объект
|
|
53
|
-
const hitObject = this.getTextObjectAt(
|
|
58
|
+
const hitObject = this.getTextObjectAt(correctedX, correctedY);
|
|
54
59
|
|
|
55
60
|
if (hitObject) {
|
|
56
61
|
this.startEditing(hitObject, event);
|
|
57
62
|
} else {
|
|
58
|
-
this.createNewText(
|
|
63
|
+
this.createNewText(worldPoint.x, worldPoint.y);
|
|
59
64
|
}
|
|
60
65
|
}
|
|
61
66
|
|
|
@@ -132,10 +137,19 @@ export class TextTool extends BaseTool {
|
|
|
132
137
|
/**
|
|
133
138
|
* Создание HTML input для редактирования текста
|
|
134
139
|
*/
|
|
135
|
-
createTextInput(
|
|
140
|
+
createTextInput(worldX, worldY, initialText) {
|
|
136
141
|
// Удаляем предыдущий input если есть
|
|
137
142
|
this.removeTextInput();
|
|
138
143
|
|
|
144
|
+
// Преобразуем мировые координаты в экранные для HTML элемента
|
|
145
|
+
const screenPos = this._toScreen(worldX, worldY);
|
|
146
|
+
|
|
147
|
+
console.log('🔍 Позиционирование input:', {
|
|
148
|
+
'мировые координаты': { x: worldX, y: worldY },
|
|
149
|
+
'экранные координаты': screenPos,
|
|
150
|
+
'container': this.getContainer()
|
|
151
|
+
});
|
|
152
|
+
|
|
139
153
|
// Создаем новый input
|
|
140
154
|
this.textInput = document.createElement('textarea');
|
|
141
155
|
this.textInput.value = initialText;
|
|
@@ -144,8 +158,8 @@ export class TextTool extends BaseTool {
|
|
|
144
158
|
// Стили input
|
|
145
159
|
Object.assign(this.textInput.style, {
|
|
146
160
|
position: 'absolute',
|
|
147
|
-
left: `${x}px`,
|
|
148
|
-
top: `${y}px`,
|
|
161
|
+
left: `${screenPos.x}px`,
|
|
162
|
+
top: `${screenPos.y}px`,
|
|
149
163
|
border: '2px solid #007bff',
|
|
150
164
|
borderRadius: '4px',
|
|
151
165
|
padding: '4px 8px',
|
|
@@ -401,14 +415,15 @@ export class TextTool extends BaseTool {
|
|
|
401
415
|
*/
|
|
402
416
|
activate(pixiApp) {
|
|
403
417
|
super.activate();
|
|
418
|
+
this.pixiApp = pixiApp;
|
|
404
419
|
|
|
405
420
|
// Устанавливаем контейнер для размещения input
|
|
406
421
|
if (pixiApp && pixiApp.view && pixiApp.view.parentElement) {
|
|
407
422
|
this.setContainer(pixiApp.view.parentElement);
|
|
408
423
|
}
|
|
409
|
-
// Устанавливаем
|
|
424
|
+
// Устанавливаем стандартный текстовый курсор
|
|
410
425
|
if (pixiApp && pixiApp.view) {
|
|
411
|
-
pixiApp.view.style.cursor =
|
|
426
|
+
pixiApp.view.style.cursor = 'text';
|
|
412
427
|
}
|
|
413
428
|
}
|
|
414
429
|
|
|
@@ -421,6 +436,50 @@ export class TextTool extends BaseTool {
|
|
|
421
436
|
}
|
|
422
437
|
}
|
|
423
438
|
|
|
439
|
+
/**
|
|
440
|
+
* Преобразование экранных координат в мировые (worldLayer)
|
|
441
|
+
*/
|
|
442
|
+
_toWorld(x, y) {
|
|
443
|
+
if (!this.pixiApp || !this.pixiApp.stage) return { x, y };
|
|
444
|
+
const world = this.pixiApp.stage.getChildByName && this.pixiApp.stage.getChildByName('worldLayer');
|
|
445
|
+
if (!world || !world.toLocal) return { x, y };
|
|
446
|
+
const p = new PIXI.Point(x, y);
|
|
447
|
+
const local = world.toLocal(p);
|
|
448
|
+
return { x: local.x, y: local.y };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Преобразование мировых координат в экранные для HTML элементов
|
|
453
|
+
*/
|
|
454
|
+
_toScreen(worldX, worldY) {
|
|
455
|
+
if (!this.pixiApp || !this.pixiApp.stage) return { x: worldX, y: worldY };
|
|
456
|
+
const world = this.pixiApp.stage.getChildByName && this.pixiApp.stage.getChildByName('worldLayer');
|
|
457
|
+
if (!world || !world.toGlobal) return { x: worldX, y: worldY };
|
|
458
|
+
const p = new PIXI.Point(worldX, worldY);
|
|
459
|
+
const global = world.toGlobal(p);
|
|
460
|
+
|
|
461
|
+
// Обратная коррекция для HTML элементов с учетом масштаба браузера
|
|
462
|
+
const zoomFactor = this._getBrowserZoomFactor();
|
|
463
|
+
return {
|
|
464
|
+
x: global.x / zoomFactor,
|
|
465
|
+
y: global.y / zoomFactor
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Получение коэффициента масштабирования браузера
|
|
471
|
+
*/
|
|
472
|
+
_getBrowserZoomFactor() {
|
|
473
|
+
// Определяем масштаб браузера разными способами
|
|
474
|
+
const outerInnerRatio = window.outerWidth / window.innerWidth;
|
|
475
|
+
const devicePixelRatio = window.devicePixelRatio || 1;
|
|
476
|
+
|
|
477
|
+
// Используемый подход: соотношение размеров окна браузера
|
|
478
|
+
const zoomFactor = outerInnerRatio;
|
|
479
|
+
|
|
480
|
+
return Math.max(0.1, Math.min(5, zoomFactor)); // Ограничиваем разумными пределами
|
|
481
|
+
}
|
|
482
|
+
|
|
424
483
|
/**
|
|
425
484
|
* Установка настроек текста по умолчанию
|
|
426
485
|
*/
|