картинка масштабируется
parent
84ab8fc59f
commit
c70fd4e98b
@ -1,48 +1,153 @@
|
||||
// konva-utils.js
|
||||
export function attachStageResizer(root, stage) {
|
||||
const observer = new ResizeObserver(() => {
|
||||
if (!stage) return;
|
||||
|
||||
stage.width(root.clientWidth);
|
||||
stage.height(root.clientHeight);
|
||||
stage.batchDraw();
|
||||
});
|
||||
|
||||
observer.observe(root);
|
||||
return observer;
|
||||
}
|
||||
|
||||
// масштабирует картинку по stage
|
||||
export function fitImageToStage(konvaImage, stage) {
|
||||
const img = konvaImage.image();
|
||||
const sw = stage.width();
|
||||
const sh = stage.height();
|
||||
const iw = img.naturalWidth;
|
||||
const ih = img.naturalHeight;
|
||||
const scale = Math.min(sw / iw, sh / ih);
|
||||
|
||||
konvaImage.width(iw);
|
||||
konvaImage.height(ih);
|
||||
konvaImage.scale({ x: scale, y: scale });
|
||||
|
||||
// центрируем
|
||||
konvaImage.x((sw - iw * scale) / 2);
|
||||
konvaImage.y((sh - ih * scale) / 2);
|
||||
}
|
||||
|
||||
export function coverImageToStage(konvaImage, stage) {
|
||||
const img = konvaImage.image();
|
||||
const sw = stage.width();
|
||||
const sh = stage.height();
|
||||
const iw = img.naturalWidth;
|
||||
const ih = img.naturalHeight;
|
||||
const scale = Math.max(sw / iw, sh / ih);
|
||||
|
||||
konvaImage.width(iw);
|
||||
konvaImage.height(ih);
|
||||
konvaImage.scale({ x: scale, y: scale });
|
||||
|
||||
// центрируем и обрезаем по центру
|
||||
konvaImage.x((sw - iw * scale) / 2);
|
||||
konvaImage.y((sh - ih * scale) / 2);
|
||||
}
|
||||
// konva-utils.js
|
||||
|
||||
// attachStageResizer(root, stage, onResize?)
|
||||
export function attachStageResizer(root, stage, onResize) {
|
||||
const observer = new ResizeObserver(() => {
|
||||
if (!stage) return;
|
||||
|
||||
stage.width(root.clientWidth);
|
||||
stage.height(root.clientHeight);
|
||||
stage.batchDraw();
|
||||
|
||||
if (typeof onResize === 'function') {
|
||||
onResize();
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(root);
|
||||
return observer;
|
||||
}
|
||||
|
||||
// fitImageToStage: вписать целиком (contain)
|
||||
export function fitImageToStage(konvaImage, stage) {
|
||||
const img = konvaImage.image();
|
||||
const sw = stage.width();
|
||||
const sh = stage.height();
|
||||
const iw = img.naturalWidth;
|
||||
const ih = img.naturalHeight;
|
||||
const scale = Math.min(sw / iw, sh / ih);
|
||||
|
||||
konvaImage.width(iw);
|
||||
konvaImage.height(ih);
|
||||
konvaImage.scale({ x: scale, y: scale });
|
||||
|
||||
konvaImage.x((sw - iw * scale) / 2);
|
||||
konvaImage.y((sh - ih * scale) / 2);
|
||||
}
|
||||
|
||||
// coverImageToStage: заполнить (cover)
|
||||
export function coverImageToStage(konvaImage, stage) {
|
||||
const img = konvaImage.image();
|
||||
const sw = stage.width();
|
||||
const sh = stage.height();
|
||||
const iw = img.naturalWidth;
|
||||
const ih = img.naturalHeight;
|
||||
const scale = Math.max(sw / iw, sh / ih);
|
||||
|
||||
konvaImage.width(iw);
|
||||
konvaImage.height(ih);
|
||||
konvaImage.scale({ x: scale, y: scale });
|
||||
|
||||
konvaImage.x((sw - iw * scale) / 2);
|
||||
konvaImage.y((sh - ih * scale) / 2);
|
||||
}
|
||||
|
||||
// возвращает { scale, x, y } для cover
|
||||
export function calcCoverTransform(konvaImage, stage) {
|
||||
const img = konvaImage.image();
|
||||
const sw = stage.width();
|
||||
const sh = stage.height();
|
||||
const iw = img.naturalWidth;
|
||||
const ih = img.naturalHeight;
|
||||
const scale = Math.max(sw / iw, sh / ih);
|
||||
const targetWidth = iw * scale;
|
||||
const targetHeight = ih * scale;
|
||||
const x = (sw - targetWidth) / 2;
|
||||
const y = (sh - targetHeight) / 2;
|
||||
return { scale, x, y };
|
||||
}
|
||||
|
||||
// Обновить существующее изображение до cover (без анимации)
|
||||
export function updateImageToCover(konvaImage, stage) {
|
||||
const { scale, x, y } = calcCoverTransform(konvaImage, stage);
|
||||
konvaImage.width(konvaImage.image().naturalWidth);
|
||||
konvaImage.height(konvaImage.image().naturalHeight);
|
||||
konvaImage.scale({ x: scale, y: scale });
|
||||
konvaImage.x(x);
|
||||
konvaImage.y(y);
|
||||
}
|
||||
|
||||
// Добавить картинку: сначала показать в реальном размере, через delayMs анимированно перейти в cover.
|
||||
// Возвращает функцию cancel(), которую нужно вызывать при unmounted.
|
||||
export function addImageWithPreview(stage, layer, url, delayMs = 1200, opts = {}) {
|
||||
// opts: { crossOrigin, initialPosition: 'top-left'|'center', coverDuration }
|
||||
const crossOrigin = opts.crossOrigin ?? 'Anonymous';
|
||||
const initialPosition = opts.initialPosition ?? 'top-left';
|
||||
const coverDuration = opts.coverDuration ?? 0.45;
|
||||
|
||||
let timeoutId = null;
|
||||
let activeTween = null;
|
||||
let konvaImage = null;
|
||||
|
||||
// helper для очистки
|
||||
function cancel() {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
}
|
||||
if (activeTween) {
|
||||
// finish or destroy tween
|
||||
try { activeTween.finish(); } catch (e) { /* ignore */ }
|
||||
activeTween = null;
|
||||
}
|
||||
// не удаляем сам konvaImage — оставляем на слое, но можно удалить при желании
|
||||
}
|
||||
|
||||
Konva.Image.fromURL(url, (imgNode) => {
|
||||
konvaImage = imgNode;
|
||||
const img = konvaImage.image();
|
||||
const iw = img.naturalWidth;
|
||||
const ih = img.naturalHeight;
|
||||
|
||||
// показать в реальном размере (scale = 1)
|
||||
konvaImage.width(iw);
|
||||
konvaImage.height(ih);
|
||||
konvaImage.scale({ x: 1, y: 1 });
|
||||
|
||||
if (initialPosition === 'center') {
|
||||
// центрируем реальный размер по сцене
|
||||
const sw = stage.width();
|
||||
const sh = stage.height();
|
||||
konvaImage.x((sw - iw) / 2);
|
||||
konvaImage.y((sh - ih) / 2);
|
||||
} else {
|
||||
konvaImage.x(0);
|
||||
konvaImage.y(0);
|
||||
}
|
||||
|
||||
konvaImage.draggable(true);
|
||||
layer.add(konvaImage);
|
||||
layer.draw();
|
||||
|
||||
// через delayMs — плавно переводим в cover
|
||||
if (delayMs > 0) {
|
||||
timeoutId = setTimeout(() => {
|
||||
timeoutId = null;
|
||||
const { scale, x, y } = calcCoverTransform(konvaImage, stage);
|
||||
|
||||
// анимируем: изменяем scaleX/scaleY и x/y
|
||||
activeTween = konvaImage.to({
|
||||
duration: coverDuration,
|
||||
scaleX: scale,
|
||||
scaleY: scale,
|
||||
x,
|
||||
y,
|
||||
easing: Konva.Easings.EaseInOut,
|
||||
onFinish: () => { activeTween = null; }
|
||||
});
|
||||
}, delayMs);
|
||||
}
|
||||
}, { crossOrigin });
|
||||
|
||||
return cancel;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue