feat: dock buttons stretch window to zone (edge/corner) + reposition; center keeps size
This commit is contained in:
1 parent
5984df1692
commit
a38f7188ee
1 file changed
+33
-20
+33
-20
@@ -9,8 +9,8 @@ export default function () {
|
||||
let lastSize = { width: 320, height: 200 }
|
||||
|
||||
// Текущий режим докинга. null = окно свободно (не прижато).
|
||||
// Пока не null — позиция пересчитывается при каждом изменении viewport
|
||||
// (через поллинг, т.к. события viewportchange в API нет), иначе
|
||||
// Пока не null — состояние (размер+позиция) пересчитывается при каждом изменении
|
||||
// viewport (через поллинг, т.к. события viewportchange в API нет), иначе
|
||||
// canvas-space-точка "уплывает" на экране при смене зума/пана.
|
||||
let dockedPosition: DockPosition | null = null
|
||||
|
||||
@@ -25,36 +25,52 @@ export default function () {
|
||||
// верх шапки, поэтому полная высота окна = (PLUGIN_HEADER_PX + height) / zoom.
|
||||
const PLUGIN_HEADER_PX = 38
|
||||
|
||||
// Прижать окно к краю видимой области canvas (bounds) в canvas-space.
|
||||
// ВАЖНО: reposition()/getPosition() и bounds — в canvas-space (единицы документа),
|
||||
// а figma.ui.resize(width,height) — в ЭКРАННЫХ пикселях. Размер окна в canvas-units =\n // width / zoom (по ширине) и (PLUGIN_HEADER_PX + height) / zoom (по высоте, т.к. окно\n // включает шапку). Без учёта шапки прижатый к низу/центру окно висит на 38px выше нужного.
|
||||
function applyDock (position: DockPosition, width: number, height: number) {
|
||||
// Вычислить целевое состояние окна для позиции докинга:
|
||||
// имитация настоящего дока — края/углы РАСТЯГИВАЮТ окно на всю соответствующую
|
||||
// сторону viewport (по ширине тянутся лево/право/углы, по высоте — верх/низ/углы),
|
||||
// центр НЕ растягивает (оставляет текущий размер). Возвращает размер (экранные px)
|
||||
// и позицию (canvas-space, прижатую к нужному краю).
|
||||
function computeDock (position: DockPosition, currentWidth: number, currentHeight: number) {
|
||||
const b = figma.viewport.bounds
|
||||
const zoom = figma.viewport.zoom || 1
|
||||
const wCanvas = width / zoom
|
||||
const hCanvas = (PLUGIN_HEADER_PX + height) / zoom
|
||||
// Растягивать ли по осям:
|
||||
const stretchX = position !== 'center' // всё кроме центра тянется по ширине
|
||||
const stretchY =
|
||||
position === 'top-left' || position === 'top-center' || position === 'top-right' ||
|
||||
position === 'bottom-left' || position === 'bottom-center' || position === 'bottom-right'
|
||||
const targetW = stretchX ? Math.round(b.width * zoom) : currentWidth
|
||||
const targetH = stretchY ? Math.round(b.height * zoom) - PLUGIN_HEADER_PX : currentHeight
|
||||
// canvas-units от целевого размера
|
||||
const wCanvas = targetW / zoom
|
||||
const hCanvas = (PLUGIN_HEADER_PX + targetH) / zoom
|
||||
let x = b.x
|
||||
let y = b.y
|
||||
// По горизонтали
|
||||
if (position === 'top-center' || position === 'center' || position === 'bottom-center') {
|
||||
x = b.x + (b.width - wCanvas) / 2
|
||||
} else if (position === 'top-right' || position === 'center-right' || position === 'bottom-right') {
|
||||
x = b.x + b.width - wCanvas
|
||||
}
|
||||
// По вертикали (hCanvas учитывает шапку окна)
|
||||
if (position === 'center-left' || position === 'center' || position === 'center-right') {
|
||||
y = b.y + (b.height - hCanvas) / 2
|
||||
} else if (position === 'bottom-left' || position === 'bottom-center' || position === 'bottom-right') {
|
||||
y = b.y + b.height - hCanvas
|
||||
}
|
||||
figma.ui.reposition(x, y)
|
||||
return { width: targetW, height: targetH, x, y }
|
||||
}
|
||||
|
||||
// Поллинг viewport: держим окно прижатым к краю при любом зуме/панорамировании.
|
||||
// Применить состояние докинга: ресайзим окно (растяжение) + reposition (прижатие).
|
||||
// Сначала resize, иначе reposition считает позицию для старого размера.
|
||||
function applyDock (position: DockPosition, width: number, height: number) {
|
||||
const state = computeDock(position, width, height)
|
||||
figma.ui.resize(state.width, state.height)
|
||||
figma.ui.reposition(state.x, state.y)
|
||||
lastSize = { width: state.width, height: state.height }
|
||||
return state
|
||||
}
|
||||
|
||||
// Поллинг viewport: держим окно в док-состоянии (размер+позиция) при любом зуме/панораме.
|
||||
// События viewportchange в Figma API НЕТ (нельзя подписаться через figma.on),
|
||||
// поэтому опрашиваем bounds и репозиционируем только когда он реально изменился.
|
||||
// Это предотвращает "уплывание" окна на экране: reposition() работает в canvas-space,
|
||||
// а bounds при зуме сжимается/растягивается — без пересчёта точка уезжает.
|
||||
// поэтому опрашиваем bounds и пере-применяем док только когда он реально изменился.
|
||||
let lastBoundsKey = ''
|
||||
const dockInterval = setInterval(function () {
|
||||
if (dockedPosition === null) return
|
||||
@@ -92,15 +108,12 @@ export default function () {
|
||||
}
|
||||
})
|
||||
|
||||
// Эмуляция докинга: прижать окно к краям canvas (9 позиций).
|
||||
// Эмуляция докинга: растянуть окно на зону и прижать к её краю (9 позиций).
|
||||
on('DOCK_WINDOW', function (payload: { position: DockPosition; width: number; height: number }) {
|
||||
const { position } = payload
|
||||
const { width, height } = clampWindowSize(payload)
|
||||
lastSize = { width, height }
|
||||
figma.ui.resize(width, height)
|
||||
dockedPosition = position
|
||||
lastBoundsKey = '' // форсируем пересчёт на ближайшем тике
|
||||
applyDock(position, width, height)
|
||||
applyDock(position, payload.width, payload.height)
|
||||
})
|
||||
|
||||
// Останавливаем поллинг при закрытии плагина, чтобы не висел в памяти.
|
||||
|
||||
Reference in new issue
Block a user