Ilja
Ilja

Reputation: 46547

Best strategy to mimic <img> object-fit / object-position property with konva images

Assume we have a 1000x1000 image drawn onto canvas, and set konva image size to something like 500x700.

Is it possible to mimic html <img /> tags object-fit property in konva so we can tell image object to fit image in cover / contain mode. Similarly it would be useful to mimic object-position.

Thus far the only way I can think of doing this is to clip image with a rectangle with some custom logic added to handle use cases above. I was wondering if there is a better way?

Upvotes: 2

Views: 1006

Answers (1)

lavrton
lavrton

Reputation: 20373

You can emulate similar CSS behavior with cropX, cropY, cropWidth and cropHeight properties of Konva.Image.

You can use this function for calculations:

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
  }
}

// usage:
const crop = getCrop(img.image(), { width: img.width(), height: img.height()}, pos);
img.setAttrs(crop);
img.getLayer().batchDraw();

Demo: https://konvajs.org/docs/sandbox/Scale_Image_To_Fit.html

Upvotes: 3

Related Questions