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 }
|
let lastSize = { width: 320, height: 200 }
|
||||||
|
|
||||||
// Текущий режим докинга. null = окно свободно (не прижато).
|
// Текущий режим докинга. null = окно свободно (не прижато).
|
||||||
// Пока не null — позиция пересчитывается при каждом изменении viewport
|
// Пока не null — состояние (размер+позиция) пересчитывается при каждом изменении
|
||||||
// (через поллинг, т.к. события viewportchange в API нет), иначе
|
// viewport (через поллинг, т.к. события viewportchange в API нет), иначе
|
||||||
// canvas-space-точка "уплывает" на экране при смене зума/пана.
|
// canvas-space-точка "уплывает" на экране при смене зума/пана.
|
||||||
let dockedPosition: DockPosition | null = null
|
let dockedPosition: DockPosition | null = null
|
||||||
|
|
||||||
@@ -25,36 +25,52 @@ export default function () {
|
|||||||
// верх шапки, поэтому полная высота окна = (PLUGIN_HEADER_PX + height) / zoom.
|
// верх шапки, поэтому полная высота окна = (PLUGIN_HEADER_PX + height) / zoom.
|
||||||
const PLUGIN_HEADER_PX = 38
|
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 выше нужного.
|
// сторону viewport (по ширине тянутся лево/право/углы, по высоте — верх/низ/углы),
|
||||||
function applyDock (position: DockPosition, width: number, height: number) {
|
// центр НЕ растягивает (оставляет текущий размер). Возвращает размер (экранные px)
|
||||||
|
// и позицию (canvas-space, прижатую к нужному краю).
|
||||||
|
function computeDock (position: DockPosition, currentWidth: number, currentHeight: number) {
|
||||||
const b = figma.viewport.bounds
|
const b = figma.viewport.bounds
|
||||||
const zoom = figma.viewport.zoom || 1
|
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 x = b.x
|
||||||
let y = b.y
|
let y = b.y
|
||||||
// По горизонтали
|
|
||||||
if (position === 'top-center' || position === 'center' || position === 'bottom-center') {
|
if (position === 'top-center' || position === 'center' || position === 'bottom-center') {
|
||||||
x = b.x + (b.width - wCanvas) / 2
|
x = b.x + (b.width - wCanvas) / 2
|
||||||
} else if (position === 'top-right' || position === 'center-right' || position === 'bottom-right') {
|
} else if (position === 'top-right' || position === 'center-right' || position === 'bottom-right') {
|
||||||
x = b.x + b.width - wCanvas
|
x = b.x + b.width - wCanvas
|
||||||
}
|
}
|
||||||
// По вертикали (hCanvas учитывает шапку окна)
|
|
||||||
if (position === 'center-left' || position === 'center' || position === 'center-right') {
|
if (position === 'center-left' || position === 'center' || position === 'center-right') {
|
||||||
y = b.y + (b.height - hCanvas) / 2
|
y = b.y + (b.height - hCanvas) / 2
|
||||||
} else if (position === 'bottom-left' || position === 'bottom-center' || position === 'bottom-right') {
|
} else if (position === 'bottom-left' || position === 'bottom-center' || position === 'bottom-right') {
|
||||||
y = b.y + b.height - hCanvas
|
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),
|
// События viewportchange в Figma API НЕТ (нельзя подписаться через figma.on),
|
||||||
// поэтому опрашиваем bounds и репозиционируем только когда он реально изменился.
|
// поэтому опрашиваем bounds и пере-применяем док только когда он реально изменился.
|
||||||
// Это предотвращает "уплывание" окна на экране: reposition() работает в canvas-space,
|
|
||||||
// а bounds при зуме сжимается/растягивается — без пересчёта точка уезжает.
|
|
||||||
let lastBoundsKey = ''
|
let lastBoundsKey = ''
|
||||||
const dockInterval = setInterval(function () {
|
const dockInterval = setInterval(function () {
|
||||||
if (dockedPosition === null) return
|
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 }) {
|
on('DOCK_WINDOW', function (payload: { position: DockPosition; width: number; height: number }) {
|
||||||
const { position } = payload
|
const { position } = payload
|
||||||
const { width, height } = clampWindowSize(payload)
|
|
||||||
lastSize = { width, height }
|
|
||||||
figma.ui.resize(width, height)
|
|
||||||
dockedPosition = position
|
dockedPosition = position
|
||||||
lastBoundsKey = '' // форсируем пересчёт на ближайшем тике
|
lastBoundsKey = '' // форсируем пересчёт на ближайшем тике
|
||||||
applyDock(position, width, height)
|
applyDock(position, payload.width, payload.height)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Останавливаем поллинг при закрытии плагина, чтобы не висел в памяти.
|
// Останавливаем поллинг при закрытии плагина, чтобы не висел в памяти.
|
||||||
|
|||||||
Reference in new issue
Block a user