はじめに
個人開発中のSNSでプロフィール画像の設定を実装した際、トリミング機能とデータ圧縮機能を用いたので、今回は私が詰まった点も含めて共有します。
開発環境
- React
18.2.0
使用ライブラリ
- cropper.js
1.5.13
- react-cropper
2.3.3
手順
cropper.jsとreact-cropperをダウンロード
npm install cropperjs@1.5.13
npm install react-cropper
react-cropper
を利用する場合は注意が必要です。
最新の cropper.js
(v2系)では、react-cropper
が必要とする一部のデータや機能が含まれていないため、正常に動作しません。
そのため、react-cropper
を使うときは cropperjs@1.x
系をインストールするのがおすすめ です。
トリミング用のモーダルコンポーネント作成
ここで以下の3つを行いました。
- クロップの形を円形で表示
- 画像サイズを縦横最大500pxにリサイズ
- 画質を落として画像容量圧縮
クロップの形を円形で表示
現在開発中のプロダクツではプロフィール画像は円形表示だったので、トリミング時もクロップが円形の方が直感的に操作しやすいです。
しかし、cropper.jsはクロップ領域を四角形でしか切り抜けません。
そのため実際に切り抜かれる画像データは四角形ですが、見た目だけクロップ枠を丸くすることでユーザーが直感的にプロフィール画像をトリミングできるようにしました。
// cropBoxを丸くする
useEffect(() => {
const style = document.createElement('style');
style.innerHTML = `
.cropper-crop-box, .cropper-view-box {
border-radius: 50% !important;
}
`;
document.head.appendChild(style);
return () => {
document.head.removeChild(style);
};
}, []);
画像サイズを縦横最大500pxにリサイズ・画質を落として画像容量圧縮
SNSでは、多くのユーザーがプロフィール画像を登録します。
さらにプロフィール画像は表示される回数も非常に多いため、
画像データのサイズをなるべく小さくすることが重要です。
その対策として、プロフィール画像をトリミングする際に
- 画像のリサイズ(縦横のサイズ調整)
- 画質の調整
を行い、容量を圧縮できるようにしました。
const handleCrop = () => {
const cropper = cropperRef.current.cropper;
const croppedCanvas = cropper.getCroppedCanvas();
// 元画像のサイズを取得
const originalWidth = croppedCanvas.width;
const originalHeight = croppedCanvas.height;
// 最大500pxにリサイズ
let targetWidth = originalWidth;
let targetHeight = originalHeight;
if (originalWidth > originalHeight && originalWidth > 500) {
targetWidth = 500;
targetHeight = Math.round((originalHeight / originalWidth) * 500);
} else if (originalHeight >= originalWidth && originalHeight > 500) {
targetHeight = 500;
targetWidth = Math.round((originalWidth / originalHeight) * 500);
}
// リサイズして圧縮
cropper
.getCroppedCanvas({
width: targetWidth,
height: targetHeight,
imageSmoothingQuality: 'high',
})
.toBlob(
blob => {
onCrop(new File([blob], 'cropped.jpg', { type: blob.type }));
onClose();
},
'image/jpeg',
0.7 // 圧縮率
);
};
コンポーネント全体
import { useRef, useEffect } from 'react';
import Cropper from 'react-cropper';
import 'cropperjs/dist/cropper.css';
export default function CropperModal({ src, onClose, onCrop }) {
const cropperRef = useRef();
// cropBoxを丸くする
useEffect(() => {
const style = document.createElement('style');
style.innerHTML = `
.cropper-crop-box, .cropper-view-box {
border-radius: 50% !important;
}
`;
document.head.appendChild(style);
return () => {
document.head.removeChild(style);
};
}, []);
const handleCrop = () => {
const cropper = cropperRef.current.cropper;
const croppedCanvas = cropper.getCroppedCanvas();
// 元画像のサイズを取得
const originalWidth = croppedCanvas.width;
const originalHeight = croppedCanvas.height;
// 最大500pxにリサイズ
let targetWidth = originalWidth;
let targetHeight = originalHeight;
if (originalWidth > originalHeight && originalWidth > 500) {
targetWidth = 500;
targetHeight = Math.round((originalHeight / originalWidth) * 500);
} else if (originalHeight >= originalWidth && originalHeight > 500) {
targetHeight = 500;
targetWidth = Math.round((originalWidth / originalHeight) * 500);
}
// リサイズして圧縮
cropper
.getCroppedCanvas({
width: targetWidth,
height: targetHeight,
imageSmoothingQuality: 'high',
})
.toBlob(
blob => {
onCrop(new File([blob], 'cropped.jpg', { type: blob.type }));
onClose();
},
'image/jpeg',
0.7 // 圧縮率
);
};
return (
<div className='fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50'>
<div className='bg-white p-4 rounded shadow-lg'>
<Cropper
src={src}
style={{ height: 300, width: 300 }}
aspectRatio={1}
guides={false}
ref={cropperRef}
viewMode={1}
dragMode='move'
background={false}
responsive={true}
autoCropArea={1}
checkOrientation={false}
/>
<div className='flex justify-end gap-2 mt-2'>
<button
type='button'
className='px-4 py-1 rounded bg-gray-300'
onClick={onClose}
>
キャンセル
</button>
<button
type='button'
className='px-4 py-1 rounded bg-blue-600 text-white'
onClick={handleCrop}
>
トリミングして決定
</button>
</div>
</div>
</div>
);
}
まとめ
-
react-cropper
を利用する場合はcropperjs@1.x
系を使うのがおすすめ - プロフィール画像は 登録数・表示回数が多いため容量削減が重要
- トリミング画面では「円形のクロップ枠」を表示することで、ユーザーに直感的な操作感を提供
- トリミング時に リサイズ(最大500px) と 画質調整(圧縮率0.7) を行うことで、画像サイズを効率的に削減可能
- この仕組みを入れることで、表示速度の改善 や サーバー負荷・ストレージコストの軽減 が期待できる