Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to resize a photo with expo react native

Given a the uri of a photo on the users device (both file:// and content://), how could i resize the photo? I'm running a managed expo app, so ideally we would be able to do this without detaching

like image 616
Ulad Kasach Avatar asked Dec 04 '22 18:12

Ulad Kasach


1 Answers

This can be done with expo's ImageManipulator util:

const resizedPhoto = await ImageManipulator.manipulateAsync(
 photo.uri,
 [{ resize: { width: 300 } }], // resize to width of 300 and preserve aspect ratio 
 { compress: 0.7, format: 'jpeg' },
);

note: i'm using expo@v31, the latest version @v33 has a different syntax - please reference the link above


bonus: here is a way to ensure that neither the width or the height exceeds a maximum value

import { ImageManipulator } from 'expo';

enum PhotoDimensions {
  WIDTH = 'width',
  HEIGHT = 'height',
}

const maximalValuesPerDimension = { width: 1000, height: 1000 };
export const resizePhotoToMaxDimensionsAndCompressAsJPEG = async ({ photo }: { photo: { width: number, height: number, uri: string } }) => {
  // 1. define the maximal dimension and the allowed value for it
  const largestDimension = (photo.width > photo.height) ? PhotoDimensions.WIDTH : PhotoDimensions.HEIGHT;
  const initialValueOfLargestDimension = photo[largestDimension];
  const maximalAllowedValueOfLargestDimension = maximalValuesPerDimension[largestDimension];
  const targetValueOfLargestDimension = (initialValueOfLargestDimension > maximalAllowedValueOfLargestDimension) ? maximalAllowedValueOfLargestDimension : initialValueOfLargestDimension;

  // 2. resize the photo w/ that target value for that dimension (keeping the aspect ratio)
  const resizedPhoto = await ImageManipulator.manipulateAsync(
    photo.uri,
    [{ resize: { [largestDimension]: targetValueOfLargestDimension } }],
    { compress: 0.7, format: 'jpeg' },
  );

  // 3. return the resized photo
  return resizedPhoto;
};

and some test coverage for it:

import { ImageManipulator } from 'expo';
import { resizePhotoToMaxDimensionsAndCompressAsJPEG } from './resizePhotoToMaxDimensionsAndCompressAsJPEG';

jest.mock('expo', () => ({
  ImageManipulator: {
    manipulateAsync: jest.fn(),
  },
}));
const manipulateAsyncMock = ImageManipulator.manipulateAsync as jest.Mock;

describe('resizePhotoToMaxDimensionsAndCompressAsJPEG', () => {
  beforeEach(() => jest.clearAllMocks());
  it('should not change the dimensions of a photo if neither of its dimensions exceed the largest allowed value', async () => {
    const inputPhoto = { uri: '__TEST_URI__', width: 821, height: 128 };
    await resizePhotoToMaxDimensionsAndCompressAsJPEG({ photo: inputPhoto });
    expect(manipulateAsyncMock).toHaveBeenCalledTimes(1);
    expect(manipulateAsyncMock.mock.calls[0][1][0].resize).toEqual({
      width: 821,
    });
  });
  it('should resize the photo accurately if the width exceeds the largest allowed value', async () => {
    const inputPhoto = { uri: '__TEST_URI__', width: 12000, height: 128 };
    await resizePhotoToMaxDimensionsAndCompressAsJPEG({ photo: inputPhoto });
    expect(manipulateAsyncMock).toHaveBeenCalledTimes(1);
    expect(manipulateAsyncMock.mock.calls[0][1][0].resize).toEqual({
      width: 1000,
    });
  });
  it('should resize the photo accurately if the height exceeds the largest allowed value', async () => {
    const inputPhoto = { uri: '__TEST_URI__', width: 821, height: 12000 };
    await resizePhotoToMaxDimensionsAndCompressAsJPEG({ photo: inputPhoto });
    expect(manipulateAsyncMock).toHaveBeenCalledTimes(1);
    expect(manipulateAsyncMock.mock.calls[0][1][0].resize).toEqual({
      height: 1000,
    });
  });
  it('should compress the photo and convert it into a jpeg', async () => {
    const inputPhoto = { uri: '__TEST_URI__', width: 821, height: 12000 };
    await resizePhotoToMaxDimensionsAndCompressAsJPEG({ photo: inputPhoto });
    expect(manipulateAsyncMock).toHaveBeenCalledTimes(1);
    expect(manipulateAsyncMock.mock.calls[0][2]).toEqual({
      compress: expect.any(Number),
      format: 'jpeg',
    });
  });
});
like image 162
Ulad Kasach Avatar answered Dec 06 '22 06:12

Ulad Kasach