Skip to main content

缩放图像以适应固定的画布区域

如何在不拉伸图像的情况下缩放图像以适应可用区域?

该演示展示了如何使用 Konva.Imagecrop 属性来模拟 CSS 的 object-fit: cover

crop 属性允许您仅使用源图像的指定区域进行画布绘制。如果做出正确的计算,则可以在不拉伸的情况下绘制得到的图像。

说明:尝试调整图像大小或使用顶部下拉菜单更改裁剪策略。图像将在符合指定尺寸的同时保持其纵横比。

import Konva from 'konva';

// 创建选择元素以选择裁剪位置
const select = document.createElement('select');
select.style.position = 'absolute';
select.style.top = '4px';
select.style.left = '4px';

const positions = [
'左上', '中上', '右上', '--',
'左中', '中中', '右中', '--',
'左下', '中下', '右下'
];

positions.forEach(pos => {
const option = document.createElement('option');
option.value = pos;
option.text = pos;
if (pos === '中中') option.selected = true;
select.appendChild(option);
});

document.body.appendChild(select);

const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});

const layer = new Konva.Layer();
stage.add(layer);

// 计算源图像、其可见大小和裁剪策略的裁剪值的函数
function getCrop(image, size, clipPosition = 'center-middle') {
const width = size.width;
const height = size.height;
const aspectRatio = width / height;

let newWidth;
let newHeight;

const imageRatio = image.width / image.height;

if (aspectRatio >= imageRatio) {
newWidth = image.width;
newHeight = image.width / aspectRatio;
} else {
newWidth = image.height * aspectRatio;
newHeight = image.height;
}

let x = 0;
let y = 0;
if (clipPosition === '左上') {
x = 0;
y = 0;
} else if (clipPosition === '左中') {
x = 0;
y = (image.height - newHeight) / 2;
} else if (clipPosition === '左下') {
x = 0;
y = image.height - newHeight;
} else if (clipPosition === '中上') {
x = (image.width - newWidth) / 2;
y = 0;
} else if (clipPosition === '中中') {
x = (image.width - newWidth) / 2;
y = (image.height - newHeight) / 2;
} else if (clipPosition === '中下') {
x = (image.width - newWidth) / 2;
y = image.height - newHeight;
} else if (clipPosition === '右上') {
x = image.width - newWidth;
y = 0;
} else if (clipPosition === '右中') {
x = image.width - newWidth;
y = (image.height - newHeight) / 2;
} else if (clipPosition === '右下') {
x = image.width - newWidth;
y = image.height - newHeight;
}

return {
cropX: x,
cropY: y,
cropWidth: newWidth,
cropHeight: newHeight,
};
}

// 应用裁剪的函数
function applyCrop(img, pos) {
img.setAttr('lastCropUsed', pos);
const crop = getCrop(
img.image(),
{ width: img.width(), height: img.height() },
pos
);
img.setAttrs(crop);
}

Konva.Image.fromURL('https://konvajs.org/assets/darth-vader.jpg', (img) => {
img.setAttrs({
width: 300,
height: 100,
x: 80,
y: 100,
name: 'image',
draggable: true,
});
layer.add(img);
// 应用默认的中中裁剪
applyCrop(img, '中中');

const tr = new Konva.Transformer({
nodes: [img],
keepRatio: false,
flipEnabled: false,
boundBoxFunc: (oldBox, newBox) => {
if (Math.abs(newBox.width) < 10 || Math.abs(newBox.height) < 10) {
return oldBox;
}
return newBox;
},
});

layer.add(tr);

img.on('transform', () => {
// 在变换时重置缩放
img.setAttrs({
scaleX: 1,
scaleY: 1,
width: img.width() * img.scaleX(),
height: img.height() * img.scaleY(),
});
applyCrop(img, img.getAttr('lastCropUsed'));
});
});

select.addEventListener('change', (e) => {
const img = layer.findOne('.image');
applyCrop(img, e.target.value);
});