192

こちらがヌードルスクラッチャーです。

HTML5ローカルストレージとxhrv2があることを念頭に置いてください。誰かが実用的な例を見つけることができるかどうか、あるいはこの質問に対して「はい」または「いいえ」を教えてくれるかどうか疑問に思っていました。

新しいローカルストレージ(またはその他)を使用して画像のサイズを事前に設定して、画像のサイズ変更についての手がかりがないユーザーが10 MBの画像を私のウェブサイトにドラッグし、新しいローカルストレージを使用してサイズを変更できるようにすることは可能ですか?次に、小さいサイズでアップロードします。

Flash、Javaアプレット、ActiveXでそれを実行できることは十分に知っています...問題は、Javascript+Html5で実行できるかどうかです。

これについての返答を楽しみにしています。

今のところタ。

4

10 に答える 10

194

はい、File APIを使用すると、キャンバス要素で画像を処理できます。

この Mozilla Hacks ブログ投稿では、ほとんどのプロセスについて説明しています。参考までに、ブログ投稿からアセンブルされたソース コードを次に示します。

// from an input element
var filesToUpload = input.files;
var file = filesToUpload[0];

var img = document.createElement("img");
var reader = new FileReader();  
reader.onload = function(e) {img.src = e.target.result}
reader.readAsDataURL(file);

var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);

var MAX_WIDTH = 800;
var MAX_HEIGHT = 600;
var width = img.width;
var height = img.height;

if (width > height) {
  if (width > MAX_WIDTH) {
    height *= MAX_WIDTH / width;
    width = MAX_WIDTH;
  }
} else {
  if (height > MAX_HEIGHT) {
    width *= MAX_HEIGHT / height;
    height = MAX_HEIGHT;
  }
}
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);

var dataurl = canvas.toDataURL("image/png");

//Post dataurl to the server with AJAX
于 2012-04-26T13:13:51.353 に答える
117

私は数年前にこの問題に取り組み、ソリューションをhttps://github.com/rossturner/HTML5-ImageUploaderとして github にアップロードしました。

robertc の回答では、Mozilla Hacks ブログ投稿で提案されているソリューションを使用していますが、2:1 (またはその倍数) ではないスケールにサイズ変更すると、画質が非常に悪くなることがわかりました。さまざまな画像サイズ変更アルゴリズムを試してみましたが、ほとんどは非常に遅くなったり、品質が良くなかったりしました。

最後に、実行速度が速く、パフォーマンスもかなり優れていると思われるソリューションを思いつきました。1 つのキャンバスから別のキャンバスにコピーする Mozilla ソリューションは、xのターゲットが与えられた場合、2:1 の比率で画像の品質を損なうことなく迅速に機能するためです。幅がピクセル、高さがyピクセルの場合、画像がxと 2 xの間、およびyと 2 yの間になるまで、このキャンバスのサイズ変更方法を使用します。この時点で、目標サイズに縮小する最後の「ステップ」として、アルゴリズムによる画像のサイズ変更を行います。いくつかの異なるアルゴリズムを試した後、オンラインではなくなったもののインターネット アーカイブからアクセスできるブログから取得した双一次補間に落ち着きました。、これは良い結果をもたらします。適用可能なコードは次のとおりです。

ImageUploader.prototype.scaleImage = function(img, completionCallback) {
    var canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;
    canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);

    while (canvas.width >= (2 * this.config.maxWidth)) {
        canvas = this.getHalfScaleCanvas(canvas);
    }

    if (canvas.width > this.config.maxWidth) {
        canvas = this.scaleCanvasWithAlgorithm(canvas);
    }

    var imageData = canvas.toDataURL('image/jpeg', this.config.quality);
    this.performUpload(imageData, completionCallback);
};

