@sequent-org/moodboard 1.4.51 → 1.4.53

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": "@sequent-org/moodboard",
3
- "version": "1.4.51",
3
+ "version": "1.4.53",
4
4
  "type": "module",
5
5
  "description": "Interactive moodboard",
6
6
  "main": "./src/index.js",
@@ -4,6 +4,7 @@ import { BoardService } from '../../services/BoardService.js';
4
4
  import { ZoomPanController } from '../../services/ZoomPanController.js';
5
5
  import { ZOrderManager } from '../../services/ZOrderManager.js';
6
6
  import { FrameService } from '../../services/FrameService.js';
7
+ import { ImageAttachmentService } from '../../services/ImageAttachmentService.js';
7
8
  import { Events } from '../events/Events.js';
8
9
 
9
10
  export async function initializeCore(core) {
@@ -20,6 +21,8 @@ export async function initializeCore(core) {
20
21
  core.zOrder.attach();
21
22
  core.frameService = new FrameService(core.eventBus, core.pixi, core.state);
22
23
  core.frameService.attach();
24
+ core.imageAttachmentService = new ImageAttachmentService(core.eventBus, core.pixi, core.state);
25
+ core.imageAttachmentService.attach();
23
26
 
24
27
  setupViewportResize(core);
25
28
 
@@ -169,12 +169,14 @@ function tryCreateConnectorStyleCommand(core, object, objectId, updates) {
169
169
  const { style, start, end } = updates.properties;
170
170
 
171
171
  // Проверяем, что в updates.properties только style/start/end (и нет посторонних ключей)
172
- const allowedKeys = new Set(['style', 'start', 'end', 'locked']);
172
+ const allowedKeys = new Set(['style', 'start', 'end', 'locked', 'lockMode', 'lockedByFrame']);
173
173
  const hasOtherKeys = Object.keys(updates.properties).some(k => !allowedKeys.has(k));
174
174
  if (hasOtherKeys) return false;
175
175
  // Должно быть хотя бы одно из: style, start, end
176
176
  if (!style && start === undefined && end === undefined
177
- && updates.properties.locked === undefined) return false;
177
+ && updates.properties.locked === undefined
178
+ && updates.properties.lockMode === undefined
179
+ && updates.properties.lockedByFrame === undefined) return false;
178
180
 
179
181
  const commandUpdates = {};
180
182
  if (style !== undefined) commandUpdates.style = style;
package/src/core/index.js CHANGED
@@ -39,7 +39,7 @@ export class CoreMoodBoard {
39
39
  autoSave: false,
40
40
  width: this.container.clientWidth || 800,
41
41
  height: this.container.clientHeight || 600,
42
- backgroundColor: 0xF5F5F5,
42
+ backgroundColor: 0xFFFFFF,
43
43
  ...options
44
44
  };
45
45
 
@@ -624,7 +624,7 @@ export class CoreMoodBoard {
624
624
  const app = this.pixi?.app;
625
625
  const rendererBg = app?.renderer?.background?.color ?? app?.renderer?.backgroundColor;
626
626
  const toHex = (num) => {
627
- try { return '#' + Number(num >>> 0).toString(16).padStart(6, '0'); } catch (_) { return '#F5F5F5'; }
627
+ try { return '#' + Number(num >>> 0).toString(16).padStart(6, '0'); } catch (_) { return '#FFFFFF'; }
628
628
  };
629
629
  const world = this.pixi?.worldLayer || app?.stage;
630
630
  const currentZoom = Math.max(0.02, Math.min(5, world?.scale?.x || 1));
@@ -653,7 +653,7 @@ export class CoreMoodBoard {
653
653
  })();
654
654
 
655
655
  const settings = {
656
- backgroundColor: toHex(rendererBg ?? 0xF5F5F5),
656
+ backgroundColor: toHex(rendererBg ?? 0xFFFFFF),
657
657
  grid: gridSettings || undefined,
658
658
  zoom: { min: 0.1, max: 5.0, default: 1.0, current: currentZoom },
659
659
  pan: currentPan,
@@ -59,7 +59,7 @@ export function injectCriticalStyles() {
59
59
  width: 100%;
60
60
  height: 100%;
61
61
  overflow: hidden;
62
- background: #f0f6fc;
62
+ background: #ffffff;
63
63
  }
64
64
 
65
65
  .moodboard-workspace__toolbar {
@@ -1,7 +1,7 @@
1
1
  import { Events } from '../../core/events/Events.js';
2
2
 
3
3
  const BOARD_DEFAULTS = {
4
- backgroundColor: '#daeefb',
4
+ backgroundColor: '#ffffff',
5
5
  grid: { type: 'cross' },
6
6
  };
7
7
 
@@ -17,10 +17,10 @@ export class FrameObject {
17
17
  this.height = this.objectData.height || 100;
18
18
  // Берем стили рамки из CSS-переменных, с дефолтом
19
19
  const rootStyles = (typeof window !== 'undefined') ? getComputedStyle(document.documentElement) : null;
20
- const cssBorderWidth = rootStyles ? parseFloat(rootStyles.getPropertyValue('--frame-border-width') || '4') : 4;
20
+ const cssBorderWidth = rootStyles ? parseFloat(rootStyles.getPropertyValue('--frame-border-width') || '5') : 5;
21
21
  const cssCornerRadius = rootStyles ? parseFloat(rootStyles.getPropertyValue('--frame-corner-radius') || '6') : 6;
22
22
  const cssBorderColor = rootStyles ? rootStyles.getPropertyValue('--frame-border-color').trim() : '';
23
- this.borderWidth = Number.isFinite(cssBorderWidth) ? cssBorderWidth : 4;
23
+ this.borderWidth = Number.isFinite(cssBorderWidth) ? cssBorderWidth : 5;
24
24
  // Используем backgroundColor из данных объекта, если есть, иначе белый
25
25
  this.fillColor = this.objectData.backgroundColor || this.objectData.properties?.backgroundColor || 0xFFFFFF;
26
26
  // Режим заливки: solid | solid-bordered | outline
@@ -50,6 +50,10 @@ export class FrameObject {
50
50
  // Под-контейнер: масштаб компенсирует зум, поэтому заголовок всегда одного размера на экране
51
51
  this.titleLayer = new PIXI.Container();
52
52
  this.titleLayer.eventMode = 'none'; // не перехватывать указатель
53
+ const _frameObjectId = this.objectData.id || '';
54
+ if (_frameObjectId) {
55
+ this.titleLayer.name = `mb-frame-title-${_frameObjectId}`;
56
+ }
53
57
 
54
58
  this.titleBg = new PIXI.Graphics();
55
59
  this.titleLayer.addChild(this.titleBg);
@@ -60,7 +64,7 @@ export class FrameObject {
60
64
  fill: 0x333333,
61
65
  fontWeight: '500'
62
66
  });
63
- this.titleText.anchor.set(0, 0);
67
+ this.titleText.anchor.set(0, 0.5);
64
68
  this.titleLayer.addChild(this.titleText);
65
69
 
66
70
  this.container.addChild(this.titleLayer);
@@ -141,7 +145,10 @@ export class FrameObject {
141
145
 
142
146
  _onSelectionAdd(data) {
143
147
  const myId = this.objectData?.id ?? this.container?._mb?.objectId;
144
- if (data?.object === myId) this.setBorderVisible(false);
148
+ if (data?.object !== myId) return;
149
+ // В outline цветная рамка — основной визуал фрейма, не дублирует синюю рамку выделения.
150
+ if (this.bgMode === 'outline') return;
151
+ this.setBorderVisible(false);
145
152
  }
146
153
 
147
154
  _onSelectionRemove(data) {
@@ -276,37 +283,29 @@ export class FrameObject {
276
283
  const fillColor = typeof color === 'number' ? color : 0xFFFFFF;
277
284
 
278
285
  if (bgMode === 'solid') {
279
- // Сплошная заливка выбранным цветом, стандартная тонкая серая рамка
280
- if (showStroke) {
281
- try {
282
- g.lineStyle({ width: this.borderWidth, color: this.strokeColor, alpha: 1, alignment: 1 });
283
- } catch (e) {
284
- g.lineStyle(this.borderWidth, this.strokeColor, 1);
285
- }
286
- }
286
+ // Сплошная заливка без собственной рамки (рамка выделения — отдельно)
287
287
  g.beginFill(fillColor, 1);
288
288
  g.drawRoundedRect(0, 0, Math.max(0, width), Math.max(0, height), this.cornerRadius);
289
289
  g.endFill();
290
290
  } else if (bgMode === 'solid-bordered') {
291
- // Контрастный белый/светлый фон + толстая цветная рамка
291
+ // Фон = выбранный цвет; рамка = S=100,V=50 от того же тона палитры
292
+ const borderColor = this._pickBorderColor(fillColor);
292
293
  if (showStroke) {
293
294
  try {
294
- g.lineStyle({ width: this.borderWidth * 2, color: fillColor, alpha: 1, alignment: 1 });
295
+ g.lineStyle({ width: this.borderWidth, color: borderColor, alpha: 1, alignment: 0 });
295
296
  } catch (e) {
296
- g.lineStyle(this.borderWidth * 2, fillColor, 1);
297
+ g.lineStyle(this.borderWidth, borderColor, 1);
297
298
  }
298
299
  }
299
- g.beginFill(0xFFFFFF, 0.92);
300
+ g.beginFill(fillColor, 1);
300
301
  g.drawRoundedRect(0, 0, Math.max(0, width), Math.max(0, height), this.cornerRadius);
301
302
  g.endFill();
302
303
  } else {
303
- // outline: прозрачный фон, рамка цвета выбранного цвета
304
- if (showStroke) {
305
- try {
306
- g.lineStyle({ width: this.borderWidth, color: fillColor, alpha: 1, alignment: 1 });
307
- } catch (e) {
308
- g.lineStyle(this.borderWidth, fillColor, 1);
309
- }
304
+ // outline: прозрачный фон, рамка цвета выбранного цвета (всегда видна, в т.ч. при фокусе)
305
+ try {
306
+ g.lineStyle({ width: this.borderWidth, color: fillColor, alpha: 1, alignment: 1 });
307
+ } catch (e) {
308
+ g.lineStyle(this.borderWidth, fillColor, 1);
310
309
  }
311
310
  g.beginFill(0x000000, 0);
312
311
  g.drawRoundedRect(0, 0, Math.max(0, width), Math.max(0, height), this.cornerRadius);
@@ -352,7 +351,7 @@ export class FrameObject {
352
351
 
353
352
  // Высота подложки в базовых пикселях: baseFontSize + 4px сверху + 4px снизу
354
353
  const labelBaseH = this.baseFontSize + 8;
355
- const gap = 4; // зазор между нижним краем подписи и верхней границей фрейма
354
+ const gap = 5; // зазор между нижним краем подписи и верхней границей фрейма
356
355
 
357
356
  // Позиционируем над фреймом (y=0 — верхний край фрейма в локальных координатах контейнера)
358
357
  this.titleLayer.x = 0;
@@ -372,6 +371,143 @@ export class FrameObject {
372
371
  this._redrawTitleBg();
373
372
  }
374
373
 
374
+ /**
375
+ * YIQ-контраст: возвращает 0x000000 или 0xFFFFFF — читаемый цвет на заданном фоне
376
+ * @param {number} hexInt Цвет фона (PIXI hex)
377
+ * @returns {number}
378
+ */
379
+ _pickContrastColor(hexInt) {
380
+ const r = (hexInt >> 16) & 0xFF;
381
+ const g = (hexInt >> 8) & 0xFF;
382
+ const b = hexInt & 0xFF;
383
+ const yiq = (r * 299 + g * 587 + b * 114) / 1000;
384
+ const darkness = (1 - yiq / 255) * 100;
385
+ return darkness >= 35 ? 0xFFFFFF : 0x000000;
386
+ }
387
+
388
+ /**
389
+ * Цвет рамки в режиме solid-bordered.
390
+ * Крайне правый по горизонтали (S=100) и по середине вертикали (V=50)
391
+ * в палитре того же тона, что и фон.
392
+ * Белый фон: крайне левый по горизонтали (S=0), середина по вертикали (V=50) → нейтральный серый.
393
+ * Прочие ахроматические цвета (серый, чёрный — s=0): чёрный.
394
+ * @param {number} hexInt Цвет фона (PIXI hex)
395
+ * @returns {number}
396
+ */
397
+ _pickBorderColor(hexInt) {
398
+ const r = (hexInt >> 16) & 0xFF;
399
+ const g = (hexInt >> 8) & 0xFF;
400
+ const b = hexInt & 0xFF;
401
+ if (r === 255 && g === 255 && b === 255) {
402
+ return 0x808080;
403
+ }
404
+ const { h, s } = this._rgbToHsv(r, g, b);
405
+ if (s === 0) {
406
+ return 0x000000;
407
+ }
408
+ return this._hsvToHex(h, 100, 50);
409
+ }
410
+
411
+ /**
412
+ * Цвет текста заголовка фрейма.
413
+ * Для почти-белых тонированных фонов (яркость < 5 по шкале 0=белый…100=чёрный,
414
+ * но не чистый белый) берёт тон из той же палитры с макс. насыщенностью (S=100, V=50).
415
+ * Иначе — обычный YIQ-контраст (чёрный/белый).
416
+ * @param {number} hexInt Цвет фона (PIXI hex)
417
+ * @returns {number}
418
+ */
419
+ _pickTitleTextColor(hexInt) {
420
+ const r = (hexInt >> 16) & 0xFF;
421
+ const g = (hexInt >> 8) & 0xFF;
422
+ const b = hexInt & 0xFF;
423
+
424
+ // Чистый белый фон — исключение: чёрный текст
425
+ if (r === 255 && g === 255 && b === 255) {
426
+ return 0x000000;
427
+ }
428
+
429
+ // Яркость по шкале 0 (белый) → 100 (чёрный) через HSL-lightness
430
+ const darkness = 100 - ((Math.max(r, g, b) + Math.min(r, g, b)) / 2 / 255) * 100;
431
+ if (darkness >= 5) {
432
+ return this._pickContrastColor(hexInt);
433
+ }
434
+
435
+ // Почти-белый тонированный фон: та же палитра, правый край по центру (S=100, V=50)
436
+ const { h } = this._rgbToHsv(r, g, b);
437
+ return this._hsvToHex(h, 100, 50);
438
+ }
439
+
440
+ /**
441
+ * @param {number} r 0..255
442
+ * @param {number} g 0..255
443
+ * @param {number} b 0..255
444
+ * @returns {{ h: number, s: number, v: number }} h 0..360, s/v 0..100
445
+ */
446
+ _rgbToHsv(r, g, b) {
447
+ const rn = r / 255, gn = g / 255, bn = b / 255;
448
+ const max = Math.max(rn, gn, bn);
449
+ const min = Math.min(rn, gn, bn);
450
+ const d = max - min;
451
+ let h = 0;
452
+ if (d !== 0) {
453
+ if (max === rn) {
454
+ h = ((gn - bn) / d) % 6;
455
+ } else if (max === gn) {
456
+ h = (bn - rn) / d + 2;
457
+ } else {
458
+ h = (rn - gn) / d + 4;
459
+ }
460
+ h *= 60;
461
+ if (h < 0) {
462
+ h += 360;
463
+ }
464
+ }
465
+ const s = max === 0 ? 0 : (d / max) * 100;
466
+ return { h, s, v: max * 100 };
467
+ }
468
+
469
+ /**
470
+ * @param {number} h 0..360
471
+ * @param {number} s 0..100
472
+ * @param {number} v 0..100
473
+ * @returns {number} PIXI hex
474
+ */
475
+ _hsvToHex(h, s, v) {
476
+ const sn = s / 100, vn = v / 100;
477
+ const c = vn * sn;
478
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
479
+ const m = vn - c;
480
+ let rp = 0, gp = 0, bp = 0;
481
+ if (h < 60) {
482
+ rp = c; gp = x;
483
+ } else if (h < 120) {
484
+ rp = x; gp = c;
485
+ } else if (h < 180) {
486
+ gp = c; bp = x;
487
+ } else if (h < 240) {
488
+ gp = x; bp = c;
489
+ } else if (h < 300) {
490
+ rp = x; bp = c;
491
+ } else {
492
+ rp = c; bp = x;
493
+ }
494
+ const r = Math.round((rp + m) * 255);
495
+ const g = Math.round((gp + m) * 255);
496
+ const b = Math.round((bp + m) * 255);
497
+ return (r << 16) | (g << 8) | b;
498
+ }
499
+
500
+ /**
501
+ * Возвращает цвета плашки заголовка в формате CSS-строк для HTML-инпута
502
+ * @returns {{ bgCss: string, textCss: string }}
503
+ */
504
+ getTitleColors() {
505
+ const bg = this.fillColor;
506
+ const text = this._pickTitleTextColor(bg);
507
+ const toHex = (n) => '#' + n.toString(16).padStart(6, '0');
508
+ return { bgCss: toHex(bg), textCss: toHex(text) };
509
+ }
510
+
375
511
  /**
376
512
  * Нарисовать скруглённую подложку под текущую ширину текста
377
513
  */
@@ -381,6 +517,13 @@ export class FrameObject {
381
517
  const padH = 8; // горизонтальный отступ с каждой стороны
382
518
  const padV = 4; // вертикальный отступ с каждой стороны
383
519
 
520
+ // Фон плашки = цвет фрейма (fill или border в зависимости от bgMode — всегда this.fillColor)
521
+ const bgColor = typeof this.fillColor === 'number' ? this.fillColor : 0xFFFFFF;
522
+ const textColor = this._pickTitleTextColor(bgColor);
523
+
524
+ // Обновляем цвет текста под контраст фона
525
+ this.titleText.style.fill = textColor;
526
+
384
527
  // Измеряем текст в базовых единицах
385
528
  const style = new PIXI.TextStyle({
386
529
  fontFamily: this.titleText.style.fontFamily,
@@ -394,18 +537,14 @@ export class FrameObject {
394
537
 
395
538
  const g = this.titleBg;
396
539
  g.clear();
397
- try {
398
- g.lineStyle({ width: 1, color: this.strokeColor, alpha: 1 });
399
- } catch (_) {
400
- g.lineStyle(1, this.strokeColor, 1);
401
- }
402
- g.beginFill(0xFFFFFF, 1);
540
+ g.lineStyle(0);
541
+ g.beginFill(bgColor, 1);
403
542
  g.drawRoundedRect(0, 0, bgW, bgH, 6);
404
543
  g.endFill();
405
544
 
406
- // Текст внутри подложки
545
+ // Текст внутри подложки — по вертикали по центру (anchor.y = 0.5)
407
546
  this.titleText.x = padH;
408
- this.titleText.y = padV;
547
+ this.titleText.y = Math.round(bgH / 2);
409
548
  }
410
549
 
411
550
  /**
@@ -86,14 +86,40 @@ export class FrameService {
86
86
  }
87
87
  }
88
88
  }
89
+ const lockedChanged = data.updates.properties.locked !== undefined;
90
+ const lockModeChanged = data.updates.properties.lockMode !== undefined;
91
+ if (lockedChanged || lockModeChanged) {
92
+ const obj = this.state.state.objects.find(o => o.id === data.objectId);
93
+ if (obj && obj.type === 'frame') {
94
+ const newLocked = lockedChanged
95
+ ? !!data.updates.properties.locked
96
+ : !!(obj.properties && obj.properties.locked);
97
+ const newLockMode = lockModeChanged
98
+ ? data.updates.properties.lockMode
99
+ : (obj.properties && obj.properties.lockMode) || 'frame';
100
+ const children = this._getFrameChildren(obj.id);
101
+ for (const childId of children) {
102
+ const childObj = this.state.state.objects.find(o => o.id === childId);
103
+ if (!childObj) continue;
104
+ childObj.properties = childObj.properties || {};
105
+ if (newLocked && newLockMode === 'frame-and-content') {
106
+ childObj.properties.locked = true;
107
+ childObj.properties.lockedByFrame = true;
108
+ } else if (childObj.properties.lockedByFrame) {
109
+ childObj.properties.locked = false;
110
+ delete childObj.properties.lockedByFrame;
111
+ }
112
+ }
113
+ this.state.markDirty();
114
+ }
115
+ }
89
116
  };
90
117
  this.eventBus.on(Events.Object.StateChanged, this._onStateChanged);
91
118
 
92
- this._onDragStart = (data) => {
119
+ this. _onDragStart = (data) => {
93
120
  const moved = this.state.state.objects.find(o => o.id === data.object);
94
121
  if (moved && moved.type === 'frame') {
95
- // Серый фон
96
- this.pixi.setFrameFill(moved.id, moved.width, moved.height, 0xFAFAFA);
122
+ this._frameDragOriginalFill = moved.backgroundColor ?? moved.properties?.backgroundColor ?? 0xFFFFFF;
97
123
  // Cнимок стартовых позиций по центру PIXI
98
124
  const fp = this.pixi.objects.get(moved.id);
99
125
  this._frameDragFrameStart = { x: fp?.x || 0, y: fp?.y || 0 };
@@ -157,18 +183,25 @@ export class FrameService {
157
183
  const movedObj = this.state.state.objects.find(o => o.id === data.object);
158
184
  if (!movedObj) return;
159
185
  if (movedObj.type === 'frame') {
160
- this.pixi.setFrameFill(movedObj.id, movedObj.width, movedObj.height, 0xFFFFFF);
186
+ const restoreFill = this._frameDragOriginalFill ?? movedObj.backgroundColor ?? movedObj.properties?.backgroundColor ?? 0xFFFFFF;
187
+ this.pixi.setFrameFill(movedObj.id, movedObj.width, movedObj.height, restoreFill);
161
188
  }
162
189
  this._recomputeFrameAttachment(movedObj.id);
163
190
  this._forceFramesBelow();
164
191
  this._frameDragFrameStart = null;
165
192
  this._frameDragChildStart = null;
193
+ this._frameDragOriginalFill = null;
166
194
  if (this._frameHoverId) {
167
195
  const frames = (this.state.state.objects || []).filter(o => o.type === 'frame');
168
196
  const prev = frames.find(fr => fr.id === this._frameHoverId);
169
- if (prev) this.pixi.setFrameFill(prev.id, prev.width, prev.height, 0xFFFFFF);
170
- this._frameHoverId = null;
171
- }
197
+ if (prev) {
198
+ const hoverOriginal = this._frameHoverOriginalFill?.get(this._frameHoverId)
199
+ ?? prev.backgroundColor ?? prev.properties?.backgroundColor ?? 0xFFFFFF;
200
+ this.pixi.setFrameFill(prev.id, prev.width, prev.height, hoverOriginal);
201
+ }
202
+ this._frameHoverId = null;
203
+ this._frameHoverOriginalFill = null;
204
+ }
172
205
  };
173
206
  this.eventBus.on(Events.Tool.DragEnd, this._onDragEnd);
174
207
  }
@@ -192,7 +225,9 @@ export class FrameService {
192
225
  this._onDragEnd = null;
193
226
  this._frameDragFrameStart = null;
194
227
  this._frameDragChildStart = null;
228
+ this._frameDragOriginalFill = null;
195
229
  this._frameHoverId = null;
230
+ this._frameHoverOriginalFill = null;
196
231
  }
197
232
 
198
233
  _getFrameChildren(frameId) {
@@ -234,11 +269,22 @@ export class FrameService {
234
269
  if (hoverId !== this._frameHoverId) {
235
270
  if (this._frameHoverId) {
236
271
  const prev = frames.find(fr => fr.id === this._frameHoverId);
237
- if (prev) this.pixi.setFrameFill(prev.id, prev.width, prev.height, 0xFFFFFF);
272
+ if (prev) {
273
+ const origFill = this._frameHoverOriginalFill?.get(this._frameHoverId)
274
+ ?? prev.backgroundColor ?? prev.properties?.backgroundColor ?? 0xFFFFFF;
275
+ this.pixi.setFrameFill(prev.id, prev.width, prev.height, origFill);
276
+ }
277
+ this._frameHoverOriginalFill?.delete(this._frameHoverId);
238
278
  }
239
279
  if (hoverId) {
240
280
  const cur = frames.find(fr => fr.id === hoverId);
241
- if (cur) this.pixi.setFrameFill(cur.id, cur.width, cur.height, 0xFAFAFA);
281
+ if (cur) {
282
+ if (!this._frameHoverOriginalFill) this._frameHoverOriginalFill = new Map();
283
+ if (!this._frameHoverOriginalFill.has(hoverId)) {
284
+ this._frameHoverOriginalFill.set(hoverId, cur.backgroundColor ?? cur.properties?.backgroundColor ?? 0xFFFFFF);
285
+ }
286
+ this.pixi.setFrameFill(cur.id, cur.width, cur.height, 0xFAFAFA);
287
+ }
242
288
  }
243
289
  this._frameHoverId = hoverId || null;
244
290
  }