解説コードを読んでみよう
このサンプルでわかること
<input type="file">で選んだファイルを JavaScript で受け取る方法FileReaderでファイルを読み込み、Imageとして開く方法canvasに大きさを変えて描き直す、画像縮小のしくみ- 縮小した画像をダウンロードできるようにする方法(
toBlob)
使っているファイル
- index.phpデモのページ(HTML・CSS・JavaScript がすべてこの中)
画像をサーバーに送る前にブラウザで小さくしておくと、送る時間が短くなり、サーバーの負担も減ります。スマホで撮った大きな写真を投稿するサービスなどでよく使われる方法です。
1. 画面を作る
サイズを入力する欄、ファイルを選ぶボタン、縮小した画像を描く <canvas>、ダウンロード用のボタンを用意します。ダウンロードボタンは最初は hidden で隠しておきます。
<div class="well">
<label>長い辺のサイズ:<input type="number" id="size" value="500" min="10" max="4000" style="width:7em"> px</label>
<hr>
<input type="file" id="file" accept="image/jpeg,image/png">
</div>
<p id="info" class="resize-info"></p>
<canvas id="canvas" width="0" height="0" class="resize-canvas"></canvas>
<p id="download-area" hidden><a id="download" class="btn btn-primary" download="resized.jpg">縮小した画像をダウンロード</a></p>
2. ファイルが選ばれたら処理を始める
change イベントは「選ばれたファイルが変わったとき」に起きます。まず入力されたサイズを数値に変え、次に選ばれたファイル this.files[0] が JPEG か PNG かを file.type で確認します。
const DEFAULT_SIZE = 500; // サイズ欄が空のときの長い辺の長さ
// ファイルが選択されたら
$('#file').on('change', function() {
// 1. 指定サイズを数値で取り出す
let size = parseInt($('#size').val(), 10);
if (isNaN(size) || size <= 0) {
size = DEFAULT_SIZE;
}
// 2. 選ばれたファイルを取り出し、JPEG か PNG か確認する
const file = this.files[0];
if (!file || (file.type !== 'image/jpeg' && file.type !== 'image/png')) {
alert('JPEG か PNG の画像を選んでください。');
this.value = '';
return;
}
parseInt(値, 10) は文字列を整数に変える関数です。入力欄の値はいつも「文字列」なので、計算に使う前に数値に変えておきます。数字でなかったときは NaN(Not a Number)になるので、isNaN() で確かめて既定値の 500 を使います。
3. ファイルを読み込み、画像として開く
FileReader の readAsDataURL() でファイルを読み込むと、onload の中で e.target.result に「data:image/jpeg;base64,…」という形の文字列が入ります。これを Image の src に入れると、画像として開けます。
// 3. ファイルを読み込んで、画像として開く
const reader = new FileReader();
reader.onload = function(e) {
const image = new Image();
image.onload = function() {
};
image.src = e.target.result;
};
reader.readAsDataURL(file);
読み込みは時間がかかるので、すぐ次の行には進みません。「読み込み終わったら onload の中を実行する」という順番になっていることに注意しましょう(非同期処理といいます)。
4. 縦横比を保ったサイズを計算する
横長の画像なら横を、縦長の画像なら縦を指定サイズにして、もう一方は元の比率から計算します。
// 4. 縦横比を保ったまま、新しい幅と高さを計算する
let width, height;
if (image.width > image.height) { // 横長
width = size;
height = size * image.height / image.width;
} else { // 縦長・正方形
height = size;
width = size * image.width / image.height;
}
width = Math.round(width);
height = Math.round(height);
たとえば 4000×3000 の横長写真を 500 にすると、横は 500、縦は 500 × 3000 ÷ 4000 = 375 になります。
5. canvas に描き直す
canvas の大きさを新しいサイズに変えてから、drawImage() で元の画像全体を新しいサイズに縮めて描きます。
// 5. canvas の大きさを変えて、画像を描き直す
const canvas = document.getElementById('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, width, height);
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, width, height);
$('#info').text('元の大きさ ' + image.width + '×' + image.height + 'px → ' + width + '×' + height + 'px');
| drawImage の引数 | 意味 |
|---|---|
image | 描く画像 |
0, 0, image.width, image.height | 元の画像のどこを使うか(左上から全体) |
0, 0, width, height | canvas のどこに、どの大きさで描くか |
6. ダウンロードできるようにする
canvas.toBlob() で、canvas の絵を JPEG ファイルのデータ(Blob)に変えます。URL.createObjectURL() でそのデータを指す URL を作り、リンクの href に入れると、クリックでダウンロードできます。
// 6. canvas の絵を JPEG のデータ(Blob)にして、ダウンロードできるようにする
canvas.toBlob(function(blob) {
const url = URL.createObjectURL(blob);
$('#download').attr('href', url);
$('#download-area').prop('hidden', false);
}, 'image/jpeg', 0.9);
書き直したポイント
古いサンプルでは、canvas の画像を Base64 文字列で取り出し、1文字ずつ数値に直して Blob を作っていました。今は canvas.toBlob() 1つで同じことができます。また、数値以外を入力したときに存在しない命令 exit を呼んでエラーになる部分や、使われていなかったアップロードボタンの処理を整理し、縮小した画像を実際にダウンロードできるようにしました。
toBlob の最後の数字 0.9 は JPEG の画質(0〜1)です。0.5 にするとファイルは小さくなりますが、画質が落ちます。数値を変えてダウンロードしたファイルのサイズを比べてみましょう。