将图像缩放以适应固定区域在画布上

如何缩放图像以适应可用区域而不拉伸?

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

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

说明:尝试调整图像大小或更改裁剪策略

Konva Crop Imageview raw
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/konva@9.3.18/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Scale to Fit Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
select {
position: absolute;
top: 4px;
left: 4px;
}
</style>
</head>

<body>
<div id="container"></div>
<select id="clip">
<option value="left-top" selected>left-top</option>
<option value="center-top">center-top</option>
<option value="right-top">right-top</option>
<option value="--">--</option>
<option value="left-middle">left-middle</option>
<option value="center-middle">center-middle</option>
<option value="right-middle">right-middle</option>
<option value="--">--</option>
<option value="left-bottom">left-bottom</option>
<option value="center-bottom">center-bottom</option>
<option value="right-bottom">right-bottom</option>
</select>
<script>
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});

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

// function to calculate crop values from source image, its visible size and a crop strategy
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 === 'left-top') {
x = 0;
y = 0;
} else if (clipPosition === 'left-middle') {
x = 0;
y = (image.height - newHeight) / 2;
} else if (clipPosition === 'left-bottom') {
x = 0;
y = image.height - newHeight;
} else if (clipPosition === 'center-top') {
x = (image.width - newWidth) / 2;
y = 0;
} else if (clipPosition === 'center-middle') {
x = (image.width - newWidth) / 2;
y = (image.height - newHeight) / 2;
} else if (clipPosition === 'center-bottom') {
x = (image.width - newWidth) / 2;
y = image.height - newHeight;
} else if (clipPosition === 'right-top') {
x = image.width - newWidth;
y = 0;
} else if (clipPosition === 'right-middle') {
x = image.width - newWidth;
y = (image.height - newHeight) / 2;
} else if (clipPosition === 'right-bottom') {
x = image.width - newWidth;
y = image.height - newHeight;
} else if (clipPosition === 'scale') {
x = 0;
y = 0;
newWidth = width;
newHeight = height;
} else {
console.error(
new Error('Unknown clip position property - ' + clipPosition)
);
}

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

// function to apply crop
function applyCrop(pos) {
const img = layer.findOne('.image');
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);
// apply default left-top crop
applyCrop('center-middle');

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', () => {
// reset scale on transform
img.setAttrs({
scaleX: 1,
scaleY: 1,
width: img.width() * img.scaleX(),
height: img.height() * img.scaleY(),
});
applyCrop(img.getAttr('lastCropUsed'));
});
}
);

document.querySelector('#clip').onchange = (e) => {
applyCrop(e.target.value);
};
</script>
</body>
</html>