@sequent-org/moodboard 1.2.94 → 1.2.96
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
|
@@ -530,7 +530,6 @@ export class PlacementTool extends BaseTool {
|
|
|
530
530
|
const world = this.app.stage.getChildByName && this.app.stage.getChildByName('worldLayer');
|
|
531
531
|
return world || this.app.stage;
|
|
532
532
|
}
|
|
533
|
-
|
|
534
533
|
|
|
535
534
|
/**
|
|
536
535
|
* Обработчик движения мыши для обновления позиции "призрака"
|
|
@@ -1847,9 +1847,21 @@ export class SelectTool extends BaseTool {
|
|
|
1847
1847
|
const toScreen = (wx, wy) => {
|
|
1848
1848
|
const worldLayer = this.textEditor.world || (this.app?.stage);
|
|
1849
1849
|
if (!worldLayer) return { x: wx, y: wy };
|
|
1850
|
+
|
|
1851
|
+
// Используем тот же подход, что в HtmlHandlesLayer для рамки выделения
|
|
1852
|
+
const containerRect = view.parentElement.getBoundingClientRect();
|
|
1853
|
+
const viewRect = view.getBoundingClientRect();
|
|
1854
|
+
const offsetLeft = viewRect.left - containerRect.left;
|
|
1855
|
+
const offsetTop = viewRect.top - containerRect.top;
|
|
1856
|
+
|
|
1857
|
+
// Преобразуем мировые координаты в экранные через toGlobal
|
|
1850
1858
|
const global = worldLayer.toGlobal(new PIXI.Point(wx, wy));
|
|
1851
|
-
|
|
1852
|
-
|
|
1859
|
+
|
|
1860
|
+
// Возвращаем CSS координаты с учетом offset
|
|
1861
|
+
return {
|
|
1862
|
+
x: offsetLeft + global.x,
|
|
1863
|
+
y: offsetTop + global.y
|
|
1864
|
+
};
|
|
1853
1865
|
};
|
|
1854
1866
|
const screenPos = toScreen(position.x, position.y);
|
|
1855
1867
|
|
|
@@ -2032,30 +2044,10 @@ export class SelectTool extends BaseTool {
|
|
|
2032
2044
|
}
|
|
2033
2045
|
} catch (_) {}
|
|
2034
2046
|
|
|
2035
|
-
|
|
2036
|
-
const zoomFactor = this._getBrowserZoomFactor();
|
|
2037
|
-
const correctedBaseLeftPx = baseLeftPx / zoomFactor;
|
|
2038
|
-
const correctedBaseTopPx = baseTopPx / zoomFactor;
|
|
2039
|
-
|
|
2040
|
-
console.log('🔍 Browser zoom detection:', {
|
|
2041
|
-
zoomFactor,
|
|
2042
|
-
basePos: { left: baseLeftPx, top: baseTopPx },
|
|
2043
|
-
correctedPos: { left: correctedBaseLeftPx, top: correctedBaseTopPx },
|
|
2044
|
-
screenPos,
|
|
2045
|
-
window: {
|
|
2046
|
-
innerWidth: window.innerWidth,
|
|
2047
|
-
outerWidth: window.outerWidth,
|
|
2048
|
-
visualViewport: window.visualViewport ? {
|
|
2049
|
-
width: window.visualViewport.width,
|
|
2050
|
-
height: window.visualViewport.height
|
|
2051
|
-
} : null
|
|
2052
|
-
}
|
|
2053
|
-
});
|
|
2054
|
-
|
|
2055
|
-
const leftPx = Math.round(correctedBaseLeftPx - padLeft);
|
|
2047
|
+
const leftPx = Math.round(baseLeftPx - padLeft);
|
|
2056
2048
|
const topPx = create
|
|
2057
|
-
? Math.round(
|
|
2058
|
-
: Math.round(
|
|
2049
|
+
? Math.round(baseTopPx - padTop - (lineHeightPx / 2)) // по клику совмещаем центр строки с точкой клика
|
|
2050
|
+
: Math.round(baseTopPx - padTop); // при редактировании совмещаем верх контента
|
|
2059
2051
|
wrapper.style.left = `${leftPx}px`;
|
|
2060
2052
|
wrapper.style.top = `${topPx}px`;
|
|
2061
2053
|
// Сохраняем CSS-позицию редактора для точной синхронизации при закрытии
|
|
@@ -2824,84 +2816,5 @@ export class SelectTool extends BaseTool {
|
|
|
2824
2816
|
// Обновляем ручки
|
|
2825
2817
|
this.updateResizeHandles();
|
|
2826
2818
|
}
|
|
2827
|
-
|
|
2828
|
-
/**
|
|
2829
|
-
* Получение коэффициента масштабирования браузера
|
|
2830
|
-
*/
|
|
2831
|
-
_getBrowserZoomFactor() {
|
|
2832
|
-
// Новый подход: через измерение canvas размеров
|
|
2833
|
-
try {
|
|
2834
|
-
if (this.app && this.app.view) {
|
|
2835
|
-
const canvas = this.app.view;
|
|
2836
|
-
|
|
2837
|
-
// Получаем CSS размеры и реальные размеры canvas
|
|
2838
|
-
const cssWidth = canvas.clientWidth;
|
|
2839
|
-
const cssHeight = canvas.clientHeight;
|
|
2840
|
-
const realWidth = canvas.width;
|
|
2841
|
-
const realHeight = canvas.height;
|
|
2842
|
-
|
|
2843
|
-
// Проверяем device pixel ratio
|
|
2844
|
-
const dpr = window.devicePixelRatio || 1;
|
|
2845
|
-
|
|
2846
|
-
// Вычисляем ожидаемые реальные размеры с учетом DPR
|
|
2847
|
-
const expectedRealWidth = cssWidth * dpr;
|
|
2848
|
-
const expectedRealHeight = cssHeight * dpr;
|
|
2849
|
-
|
|
2850
|
-
// Если реальные размеры отличаются от ожидаемых, значит есть zoom
|
|
2851
|
-
let zoomFactorW = 1.0;
|
|
2852
|
-
let zoomFactorH = 1.0;
|
|
2853
|
-
|
|
2854
|
-
if (expectedRealWidth > 0) {
|
|
2855
|
-
zoomFactorW = realWidth / expectedRealWidth;
|
|
2856
|
-
}
|
|
2857
|
-
if (expectedRealHeight > 0) {
|
|
2858
|
-
zoomFactorH = realHeight / expectedRealHeight;
|
|
2859
|
-
}
|
|
2860
|
-
|
|
2861
|
-
// Берем среднее значение или наиболее близкое к 1.0
|
|
2862
|
-
const avgZoom = (zoomFactorW + zoomFactorH) / 2;
|
|
2863
|
-
|
|
2864
|
-
console.log('🔍 Canvas zoom detection:', {
|
|
2865
|
-
css: { width: cssWidth, height: cssHeight },
|
|
2866
|
-
real: { width: realWidth, height: realHeight },
|
|
2867
|
-
dpr,
|
|
2868
|
-
expected: { width: expectedRealWidth, height: expectedRealHeight },
|
|
2869
|
-
zoom: { width: zoomFactorW, height: zoomFactorH, avg: avgZoom }
|
|
2870
|
-
});
|
|
2871
|
-
|
|
2872
|
-
// Если обнаружили разумное отклонение от 1.0, используем его
|
|
2873
|
-
if (isFinite(avgZoom) && avgZoom > 0.1 && avgZoom < 10 && Math.abs(avgZoom - 1.0) > 0.01) {
|
|
2874
|
-
return avgZoom;
|
|
2875
|
-
}
|
|
2876
|
-
}
|
|
2877
|
-
} catch (_) {}
|
|
2878
|
-
|
|
2879
|
-
// Fallback: простой метод через screen размеры
|
|
2880
|
-
try {
|
|
2881
|
-
// Сравниваем screen.width с window.innerWidth
|
|
2882
|
-
const screenRatio = screen.width / window.innerWidth;
|
|
2883
|
-
const dpr = window.devicePixelRatio || 1;
|
|
2884
|
-
const expectedRatio = dpr;
|
|
2885
|
-
|
|
2886
|
-
if (Math.abs(screenRatio - expectedRatio) > 0.1) {
|
|
2887
|
-
const zoomFactor = screenRatio / expectedRatio;
|
|
2888
|
-
|
|
2889
|
-
console.log('🔍 Screen zoom detection:', {
|
|
2890
|
-
screen: { width: screen.width, height: screen.height },
|
|
2891
|
-
window: { innerWidth: window.innerWidth, innerHeight: window.innerHeight },
|
|
2892
|
-
screenRatio,
|
|
2893
|
-
expectedRatio,
|
|
2894
|
-
zoomFactor
|
|
2895
|
-
});
|
|
2896
|
-
|
|
2897
|
-
if (isFinite(zoomFactor) && zoomFactor > 0.1 && zoomFactor < 10) {
|
|
2898
|
-
return zoomFactor;
|
|
2899
|
-
}
|
|
2900
|
-
}
|
|
2901
|
-
} catch (_) {}
|
|
2902
|
-
|
|
2903
|
-
console.log('🔍 Zoom detection: using fallback (no zoom correction)');
|
|
2904
|
-
return 1.0; // Fallback: без коррекции
|
|
2905
|
-
}
|
|
2906
2819
|
|
|
2907
2820
|
}
|
|
@@ -1,4 +1,21 @@
|
|
|
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`;
|
|
2
19
|
|
|
3
20
|
/**
|
|
4
21
|
* Инструмент для создания и редактирования текстовых объектов
|
|
@@ -6,7 +23,7 @@ import { BaseTool } from '../BaseTool.js';
|
|
|
6
23
|
export class TextTool extends BaseTool {
|
|
7
24
|
constructor(eventBus) {
|
|
8
25
|
super('text', eventBus);
|
|
9
|
-
|
|
26
|
+
this.cursor = 'text';
|
|
10
27
|
this.hotkey = 't';
|
|
11
28
|
this.container = null;
|
|
12
29
|
|
|
@@ -32,35 +49,13 @@ export class TextTool extends BaseTool {
|
|
|
32
49
|
onMouseDown(event) {
|
|
33
50
|
super.onMouseDown(event);
|
|
34
51
|
|
|
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
|
-
|
|
57
52
|
// Проверяем, кликнули ли на существующий текстовый объект
|
|
58
|
-
const hitObject = this.getTextObjectAt(
|
|
53
|
+
const hitObject = this.getTextObjectAt(event.x, event.y);
|
|
59
54
|
|
|
60
55
|
if (hitObject) {
|
|
61
56
|
this.startEditing(hitObject, event);
|
|
62
57
|
} else {
|
|
63
|
-
this.createNewText(
|
|
58
|
+
this.createNewText(event.x, event.y);
|
|
64
59
|
}
|
|
65
60
|
}
|
|
66
61
|
|
|
@@ -137,19 +132,10 @@ export class TextTool extends BaseTool {
|
|
|
137
132
|
/**
|
|
138
133
|
* Создание HTML input для редактирования текста
|
|
139
134
|
*/
|
|
140
|
-
createTextInput(
|
|
135
|
+
createTextInput(x, y, initialText) {
|
|
141
136
|
// Удаляем предыдущий input если есть
|
|
142
137
|
this.removeTextInput();
|
|
143
138
|
|
|
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
|
-
|
|
153
139
|
// Создаем новый input
|
|
154
140
|
this.textInput = document.createElement('textarea');
|
|
155
141
|
this.textInput.value = initialText;
|
|
@@ -158,8 +144,8 @@ export class TextTool extends BaseTool {
|
|
|
158
144
|
// Стили input
|
|
159
145
|
Object.assign(this.textInput.style, {
|
|
160
146
|
position: 'absolute',
|
|
161
|
-
left: `${
|
|
162
|
-
top: `${
|
|
147
|
+
left: `${x}px`,
|
|
148
|
+
top: `${y}px`,
|
|
163
149
|
border: '2px solid #007bff',
|
|
164
150
|
borderRadius: '4px',
|
|
165
151
|
padding: '4px 8px',
|
|
@@ -415,15 +401,14 @@ export class TextTool extends BaseTool {
|
|
|
415
401
|
*/
|
|
416
402
|
activate(pixiApp) {
|
|
417
403
|
super.activate();
|
|
418
|
-
this.pixiApp = pixiApp;
|
|
419
404
|
|
|
420
405
|
// Устанавливаем контейнер для размещения input
|
|
421
406
|
if (pixiApp && pixiApp.view && pixiApp.view.parentElement) {
|
|
422
407
|
this.setContainer(pixiApp.view.parentElement);
|
|
423
408
|
}
|
|
424
|
-
// Устанавливаем
|
|
409
|
+
// Устанавливаем кастомный курсор на холст
|
|
425
410
|
if (pixiApp && pixiApp.view) {
|
|
426
|
-
pixiApp.view.style.cursor = '
|
|
411
|
+
pixiApp.view.style.cursor = (this.cursor && this.cursor !== 'default') ? this.cursor : '';
|
|
427
412
|
}
|
|
428
413
|
}
|
|
429
414
|
|
|
@@ -436,50 +421,6 @@ export class TextTool extends BaseTool {
|
|
|
436
421
|
}
|
|
437
422
|
}
|
|
438
423
|
|
|
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
|
-
|
|
483
424
|
/**
|
|
484
425
|
* Установка настроек текста по умолчанию
|
|
485
426
|
*/
|
package/src/ui/HtmlTextLayer.js
CHANGED
|
@@ -305,15 +305,42 @@ export class HtmlTextLayer {
|
|
|
305
305
|
el.style.lineHeight = newLH;
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
// Позиция и габариты в
|
|
309
|
-
const
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
308
|
+
// Позиция и габариты в CSS координатах - используем тот же подход что в HtmlHandlesLayer
|
|
309
|
+
const worldLayer = this.core.pixi.worldLayer || this.core.pixi.app.stage;
|
|
310
|
+
const view = this.core.pixi.app.view;
|
|
311
|
+
|
|
312
|
+
if (worldLayer && view && view.parentElement) {
|
|
313
|
+
const containerRect = view.parentElement.getBoundingClientRect();
|
|
314
|
+
const viewRect = view.getBoundingClientRect();
|
|
315
|
+
const offsetLeft = viewRect.left - containerRect.left;
|
|
316
|
+
const offsetTop = viewRect.top - containerRect.top;
|
|
317
|
+
|
|
318
|
+
// Преобразуем мировые координаты в экранные через toGlobal
|
|
319
|
+
const tl = worldLayer.toGlobal(new PIXI.Point(x, y));
|
|
320
|
+
const br = worldLayer.toGlobal(new PIXI.Point(x + w, y + h));
|
|
321
|
+
|
|
322
|
+
// CSS координаты с учетом offset
|
|
323
|
+
const left = offsetLeft + tl.x;
|
|
324
|
+
const top = offsetTop + tl.y;
|
|
325
|
+
const width = Math.max(1, br.x - tl.x);
|
|
326
|
+
const height = Math.max(1, br.y - tl.y);
|
|
327
|
+
|
|
328
|
+
el.style.left = `${left}px`;
|
|
329
|
+
el.style.top = `${top}px`;
|
|
330
|
+
if (w && h) {
|
|
331
|
+
el.style.width = `${width}px`;
|
|
332
|
+
el.style.height = `${height}px`;
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
// Fallback к старому методу
|
|
336
|
+
const left = (tx + s * x) / res;
|
|
337
|
+
const top = (ty + s * y) / res;
|
|
338
|
+
el.style.left = `${left}px`;
|
|
339
|
+
el.style.top = `${top}px`;
|
|
340
|
+
if (w && h) {
|
|
341
|
+
el.style.width = `${Math.max(1, (w * s) / res)}px`;
|
|
342
|
+
el.style.height = `${Math.max(1, (h * s) / res)}px`;
|
|
343
|
+
}
|
|
317
344
|
}
|
|
318
345
|
// Поворот вокруг центра (как у PIXI и HTML-ручек)
|
|
319
346
|
el.style.transformOrigin = 'center center';
|
|
@@ -369,68 +396,7 @@ export class HtmlTextLayer {
|
|
|
369
396
|
position
|
|
370
397
|
});
|
|
371
398
|
}
|
|
372
|
-
} catch (_) { }
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Получение коэффициента масштабирования браузера
|
|
377
|
-
*/
|
|
378
|
-
_getBrowserZoomFactor() {
|
|
379
|
-
// Новый подход: через измерение canvas размеров
|
|
380
|
-
try {
|
|
381
|
-
if (this.core && this.core.pixi && this.core.pixi.app && this.core.pixi.app.view) {
|
|
382
|
-
const canvas = this.core.pixi.app.view;
|
|
383
|
-
|
|
384
|
-
// Получаем CSS размеры и реальные размеры canvas
|
|
385
|
-
const cssWidth = canvas.clientWidth;
|
|
386
|
-
const cssHeight = canvas.clientHeight;
|
|
387
|
-
const realWidth = canvas.width;
|
|
388
|
-
const realHeight = canvas.height;
|
|
389
|
-
|
|
390
|
-
// Проверяем device pixel ratio
|
|
391
|
-
const dpr = window.devicePixelRatio || 1;
|
|
392
|
-
|
|
393
|
-
// Вычисляем ожидаемые реальные размеры с учетом DPR
|
|
394
|
-
const expectedRealWidth = cssWidth * dpr;
|
|
395
|
-
const expectedRealHeight = cssHeight * dpr;
|
|
396
|
-
|
|
397
|
-
// Если реальные размеры отличаются от ожидаемых, значит есть zoom
|
|
398
|
-
let zoomFactorW = 1.0;
|
|
399
|
-
let zoomFactorH = 1.0;
|
|
400
|
-
|
|
401
|
-
if (expectedRealWidth > 0) {
|
|
402
|
-
zoomFactorW = realWidth / expectedRealWidth;
|
|
403
|
-
}
|
|
404
|
-
if (expectedRealHeight > 0) {
|
|
405
|
-
zoomFactorH = realHeight / expectedRealHeight;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Берем среднее значение
|
|
409
|
-
const avgZoom = (zoomFactorW + zoomFactorH) / 2;
|
|
410
|
-
|
|
411
|
-
// Если обнаружили разумное отклонение от 1.0, используем его
|
|
412
|
-
if (isFinite(avgZoom) && avgZoom > 0.1 && avgZoom < 10 && Math.abs(avgZoom - 1.0) > 0.01) {
|
|
413
|
-
return avgZoom;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
399
|
} catch (_) {}
|
|
417
|
-
|
|
418
|
-
// Fallback: простой метод через screen размеры
|
|
419
|
-
try {
|
|
420
|
-
const screenRatio = screen.width / window.innerWidth;
|
|
421
|
-
const dpr = window.devicePixelRatio || 1;
|
|
422
|
-
const expectedRatio = dpr;
|
|
423
|
-
|
|
424
|
-
if (Math.abs(screenRatio - expectedRatio) > 0.1) {
|
|
425
|
-
const zoomFactor = screenRatio / expectedRatio;
|
|
426
|
-
|
|
427
|
-
if (isFinite(zoomFactor) && zoomFactor > 0.1 && zoomFactor < 10) {
|
|
428
|
-
return zoomFactor;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
} catch (_) {}
|
|
432
|
-
|
|
433
|
-
return 1.0; // Fallback: без коррекции
|
|
434
400
|
}
|
|
435
401
|
}
|
|
436
402
|
|