@sequent-org/moodboard 1.2.114 → 1.2.116
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
package/src/tools/ToolManager.js
CHANGED
|
@@ -34,6 +34,7 @@ export class ToolManager {
|
|
|
34
34
|
// Последняя позиция курсора относительно контейнера (CSS-пиксели)
|
|
35
35
|
this.lastMousePos = null;
|
|
36
36
|
this.isMouseOverContainer = false;
|
|
37
|
+
this._originalPixiCursorStyles = null;
|
|
37
38
|
|
|
38
39
|
// Устанавливаем курсор по умолчанию на контейнер, если инструмент ещё не активирован
|
|
39
40
|
if (this.container) {
|
|
@@ -77,6 +78,7 @@ export class ToolManager {
|
|
|
77
78
|
if (typeof this.activeTool.activate === 'function') {
|
|
78
79
|
this.activeTool.activate(this.pixiApp);
|
|
79
80
|
}
|
|
81
|
+
this.syncActiveToolCursor();
|
|
80
82
|
|
|
81
83
|
return true;
|
|
82
84
|
}
|
|
@@ -148,7 +150,9 @@ export class ToolManager {
|
|
|
148
150
|
this.isMouseOverContainer = true;
|
|
149
151
|
if (!this.activeTool) {
|
|
150
152
|
this.container.style.cursor = DEFAULT_CURSOR;
|
|
153
|
+
return;
|
|
151
154
|
}
|
|
155
|
+
this.syncActiveToolCursor();
|
|
152
156
|
});
|
|
153
157
|
this.container.addEventListener('mouseleave', () => { this.isMouseOverContainer = false; });
|
|
154
158
|
// Убираем отдельные слушатели aux-pan на контейнере, чтобы не дублировать mousedown/mouseup
|
|
@@ -305,9 +309,55 @@ export class ToolManager {
|
|
|
305
309
|
// Если временно активирован pan, проксируем движение именно ему
|
|
306
310
|
if (this.temporaryTool === 'pan' && this.activeTool?.name === 'pan') {
|
|
307
311
|
this.activeTool.onMouseMove(event);
|
|
312
|
+
this.syncActiveToolCursor();
|
|
308
313
|
return;
|
|
309
314
|
}
|
|
310
315
|
this.activeTool.onMouseMove(event);
|
|
316
|
+
this.syncActiveToolCursor();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
isCursorLockedToActiveTool() {
|
|
320
|
+
return !!this.activeTool && this.activeTool.name !== 'select';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
getPixiCursorStyles() {
|
|
324
|
+
const renderer = this.pixiApp && this.pixiApp.renderer;
|
|
325
|
+
if (!renderer) return null;
|
|
326
|
+
const events = renderer.events || (renderer.plugins && renderer.plugins.interaction);
|
|
327
|
+
return events && events.cursorStyles ? events.cursorStyles : null;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
getActiveToolCursor() {
|
|
331
|
+
const cursor = this.activeTool && this.activeTool.cursor;
|
|
332
|
+
if (typeof cursor === 'string' && cursor.length > 0) return cursor;
|
|
333
|
+
return DEFAULT_CURSOR;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
syncActiveToolCursor() {
|
|
337
|
+
const cursorStyles = this.getPixiCursorStyles();
|
|
338
|
+
const lockCursor = this.isCursorLockedToActiveTool();
|
|
339
|
+
const activeCursor = this.getActiveToolCursor();
|
|
340
|
+
|
|
341
|
+
if (cursorStyles && !this._originalPixiCursorStyles) {
|
|
342
|
+
this._originalPixiCursorStyles = {
|
|
343
|
+
pointer: cursorStyles.pointer,
|
|
344
|
+
default: cursorStyles.default
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (cursorStyles) {
|
|
349
|
+
if (lockCursor) {
|
|
350
|
+
cursorStyles.pointer = activeCursor;
|
|
351
|
+
cursorStyles.default = activeCursor;
|
|
352
|
+
} else if (this._originalPixiCursorStyles) {
|
|
353
|
+
cursorStyles.pointer = this._originalPixiCursorStyles.pointer;
|
|
354
|
+
cursorStyles.default = this._originalPixiCursorStyles.default;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (lockCursor && this.pixiApp && this.pixiApp.view) {
|
|
359
|
+
this.pixiApp.view.style.cursor = activeCursor;
|
|
360
|
+
}
|
|
311
361
|
}
|
|
312
362
|
|
|
313
363
|
handleMouseUp(e) {
|
|
@@ -329,6 +379,7 @@ export class ToolManager {
|
|
|
329
379
|
return;
|
|
330
380
|
}
|
|
331
381
|
this.activeTool.onMouseUp(event);
|
|
382
|
+
this.syncActiveToolCursor();
|
|
332
383
|
}
|
|
333
384
|
|
|
334
385
|
handleDoubleClick(e) {
|
|
@@ -614,5 +665,12 @@ export class ToolManager {
|
|
|
614
665
|
try { window.removeEventListener('wheel', this._onWindowWheel); } catch (_) {}
|
|
615
666
|
this._onWindowWheel = null;
|
|
616
667
|
}
|
|
668
|
+
|
|
669
|
+
const cursorStyles = this.getPixiCursorStyles();
|
|
670
|
+
if (cursorStyles && this._originalPixiCursorStyles) {
|
|
671
|
+
cursorStyles.pointer = this._originalPixiCursorStyles.pointer;
|
|
672
|
+
cursorStyles.default = this._originalPixiCursorStyles.default;
|
|
673
|
+
}
|
|
674
|
+
this._originalPixiCursorStyles = null;
|
|
617
675
|
}
|
|
618
676
|
}
|
|
@@ -8,8 +8,9 @@ import * as PIXI from 'pixi.js';
|
|
|
8
8
|
export class DrawingTool extends BaseTool {
|
|
9
9
|
constructor(eventBus) {
|
|
10
10
|
super('draw', eventBus);
|
|
11
|
-
this.cursor = 'crosshair';
|
|
12
11
|
this.hotkey = 'd';
|
|
12
|
+
const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'><path d='M4 20 L20 4 L28 12 L12 28 L4 28 Z' fill='black'/></svg>`;
|
|
13
|
+
this.cursor = `url("data:image/svg+xml;utf8,${encodeURIComponent(svg)}") 4 28, crosshair`;
|
|
13
14
|
|
|
14
15
|
// Состояние рисования
|
|
15
16
|
this.isDrawing = false;
|
|
@@ -50,12 +51,7 @@ export class DrawingTool extends BaseTool {
|
|
|
50
51
|
super.activate();
|
|
51
52
|
this.app = app;
|
|
52
53
|
this.world = this._getWorldLayer();
|
|
53
|
-
|
|
54
|
-
const svg = `<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'><path d='M4 20 L20 4 L28 12 L12 28 L4 28 Z' fill='black'/></svg>`;
|
|
55
|
-
// Горячая точка ставим в реальный нижний левый угол фигуры (x=4, y=28),
|
|
56
|
-
// чтобы линия выходила прямо из карандаша без зазора.
|
|
57
|
-
const url = `url("data:image/svg+xml;utf8,${encodeURIComponent(svg)}") 4 28, crosshair`;
|
|
58
|
-
if (this.app && this.app.view) this.app.view.style.cursor = url;
|
|
54
|
+
this.setCursor();
|
|
59
55
|
}
|
|
60
56
|
|
|
61
57
|
deactivate() {
|
|
@@ -70,6 +66,12 @@ export class DrawingTool extends BaseTool {
|
|
|
70
66
|
this.world = null;
|
|
71
67
|
}
|
|
72
68
|
|
|
69
|
+
setCursor() {
|
|
70
|
+
if (this.app && this.app.view) {
|
|
71
|
+
this.app.view.style.cursor = this.cursor;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
73
75
|
onMouseDown(event) {
|
|
74
76
|
super.onMouseDown(event);
|
|
75
77
|
if (!this.world) this.world = this._getWorldLayer();
|
|
@@ -320,6 +322,12 @@ export class DrawingTool extends BaseTool {
|
|
|
320
322
|
return { x: local.x, y: local.y };
|
|
321
323
|
}
|
|
322
324
|
|
|
325
|
+
_worldToGlobal(x, y) {
|
|
326
|
+
if (!this.world || typeof this.world.toGlobal !== 'function') return { x, y };
|
|
327
|
+
const global = this.world.toGlobal(new PIXI.Point(x, y));
|
|
328
|
+
return { x: global.x, y: global.y };
|
|
329
|
+
}
|
|
330
|
+
|
|
323
331
|
_getWorldLayer() {
|
|
324
332
|
if (!this.app || !this.app.stage) return null;
|
|
325
333
|
const world = this.app.stage.getChildByName && this.app.stage.getChildByName('worldLayer');
|
|
@@ -331,12 +339,14 @@ export class DrawingTool extends BaseTool {
|
|
|
331
339
|
const req = { objects: [] };
|
|
332
340
|
this.emit('get:all:objects', req);
|
|
333
341
|
const objects = req.objects || [];
|
|
342
|
+
const prevGlobal = this._worldToGlobal(prev.x, prev.y);
|
|
343
|
+
const currGlobal = this._worldToGlobal(p.x, p.y);
|
|
334
344
|
// Радиус воздействия ластика (связан с отображаемой толщиной)
|
|
335
345
|
const radius = 8;
|
|
336
|
-
const segMinX = Math.min(
|
|
337
|
-
const segMaxX = Math.max(
|
|
338
|
-
const segMinY = Math.min(
|
|
339
|
-
const segMaxY = Math.max(
|
|
346
|
+
const segMinX = Math.min(prevGlobal.x, currGlobal.x) - radius;
|
|
347
|
+
const segMaxX = Math.max(prevGlobal.x, currGlobal.x) + radius;
|
|
348
|
+
const segMinY = Math.min(prevGlobal.y, currGlobal.y) - radius;
|
|
349
|
+
const segMaxY = Math.max(prevGlobal.y, currGlobal.y) + radius;
|
|
340
350
|
|
|
341
351
|
for (const item of objects) {
|
|
342
352
|
const id = item.id;
|
|
@@ -366,8 +376,8 @@ export class DrawingTool extends BaseTool {
|
|
|
366
376
|
const scaleY = baseH ? (b.height / baseH) : 1;
|
|
367
377
|
const eraserThresh = Math.max(6, (props.strokeWidth || 2) / 2 + radius);
|
|
368
378
|
// трансформируем сегмент ластика в локальные координаты фигуры
|
|
369
|
-
const localPrev = pixi.toLocal(new PIXI.Point(
|
|
370
|
-
const localCurr = pixi.toLocal(new PIXI.Point(
|
|
379
|
+
const localPrev = pixi.toLocal(new PIXI.Point(prevGlobal.x, prevGlobal.y));
|
|
380
|
+
const localCurr = pixi.toLocal(new PIXI.Point(currGlobal.x, currGlobal.y));
|
|
371
381
|
// Проверяем пересечение с каждым отрезком рисунка
|
|
372
382
|
for (let i = 0; i < pts.length - 1 && !intersects; i++) {
|
|
373
383
|
const ax = pts[i].x * scaleX;
|
|
@@ -47,6 +47,7 @@ export class PlacementTool extends BaseTool {
|
|
|
47
47
|
// Обновляем курсор в зависимости от pending
|
|
48
48
|
if (this.app && this.app.view) {
|
|
49
49
|
const cur = this._getPendingCursor();
|
|
50
|
+
this.cursor = cur;
|
|
50
51
|
this.app.view.style.cursor = (cur === 'default') ? '' : cur;
|
|
51
52
|
}
|
|
52
53
|
// При выборе текста заставляем pointer вести себя как текстовый курсор
|
|
@@ -147,7 +148,8 @@ export class PlacementTool extends BaseTool {
|
|
|
147
148
|
this.world = this._getWorldLayer();
|
|
148
149
|
// Курсор в зависимости от типа размещаемого объекта
|
|
149
150
|
if (this.app && this.app.view) {
|
|
150
|
-
this.
|
|
151
|
+
this.cursor = this._getPendingCursor();
|
|
152
|
+
this.app.view.style.cursor = this.cursor;
|
|
151
153
|
// Добавляем обработчик движения мыши для "призрака"
|
|
152
154
|
this.app.view.addEventListener('mousemove', this._onMouseMove.bind(this));
|
|
153
155
|
}
|
|
@@ -492,7 +494,8 @@ export class PlacementTool extends BaseTool {
|
|
|
492
494
|
|
|
493
495
|
startFrameDrawMode() {
|
|
494
496
|
// Курсор при рисовании фрейма
|
|
495
|
-
|
|
497
|
+
this.cursor = 'crosshair';
|
|
498
|
+
if (this.app && this.app.view) this.app.view.style.cursor = this.cursor;
|
|
496
499
|
}
|
|
497
500
|
|
|
498
501
|
_onFrameDrawMove(event) {
|
|
@@ -941,13 +944,15 @@ export class PlacementTool extends BaseTool {
|
|
|
941
944
|
const cursorSize = 24;
|
|
942
945
|
const url = encodeURI(src);
|
|
943
946
|
// Используем CSS cursor с изображением, если поддерживается
|
|
944
|
-
this.
|
|
947
|
+
this.cursor = `url(${url}) ${Math.floor(cursorSize/2)} ${Math.floor(cursorSize/2)}, default`;
|
|
948
|
+
this.app.view.style.cursor = this.cursor;
|
|
945
949
|
}
|
|
946
950
|
} catch (_) {}
|
|
947
951
|
} else {
|
|
948
952
|
// Для эмоджи используем стандартный курсор
|
|
949
953
|
if (this.app && this.app.view) {
|
|
950
|
-
this.
|
|
954
|
+
this.cursor = 'crosshair';
|
|
955
|
+
this.app.view.style.cursor = this.cursor;
|
|
951
956
|
}
|
|
952
957
|
}
|
|
953
958
|
}
|