ImageUploader.prototype.scaleCanvasWithAlgorithm = function(canvas) {
    var scaledCanvas = document.createElement('canvas');

    var scale = this.config.maxWidth / canvas.width;

    scaledCanvas.width = canvas.width * scale;
    scaledCanvas.height = canvas.height * scale;

    var srcImgData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
    var destImgData = scaledCanvas.getContext('2d').createImageData(scaledCanvas.width, scaledCanvas.height);

    this.applyBilinearInterpolation(srcImgData, destImgData, scale);

    scaledCanvas.getContext('2d').putImageData(destImgData, 0, 0);

    return scaledCanvas;
};

ImageUploader.prototype.getHalfScaleCanvas = function(canvas) {
    var halfCanvas = document.createElement('canvas');
    halfCanvas.width = canvas.width / 2;
    halfCanvas.height = canvas.height / 2;

    halfCanvas.getContext('2d').drawImage(canvas, 0, 0, halfCanvas.width, halfCanvas.height);

    return halfCanvas;
};

ImageUploader.prototype.applyBilinearInterpolation = function(srcCanvasData, destCanvasData, scale) {
    function inner(f00, f10, f01, f11, x, y) {
        var un_x = 1.0 - x;
        var un_y = 1.0 - y;
        return (f00 * un_x * un_y + f10 * x * un_y + f01 * un_x * y + f11 * x * y);
    }
    var i, j;
    var iyv, iy0, iy1, ixv, ix0, ix1;
    var idxD, idxS00, idxS10, idxS01, idxS11;
    var dx, dy;
    var r, g, b, a;
    for (i = 0; i < destCanvasData.height; ++i) {
        iyv = i / scale;
        iy0 = Math.floor(iyv);
        // Math.ceil can go over bounds
        iy1 = (Math.ceil(iyv) > (srcCanvasData.height - 1) ? (srcCanvasData.height - 1) : Math.ceil(iyv));
        for (j = 0; j < destCanvasData.width; ++j) {
            ixv = j / scale;
            ix0 = Math.floor(ixv);
            // Math.ceil can go over bounds
            ix1 = (Math.ceil(ixv) > (srcCanvasData.width - 1) ? (srcCanvasData.width - 1) : Math.ceil(ixv));
            idxD = (j + destCanvasData.width * i) * 4;
            // matrix to vector indices
            idxS00 = (ix0 + srcCanvasData.width * iy0) * 4;
            idxS10 = (ix1 + srcCanvasData.width * iy0) * 4;
            idxS01 = (ix0 + srcCanvasData.width * iy1) * 4;
            idxS11 = (ix1 + srcCanvasData.width * iy1) * 4;
            // overall coordinates to unit square
            dx = ixv - ix0;
            dy = iyv - iy0;
            // I let the r, g, b, a on purpose for debugging
            r = inner(srcCanvasData.data[idxS00], srcCanvasData.data[idxS10], srcCanvasData.data[idxS01], srcCanvasData.data[idxS11], dx, dy);
            destCanvasData.data[idxD] = r;

            g = inner(srcCanvasData.data[idxS00 + 1], srcCanvasData.data[idxS10 + 1], srcCanvasData.data[idxS01 + 1], srcCanvasData.data[idxS11 + 1], dx, dy);
            destCanvasData.data[idxD + 1] = g;

            b = inner(srcCanvasData.data[idxS00 + 2], srcCanvasData.data[idxS10 + 2], srcCanvasData.data[idxS01 + 2], srcCanvasData.data[idxS11 + 2], dx, dy);
            destCanvasData.data[idxD + 2] = b;

            a = inner(srcCanvasData.data[idxS00 + 3], srcCanvasData.data[idxS10 + 3], srcCanvasData.data[idxS01 + 3], srcCanvasData.data[idxS11 + 3], dx, dy);
            destCanvasData.data[idxD + 3] = a;
        }
    }
};

config.maxWidthこれにより、元の縦横比を維持しながら、画像が の幅に縮小されます。開発時には、これは主要なデスクトップ ブラウザー (IE9+、Firefox、Chrome) に加えて iPad/iPhone Safari でも機能したため、今日の HTML5 の広範な採用を考えると、互換性があると予想されます。canvas.toDataURL() 呼び出しは MIME タイプと画像品質を取得することに注意してください。これにより、品質と出力ファイル形式を制御できます (必要に応じて、入力とは異なる可能性があります)。

