背景
ユーザーがアップロードした画像をそのままレンダリングすると大きすぎるファイルだとこんな感じになる。
↑デカすぎんだろ.
コード
function resizeImg(file, maxWidth, maxHeight) {
return new Promise((resolve) => {
const img = new Image();
const fileurl = URL.createObjectURL(file);
img.src = fileurl;
console.log(fileurl)
img.onload = () => {
// 比率を保ってリサイズサイズを決定
const originalWidth = img.width;
const originalHeight = img.height;
const ratio = Math.min(maxWidth / originalWidth, maxHeight / originalHeight);
const newWidth = Math.round(originalWidth * ratio);
const newHeight = Math.round(originalHeight * ratio);
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
if (!context) {
URL.revokeObjectURL(fileurl);
resolve(null);
}
canvas.width = newWidth;
canvas.height = newHeight;
context.drawImage(img, 0, 0, newWidth, newHeight);
canvas.toBlob((blob) => {
URL.revokeObjectURL(fileurl);
resolve(blob);
}, file.type);
};
img.onerror = () => {
URL.revokeObjectURL(fileurl);
resolve(null);
};
});
}
imgの部分
const img = new Image(); //<img>タグを作成
img.src // 画像の読み込み用URL指定
img.onload // 画像の読み込みが成功したときに関数を実行
img.onerror // 読み込み時にエラーが発生したときに関数を実行
canvasの部分
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
canvasとは?
画像を描画する仮想のキャンバス要素で画面には描画されない
getContext("2d")で「2d描画のコンテキストを取得」
canvas.width = width;
canvas.height = height;
context.drawImage(img, 0, 0, width, height);
引数に受け取ったピクセルでキャンバスの縦横を指定します。
drawImage(img, 0, 0, width, height)はキャンパスへ、画像を張り付ける関数
canvas.toBlob((blob) => {
URL.revokeObjectURL(objectUrl); // メモリ解放
resolve(blob); //Promiseにblobの結果を返す
}, file.type);
今回はPromiseの成功結果としてblobを返している。
canvas.toBlobの処理の流れは下記。
第二引数で指定されたfile.typeでキャンバスの描画データをblobへと変換
↓
変換データが第一引数(コールバック関数)に渡されて実行される
URL.revokeObjectURL(objectUrl);の意味
メモリ上にアクセス用の画像データが一時的に展開されているが、これはブラウザを閉じるまで保持されてしまう。
そのため、明示的にコード側で閉じる必要性がある。
URL.revokeObjectURL関数でメモリデータアクセス用のurlを指定することで削除可能
受け取ったblobをファイルオブジェクトにする
const fileblob = await resizeImg(FileData, 400,600);
var resizedFile = new File([fileblob], FileData.name, { type: FileData.type });
このresizedFileをimgタグのsrcに渡すと良い。
実際にテスト
(html部分などについては割愛)
const fileblob = await resizeImg(FileData, 400,600);