イメージ圧縮アルゴリズム
canvasAPIによるウェブアップロード時のイメージ圧縮。
幅と高さによる圧縮
ステップの簡単な説明
- 入力ボックスはアップロードを決定するために使用され、アップロードされたファイルはchangeイベントを通して取得されます。
- アップロードされたファイルに対して簡単な種類とサイズの判定を行い、圧縮を開始します。
- イメージを圧縮するための最初のステップは、ユーザーによってアップロードされたイメージをbase64形式に変換することです - new FileReader() -> ReaderAsDataUrl() 非同期読み込み -> 読み込みイベントの完了でbase64を取得します。
- 圧縮イメージの幅と高さを計算し、新しい幅と高さを割り当てたCanvasキャンバスを作成します。次に、canvasDomを非表示にして、domノードにマウントします。
- getContext('2d') canvas オブジェクトを取得し、 drawImage メソッドを呼び出して描画します。
- CanvasDOMを削除し、圧縮されたbase64をコールバック経由でサーバーに渡すだけです。
Tips
- 変更イベントによる非同期完了リスニング
- 圧縮は、幅と高さを圧縮します naturalWidth/naturalHeihgt HTML5 属性は、ソースイメージの高さ幅を取得します。
- Canvasタグを作成し、圧縮された幅と高さを割り当てます。
- visibility-hidden を使った非表示は、単なるスタイルの非表示です。visibility 属性が hidden であるため、DOM の
- ctx.clearReact() -> イメージの上書きを防ぐため、ファイルが再アップロードされるたびにCanvasキャンバスをクリアします。
- ctx.drawImage()は、canvasを介して対応するファイルを描画します。
- HTMLCanvasDOM.toDataUrl()
canvas を base64 形式に変換し、同時に圧縮して返り値を取得します。- 圧縮が成功したら、コールバックを使用して新しいデータ URL を渡し、バックエンドに論理リクエストを送信します。
コードデモ
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvasイメージ圧縮アルゴリズム</title>
</head>
<body>
<!-- イメージが圧縮のためにサーバーにアップロードされる。>
<input type="file" id="file" />
<script>
const ACCEPT = ['image/jpg', 'image/png', 'image/jpeg']
const upload = document.getElementById("file")
const MAXSIZE = ;
const MAXTIP = "1MB"
function convertImageToBase64(file, callback) {
// ウェブアプリケーションがコンピュータに保存されているファイルを非同期で読み込めるように、FileReaderオブジェクトを作成する。
// つまり、ファイルオブジェクト
let reader = new FileReader()
// ロードイベントを追加し、ロード完了後にロードがトリガーされるようにする。
reader.addEventListener("load", function (e) {
const base64Image = e.target.result // リーダーに相当する.result ファイルのBase64を取得する
// メモリの回復
callback && callback(base64Image) // コールバックを呼び出して圧縮する
reader = null
})
// readAsDataURL指定されたファイルまたはblobオブジェクトを読み込むメソッド
reader.readAsDataURL(file)
}
// 圧縮アルゴリズム関数
/*
1.まず、base64イメージ文字列を取得する
2.元のイメージの幅と高さを取得するために、イメージオブジェクトを作成する。
3.条件を満たすように、元イメージの幅と高さを圧縮する。
4.canvasAPIを呼び出して新しいイメージを描画する。
5.描画に成功したら、canvasAPIを呼び出して描画する。
6.圧縮されたbase64を取得する
*/
function compress(base64Image, callback) {
let maxW = 1024;
let maxH = 1024;
const image = new Image() // イメージオブジェクトの作成は、aタグの作成と同じである。
image.addEventListener('load', function (e) {
// image読み込みが完了したとき、つまりsrcが読み込まれたときにトリガーされる。
let radio; // 圧縮率
let needCompress = false; // 圧縮が必要かどうか
// image.naturalWidth/naturalHeight H5新しいプロパティ ソースイメージの幅と高さを取得する
if (image.naturalWidth > maxW) {
needCompress = true;
// 幅と高さを圧縮した後のサイズを取得する
radio = image.naturalWidth / maxW
maxH = image.naturalHeight / radio
}
// 同じように幅を圧縮した後、圧縮された高さが要件を満たしているかどうかも確認する必要がある。 満たしていない場合は、幅と高さの圧縮を続ける。
if (image.naturalHeight > maxH) {
needCompress = true;
radio = image.naturalHeight / maxH
maxW = image.naturalWidth / radio
}
// 圧縮なし
if (!needCompress) {
maxW = image.naturalWidth;
maxH = image.naturalHeight;
}
// 最初の圧縮が完了した
// 次のステップでは、canvas を使用して高品質の圧縮を行う
const canvas = document.createElement('canvas')
canvas.height = maxH;
canvas.width = maxW;
canvas.setAttribute("id", "_compress_")
// visibility hidden DOMをレンダリングしない代わりに非表示にするキャンバスを作成する必要がある。
canvas.style.visibility = 'hidden'
document.body.appendChild(canvas)
const ctx = canvas.getContext('2d')
// canvas.clearRect() メソッドは、指定された矩形内の指定されたピクセルをクリアする。(x1, y1, 幅,height)
// 再アップロードによる上書きを防ぐ
ctx.clearRect(0, 0, maxW, maxH)
// canvas.drawImage() メソッドは、イメージ、canvas、またはcanvas上に描画する。
// /イメージオブジェクト 始点 x 始点 y 描画幅 描画高さ
ctx.drawImage(image, 0, 0, maxW, maxH)
// 次のステップでは、canvasを圧縮し、APIを通じてbase64形式で出力する。
/**
* @HTMLCanvasElement.toDataURL(type, encoderOptions); コールバック先はCanvasのDomオブジェクトであり、ctxではないことに注意。
* @param {String} typeを使用して、type image formatをパラメータ化することができる。/png,イメージの解像度は96 dpiである。
* @param {Number} encoderOptions オプション イメージ形式が image であることを指定する。/jpegまたはイメージ/webpキャンバスの場合、画質は0から1まで選択できる。値が範囲外の場合は、デフォルトの0が使用される。.92
* @returns {dataURL} メソッドは、表示するイメージを含むデータURIを返す。
*/
console.log(base64Image, 'base64Image')
/* これはCanvas DOMノードであり、Canvasオブジェクトではないことに注意。*/
const compressImage = canvas.toDataURL('image/jpeg', 0.8) // 通常、圧縮率は0である。.8-0.9
callback && callback(compressImage); // バックエンド転送ロジックの圧縮が完了する
// 圧縮されたイメージがメモリ内にある。
// 次に、canvas要素を削除し、DOMを呼び出す。.remove()
canvas.remove()
// プレビューをアップロードする必要がある場合は、プレビュー用に別の新しいイメージを作成できる。
const _image = new Image()
_image.src = compressImage;
document.body.appendChild(_image)
//圧縮率を計算する srcの長さで比較する。
console.log(` ${image.src.length/_iamge.src.length}`)
})
image.src = base64Image;
// document.body.appendChild(image) //
}
// アップロードされたイメージに変更イベントをバインドする
// mockバックエンドロジックをアップロードする
function uploadToServer(compressImage) {
console.log("バックエンドをアップロードする")
}
upload.addEventListener('change', function (e) {
// アップロードされたファイルを取得する Arryを分解して最初の要素を取得する。
const [file] = e.target.files;
if (!file) {
return;
}
const {
type: fileType,
size: fileSize
} = file;
// アップロード検証ロジック
if (!ACCEPT.includes(fileType)) {
alert(` [${fileType}] `)
upload.value = ""
return
}
if (fileSize > MAXSIZE) {
alert(` ${MAXTIP}`)
upload.value = ""
return
}
// イメージを圧縮するには、base64に変換する必要がある。
convertImageToBase64(file, (base64Image) => {
compress(base64Image, uploadToServer)
})
})
</script>
</body>
</html>