これがカバーしていない唯一のポイントは、向きの情報を維持することです。このメタデータの知識がなければ、画像はサイズ変更され、そのまま保存され、向きのために画像内のメタデータが失われます。つまり、タブレットデバイスで「上下逆」に撮影された画像はそのようにレンダリングされますが、デバイスのカメラ ビューファインダーでは反転されます。これが懸念される場合は、このブログ投稿に、これを達成する方法に関する優れたガイドとコード例があり、上記のコードに統合できると確信しています。

于 2014-07-16T08:01:53.297 に答える
30

上記の訂正:

<img src="" id="image">
<input id="input" type="file" onchange="handleFiles()">
<script>

function handleFiles()
{
    var filesToUpload = document.getElementById('input').files;
    var file = filesToUpload[0];

    // Create an image
    var img = document.createElement("img");
    // Create a file reader
    var reader = new FileReader();
    // Set the image once loaded into file reader
    reader.onload = function(e)
    {
        img.src = e.target.result;

        var canvas = document.createElement("canvas");
        //var canvas = $("<canvas>", {"id":"testing"})[0];
        var ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0);

        var MAX_WIDTH = 400;
        var MAX_HEIGHT = 300;
        var width = img.width;
        var height = img.height;

        if (width > height) {
          if (width > MAX_WIDTH) {
            height *= MAX_WIDTH / width;
            width = MAX_WIDTH;
          }
        } else {
          if (height > MAX_HEIGHT) {
            width *= MAX_HEIGHT / height;
            height = MAX_HEIGHT;
          }
        }
        canvas.width = width;
        canvas.height = height;
        var ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0, width, height);

        var dataurl = canvas.toDataURL("image/png");
        document.getElementById('image').src = dataurl;     
    }
    // Load files into file reader
    reader.readAsDataURL(file);


    // Post the data
    /*
    var fd = new FormData();
    fd.append("name", "some_filename.jpg");
    fd.append("image", dataurl);
    fd.append("info", "lah_de_dah");
    */
}</script>
于 2014-07-15T11:21:33.990 に答える
8

車輪を再発明したくない場合は、plupload.comを試してください。

于 2012-12-15T05:33:49.837 に答える
8

タイプスクリプト

async resizeImg(file: Blob): Promise<Blob> {
    let img = document.createElement("img");
    img.src = await new Promise<any>(resolve => {
        let reader = new FileReader();
        reader.onload = (e: any) => resolve(e.target.result);
        reader.readAsDataURL(file);
    });
    await new Promise(resolve => img.onload = resolve)
    let canvas = document.createElement("canvas");
    let ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    let MAX_WIDTH = 1000;
    let MAX_HEIGHT = 1000;
    let width = img.naturalWidth;
    let height = img.naturalHeight;
    if (width > height) {
        if (width > MAX_WIDTH) {
            height *= MAX_WIDTH / width;
            width = MAX_WIDTH;
        }
    } else {
        if (height > MAX_HEIGHT) {
            width *= MAX_HEIGHT / height;
            height = MAX_HEIGHT;
        }
    }
    canvas.width = width;
    canvas.height = height;
    ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0, width, height);
    let result = await new Promise<Blob>(resolve => { canvas.toBlob(resolve, 'image/jpeg', 0.95); });
    return result;
}
于 2018-08-13T18:48:02.120 に答える
5
fd.append("image", dataurl);

これは機能しません。PHP側では、これでファイルを保存できません。

代わりに次のコードを使用してください。

var blobBin = atob(dataurl.split(',')[1]);
var array = [];
for(var i = 0; i < blobBin.length; i++) {
  array.push(blobBin.charCodeAt(i));
}
var file = new Blob([new Uint8Array(array)], {type: 'image/png', name: "avatar.png"});

fd.append("image", file); // blob file
于 2016-03-09T14:41:49.080 に答える