картинка масштабируется
parent
84ab8fc59f
commit
c70fd4e98b
@ -1,48 +1,153 @@
|
|||||||
// konva-utils.js
|
// konva-utils.js
|
||||||
export function attachStageResizer(root, stage) {
|
|
||||||
const observer = new ResizeObserver(() => {
|
// attachStageResizer(root, stage, onResize?)
|
||||||
if (!stage) return;
|
export function attachStageResizer(root, stage, onResize) {
|
||||||
|
const observer = new ResizeObserver(() => {
|
||||||
stage.width(root.clientWidth);
|
if (!stage) return;
|
||||||
stage.height(root.clientHeight);
|
|
||||||
stage.batchDraw();
|
stage.width(root.clientWidth);
|
||||||
});
|
stage.height(root.clientHeight);
|
||||||
|
stage.batchDraw();
|
||||||
observer.observe(root);
|
|
||||||
return observer;
|
if (typeof onResize === 'function') {
|
||||||
}
|
onResize();
|
||||||
|
}
|
||||||
// масштабирует картинку по stage
|
});
|
||||||
export function fitImageToStage(konvaImage, stage) {
|
|
||||||
const img = konvaImage.image();
|
observer.observe(root);
|
||||||
const sw = stage.width();
|
return observer;
|
||||||
const sh = stage.height();
|
}
|
||||||
const iw = img.naturalWidth;
|
|
||||||
const ih = img.naturalHeight;
|
// fitImageToStage: вписать целиком (contain)
|
||||||
const scale = Math.min(sw / iw, sh / ih);
|
export function fitImageToStage(konvaImage, stage) {
|
||||||
|
const img = konvaImage.image();
|
||||||
konvaImage.width(iw);
|
const sw = stage.width();
|
||||||
konvaImage.height(ih);
|
const sh = stage.height();
|
||||||
konvaImage.scale({ x: scale, y: scale });
|
const iw = img.naturalWidth;
|
||||||
|
const ih = img.naturalHeight;
|
||||||
// центрируем
|
const scale = Math.min(sw / iw, sh / ih);
|
||||||
konvaImage.x((sw - iw * scale) / 2);
|
|
||||||
konvaImage.y((sh - ih * scale) / 2);
|
konvaImage.width(iw);
|
||||||
}
|
konvaImage.height(ih);
|
||||||
|
konvaImage.scale({ x: scale, y: scale });
|
||||||
export function coverImageToStage(konvaImage, stage) {
|
|
||||||
const img = konvaImage.image();
|
konvaImage.x((sw - iw * scale) / 2);
|
||||||
const sw = stage.width();
|
konvaImage.y((sh - ih * scale) / 2);
|
||||||
const sh = stage.height();
|
}
|
||||||
const iw = img.naturalWidth;
|
|
||||||
const ih = img.naturalHeight;
|
// coverImageToStage: заполнить (cover)
|
||||||
const scale = Math.max(sw / iw, sh / ih);
|
export function coverImageToStage(konvaImage, stage) {
|
||||||
|
const img = konvaImage.image();
|
||||||
konvaImage.width(iw);
|
const sw = stage.width();
|
||||||
konvaImage.height(ih);
|
const sh = stage.height();
|
||||||
konvaImage.scale({ x: scale, y: scale });
|
const iw = img.naturalWidth;
|
||||||
|
const ih = img.naturalHeight;
|
||||||
// центрируем и обрезаем по центру
|
const scale = Math.max(sw / iw, sh / ih);
|
||||||
konvaImage.x((sw - iw * scale) / 2);
|
|
||||||
konvaImage.y((sh - ih * scale) / 2);
|
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