Emulate "object-fit: contain" for Image in Konva.js

204 views Asked by At

I am trying to make resizable image with content fully fitted in rect. I found solution how to emulate object-fit: cover, but I need contain. I made some solution:

const width = size.width;
const height = size.height;

var scalex = image.width / width;
var scaley = image.height / height;
var scale = Math.max(scalex, scaley);

return {
  cropWidth: width * scale,
  cropHeight: height * scale,
};

But it not centered the image inside

enter image description here

Have any ideas how to make it centered?

1

There are 1 answers

2
lavrton On BEST ANSWER

You can't do that with just Konva.Image and crop. Konva can cut content with cropping. But it can't render it with empty spaces. For that case, we can use external canvas.

// Create a new Konva stage with the dimensions of the window
const stage = new Konva.Stage({
  container: 'container',
  width: window.innerWidth,
  height: window.innerHeight
});

// Create a new layer to hold the image and transformer
const layer = new Konva.Layer();
// Add the layer to the stage
stage.add(layer);

// Define a function to fit and center an image within a canvas element
function fitContain({canvas, image, canvasWidth, canvasHeight}) {
    // Get the 2D rendering context for the canvas
    const ctx = canvas.getContext('2d');
    
    // Set the dimensions of the canvas
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;
    
    // Calculate the aspect ratios of the image and the canvas
    const imageAspect = image.naturalWidth / image.naturalHeight;
    const canvasAspect = canvasWidth / canvasHeight;
    
    let renderWidth, renderHeight;
    
    // Determine the dimensions to render the image at to maintain aspect ratio
    if (imageAspect > canvasAspect) {
        renderWidth = canvasWidth;
        renderHeight = canvasWidth / imageAspect;
    } else {
        renderWidth = canvasHeight * imageAspect;
        renderHeight = canvasHeight;
    }
    
    // Calculate the position to center the image in the canvas
    const offsetX = (canvasWidth - renderWidth) / 2;
    const offsetY = (canvasHeight - renderHeight) / 2;
    
    // Draw the image on the canvas at the calculated dimensions and position
    ctx.drawImage(image, offsetX, offsetY, renderWidth, renderHeight);
    
    // Return the canvas element
    return canvas;
}

// Create a new image element
const image = new window.Image();
// Set the source of the image
image.src = 'https://i.imgur.com/ktWThtZ.png';
// Define an onload handler to execute once the image has loaded
image.onload = () => {
  // Create a new canvas element
  const canvas = document.createElement('canvas');
  
  // Create a new Konva Image to hold the canvas
  const img = new Konva.Image({
    image: canvas,
    x: 50,
    y: 60,
    width: 200,
    height: 100,
    draggable: true
  });
  // Add the Konva Image to the layer
  layer.add(img);
  // Call fitContain to fit and center the image in the canvas
  fitContain({canvas, image, canvasWidth: img.width(), canvasHeight: img.height() });
  
  // Create a new transformer to allow the Konva Image to be resized
  const tr = new Konva.Transformer({
    flipEnabled: false,
    nodes: [img]
  });
  // Add the transformer to the layer
  layer.add(tr);
  
  // Define a transform event handler to update the canvas when the Konva Image is resized
  img.on('transform', () => {
    // Update the dimensions of the Konva Image to reflect the scaling transformation
    img.setAttrs({
      width: img.width() * img.scaleX(),
      height: img.height() * img.scaleY(),
      scaleX: 1,
      scaleY: 1
    });
    // Call fitContain to fit and center the image in the canvas again
    fitContain({canvas, image, canvasWidth: img.width(), canvasHeight: img.height() });
  })
}
  <script src="https://unpkg.com/konva@^9/konva.min.js"></script>
    <div id="container"></div>