ホームページ ウェブフロントエンド H5 チュートリアル フロントエンドの画像のトリミングとアップロード機能を実装する方法

フロントエンドの画像のトリミングとアップロード機能を実装する方法

Oct 17, 2016 am 10:01 AM

フロントエンドはローカルファイルを直接操作できないため、ユーザーがクリックしてファイルを選択するか、フラッシュなどのサードパーティコントロールを使用することになりますが、フラッシュは日に日に減少しているため、フラッシュの使用は減少しています。まだお勧めできません。同時に、HTML5 の台頭により、多くの API 制御が提供され、フロントエンドでネイティブ API を使用して画像を処理できるようになり、バックエンド サーバーの負荷が軽減され、使いやすくなりました。

最終的な効果は次のとおりです:

フロントエンドの画像のトリミングとアップロード機能を実装する方法

それにはいくつかの機能があり、1つ目はドラッグアンドドロップをサポートすること、2つ目は圧縮すること、3つ目はトリミングと編集を行うこと、4つ目はアップロードと編集を行うことです。アップロード進行状況の表示、以下 各機能の実装を順番に紹介します:

1. ドラッグアンドドロップして画像を表示します

ドラッグアンドドロップの読み取り機能は主に HTML5 のドラッグイベントをリッスンするためのものです。これを行う方法は、API を確認することでわかります。重要なのは、ユーザーがドラッグした画像を読み取り、ローカル表示用に Base64 に変換する方法です。

ドラッグ アンド ドロップ イベントをリッスンします

varhandler={
    init:function($container){
        //需要把dragover的默认行为禁掉,不然会跳页
        $container.on("dragover",function(event){
            event.preventDefault();
        });
        $container.on("drop",function(event){
            event.preventDefault();
            //这里获取拖过来的图片文件,为一个File对象
            varfile=event.originalEvent.dataTransfer.files[0];
            handler.handleDrop($(this),file);
        });
     }
}
ログイン後にコピー

コードの 10 行目で画像ファイルを取得し、それを処理のために 11 行目に渡します。

input が使用されている場合は、input の変更イベントをリッスンします:

        $container.on("change","input[type=file]",function(event){
            if(!this.value)return;
            varfile=this.files[0];
            handler.handleDrop($(this).closest(".container"),file);
            this.value="";
        });
ログイン後にコピー

コードの 3 行目で、File オブジェクトを取得し、それを処理のために handleDrop にも渡します

次に、handleDrop 関数で、次のコンテンツを読み取ります。ファイルをbase64形式に転送します:

handleDrop:function($container,file){
    var$img=  $container.find("img");
    handler.readImgFile(file,$img,$container);
},
ログイン後にコピー

私のコードはreadImgFile関数も調整しました。主に大きなモジュールを逆アセンブルし、小さなモジュールを再利用するためのヘルパー関数がたくさんあります。

readImgFileで画像ファイルの内容を読み取ります:

FileReaderを使用してファイルを読み取ります

readImgFile:function(file,$img,$container){
    varreader=newFileReader(file);
    //检验用户是否选则是图片文件
    if(file.type.split("/")[0]!=="image"){
        util.toast("You should choose an image file");
        return;
    }  
    reader.onload=function(event){
        varbase64=event.target.result;
        handler.compressAndUpload($img,base64,file,  $container);
    }  
    reader.readAsDataURL(file);
}
ログイン後にコピー

ここでは、ファイルの内容はFileReaderを通じて読み取られ、readAsDataURLが調整されます。 このAPIは、バイナリ画像の内容をbase64形式に変換して読み取ることができます。完了後、onload イベントがトリガーされて表示され、onload にアップロードされます:

//获取图片base64内容
varbase64=event.target.result;
//如果图片大于1MB,将body置半透明
if(file.size>ONE_MB){
    $("body").css("opacity",0.5);
}
//因为这里图片太大会被卡一下,整个页面会不可操作
$img.attr("src",baseUrl);
//还原
if(file.size>ONE_MB){
    $("body").css("opacity",1);
}
//然后再调一个压缩和上传的函数
handler.compressAndUpload($img,file,$container);
ログイン後にコピー

画像のサイズが数 MB の場合、上記の 8 行目で表示されるときにスタックします。作者は Web ワーカーのマルチスレッドを使用しようとしました。この問題を解決するには、マルチスレッドにはウィンドウオブジェクトがなく、DOM を操作できないため、この問題をうまく解決できません。ページを空白にすることで、現在処理中であることをユーザーに伝え、しばらくお待ちください

ここで別の問題が発生します。つまり、写真が撮影された場合です。 iOS システムにより、水平方向に撮影されないため、ディスプレイ上で撮影された写真の回転角度に問題が発生します。次のような垂直方向に撮影された写真:

フロントエンドの画像のトリミングとアップロード機能を実装する方法

つまり、どのように撮っても、実際のiOS で保存された写真は横向きに配置されるため、ユーザーは手動で回転する必要があります。回転角度は exif データ構造に配置されており、これを読み取るには EXIF ライブラリを使用します。

exif 情報を読み取る

readImgFile:function(file,$img,$container){
    EXIF.getData(file,function(){
        varorientation=this.exifdata.Orientation,
            rotateDeg=0;
        //如果不是ios拍的照片或者是横拍的,则不用处理,直接读取
        if(typeoforientation==="undefined"||orientation===1){
            //原本的readImgFile,添加一个rotateDeg的参数
            handler.doReadImgFile(file,$img,$container,rotateDeg);
        }  
        //否则用canvas旋转一下
        else{
            rotateDeg=orientation===6?90*Math.PI/180:
                            orientation===8?-90*Math.PI/180:
                            orientation===3?180*Math.PI/180:0;
            handler.doReadImgFile(file,$img,$container,rotateDeg);
        }  
    });
}
ログイン後にコピー

角度がわかったら、 Canvas 処理では、圧縮でも Canvas を使用するため、次の圧縮画像について説明します

圧縮画像は、Canvas を使用して簡単に圧縮を実現できます。キャンバスのコンテンツを Base64 にエクスポートすると、圧縮された画像を取得できます:

//设定图片最大压缩宽度为1500px
varmaxWidth=1500;
varresultImg=handler.compress($img[0],maxWidth,file.type);
ログイン後にコピー

compress 関数で圧縮します。この関数では、最初にキャンバス オブジェクトを作成し、次にキャンバスのサイズを計算します。 Size:

compress:function(img,maxWidth,mimeType){
    //创建一个canvas对象
    varcvs=document.createElement('canvas');
    varwidth=img.naturalWidth,
        height=img.naturalHeight,
        imgRatio=width/height;
    //如果图片维度超过了给定的maxWidth 1500,
    //为了保持图片宽高比,计算画布的大小
    if(width>maxWidth){
        width=maxWidth;
        height=width/imgRatio;
    }  
    cvs.width=width;
    cvs.height=height;
}
ログイン後にコピー

次に、大きな画像を小さなキャンバスに配置し、エクスポートします。

圧縮処理

    //把大图片画到一个小画布
    varctx=cvs.getContext("2d").drawImage(img,0,0,img.naturalWidth,img.naturalHeight,0,0,width,height);
    //图片质量进行适当压缩
    varquality=width>=1500?0.5:
                    width>600?0.6:1;
    //导出图片为base64
    varnewImageData=cvs.toDataURL(mimeType,quality);
 
    varresultImg=newImage();
    resultImg.src=newImageData;
    returnresultImg;
ログイン後にコピー

最後の行は、圧縮された小さな画像を返します。これは切り取ることができます。

トリミングについて説明する前に、2番目のポイントでiOSで撮影した写真を回転する必要があると述べたので、圧縮するときに一緒に処理できます。つまり、回転する必要がある場合は、キャンバス上に描画して回転させます。

キャンバスを回転

varctx=cvs.getContext("2d");
vardestX=0,
    destY=0;
if(rotateDeg){
    ctx.translate(cvs.width/2,cvs.height/2);
    ctx.rotate(rotateDeg);
    destX=-width/2,
    destY=-height/2;
}
ctx.drawImage(img,0,0,img.naturalWidth,img.naturalHeight,destX,destY,width,height);
ログイン後にコピー

これにより、回転および圧縮された画像を取得した後、それをトリミングやトリミングに使用できます。編集

3. 画像をトリミングします

このプラグインは非常に強力で、トリミング、回転、反転をサポートしていますが、実際には画像を処理するだけです。ユーザーが行った変更を記録し、それを自分で処理します。変換されたデータは、処理のためにバックエンドに渡すことができます。 IE8と互換性がある必要がないため、ここではフロントエンドで処理します。

如下,我把一张图片,旋转了一下,同时翻转了一下:

フロントエンドの画像のトリミングとアップロード機能を実装する方法

它的输出是:

{
    height:319.2000000000001,
    rotate:45,
    scaleX:-1,
    scaleY:1,
    width:319.2000000000001
    x:193.2462838120872
    y:193.2462838120872
}
ログイン後にコピー

通过这些信息就知道了:图片被左右翻转了一下,同时顺时针转了45度,还知道裁剪选框的位置和大小。通过这些完整的信息就可以做一对一的处理。

在展示的时候,插件使用的是img标签,设置它的css的transform属性进行变换。真正的处理还是要借助canvas,这里分三步说明:

1. 假设用户没有进行旋转和翻转,只是选了简单地选了下区域裁剪了一下,那就简单很多。最简单的办法就是创建一个canvas,它的大小就是选框的大小,然后根据起点x、y和宽高把图片相应的位置画到这个画布,再导出图片就可以了。由于考虑到需要翻转,所以用第二种方法,创建一个和图片一样大小的canvas,把图片原封不动地画上去,然后把选中区域的数据imageData存起来,重新设置画布的大小为选中框的大小,再把imageData画上去,最后再导出就可以了:

简单裁剪实现

varcvs=document.createElement('canvas');
varimg=$img[0];
varwidth=img.naturalWidth,
    height=img.naturalHeight;
cvs.width=width;
cvs.height=height;
 
varctx=cvs.getContext("2d");
vardestX=0,
    destY=0;
ctx.drawImage(img,destX,destY);
 
//把选中框里的图片内容存起来
varimageData=ctx.getImageData(cropOptions.x,cropOptions.y,cropOptions.width,cropOptions.height);
cvs.width=cropOptions.width;
cvs.height=cropOptions.height;
//然后再画上去
ctx.putImageData(imageData,0,0);
ログイン後にコピー

代码14行,通过插件给的数据,保存选中区域的图片数据,18行再把它画上去

2. 如果用户做了翻转,用上面的结构很容易可以实现,只需要在第11行drawImage之前对画布做一下翻转变化:

canvas flip实现

//fip
if(cropOptions.scaleX===-1||cropOptions.scaleY===-1){
    destX=cropOptions.scaleX===-1?width*-1:0;      // Set x position to -100% if flip horizontal
    destY=cropOptions.scaleY===-1?height*-1:0;     // Set y position to -100% if flip vertical
    ctx.scale(cropOptions.scaleX,cropOptions.scaleY);
}
ctx.drawImage(img,destX,destY);
ログイン後にコピー

其它的都不用变,就可以实现上下左右翻转了,难点在于既要翻转又要旋转

3. 两种变换叠加没办法直接通过变化canvas的坐标,一次性drawImage上去。还是有两种办法,第一种是用imageData进行数学变换,计算一遍得到imageData里面,从第一行到最后一行每个像素新的rgba值是多少,然后再画上去;第二种办法,就是创建第二个canvas,第一个canvas作翻转,把它的结果画到第二个canvas,然后再旋转,最后导到。由于第二种办法相对比较简单,我们采取第二种办法:

同上,在第一个canvas画完之后:

实现旋转、翻转结合

ctx.drawImage(img,destX,destY);
//rotate
if(cropOptions.rotate!==0){
    varnewCanvas=document.createElement("canvas"),
        deg=cropOptions.rotate/180*Math.PI;
    //旋转之后,导致画布变大,需要计算一下
    newCanvas.width=Math.abs(width*Math.cos(deg))+Math.abs(height*Math.sin(deg));
    newCanvas.height=Math.abs(width*Math.sin(deg))+Math.abs(height*Math.cos(deg));
    varnewContext=newCanvas.getContext("2d");
    newContext.save();
    newContext.translate(newCanvas.width/2,newCanvas.height/2);
    newContext.rotate(deg);
    destX=-width/2,
    destY=-height/2;
    //将第一个canvas的内容在经旋转后的坐标系画上来
    newContext.drawImage(cvs,destX,destY);
    newContext.restore();
    ctx=newContext;
    cvs=newCanvas;
}
ログイン後にコピー

将第二步的代码插入第一步,再将第三步的代码插入第二步,就是一个完整的处理过程了。

最后再介绍下上传

4. 文件上传和上传进度

文件上传只能通过表单提交的形式,编码方式为multipart/form-data,这个我在《三种上传文件不刷新页面的方法讨论:iframe/FormData/FileReader》已做详细讨论,可以通过写一个form标签进行提交,但也可以模拟表单提交的格式,表单提交的格式在那篇文章已提及。

首先创建一个ajax请求:

varxhr=newXMLHttpRequest();
xhr.open('POST',upload_url,true);
varboundary='someboundary';
xhr.setRequestHeader('Content-Type','multipart/form-data; boundary='+boundary);
ログイン後にコピー

并设置编码方式,然后拼表单格式的数据进行上传:

ajax上传

vardata=img.src;
data=data.replace('data:'+file.type+';base64,','');
xhr.sendAsBinary([
    //name=data
    '--'+boundary,
        'Content-Disposition: form-data; name="data"; filename="'+file.name+'"',
        'Content-Type: '+file.type,'',
        atob(data),'--'+boundary,
    //name=docName
    '--'+boundary,
        'Content-Disposition: form-data; name="docName"','',
        file.name,
    '--'+boundary+'--'
].join('\r\n'));
ログイン後にコピー

表单数据不同的字段是用boundary的随机字符串分隔的。拼好之后用sendAsBinary发出去,在调这个函数之前先监听下它的事件,包括
1) 上传的进度:

xhr.upload.onprogress=function(event){
    if(event.lengthComputable){
        duringCallback((event.loaded/event.total)*100);
    }
};
ログイン後にコピー

这里凋duringCallback的回调函数,给这个回调函数传了当前进度的参数,用这个参数就可以设置进度条的过程了。进度条可以自己实现,或者直接上网找一个,随便一搜就有了。
2) 成功和失败:

xhr.onreadystatechange=function(){
    if(this.readyState==4){
        if(this.status==200){
            successCallback(this.responseText);
        }elseif(this.status>=400){
            if(errorCallback&&  errorCallback instanceofFunction){
                errorCallback(this.responseText);
            }      
        }      
    }
};
ログイン後にコピー

这个上传功能参考了一个JIC插件

至此整个功能就拆解说明完了,上面的代码可以兼容到IE10,FileReader的api到IE10才兼容,问题应该不大,因为微软都已经放弃了IE11以下的浏览器,为啥我们还要去兼容呢。

这个东西一来减少了后端的压力,二来不用和后端来回交互,对用户来说还是比较好的,除了上面说的一个地方会被卡一下之外。核心代码已在上面说明,完整代码和demo就不再放出来了。


このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

H5プロジェクトの実行方法 H5プロジェクトの実行方法 Apr 06, 2025 pm 12:21 PM

H5プロジェクトを実行するには、次の手順が必要です。Webサーバー、node.js、開発ツールなどの必要なツールのインストール。開発環境の構築、プロジェクトフォルダーの作成、プロジェクトの初期化、コードの書き込み。開発サーバーを起動し、コマンドラインを使用してコマンドを実行します。ブラウザでプロジェクトをプレビューし、開発サーバーURLを入力します。プロジェクトの公開、コードの最適化、プロジェクトの展開、Webサーバーの構成のセットアップ。

H5ページの生産とはどういう意味ですか? H5ページの生産とはどういう意味ですか? Apr 06, 2025 am 07:18 AM

H5ページの制作とは、HTML5、CSS3、JavaScriptなどのテクノロジーを使用したクロスプラットフォーム互換のWebページの作成を指します。そのコアは、ブラウザの解析コード、レンダリング構造、スタイル、インタラクティブ機能にあります。一般的なテクノロジーには、アニメーションエフェクト、レスポンシブデザイン、およびデータ相互作用が含まれます。エラーを回避するには、開発者をデバッグする必要があります。パフォーマンスの最適化とベストプラクティスには、画像形式の最適化、リクエスト削減、コード仕様などが含まれ、読み込み速度とコード品質を向上させます。

H5のクリックアイコンの作成方法 H5のクリックアイコンの作成方法 Apr 06, 2025 pm 12:15 PM

H5クリックアイコンを作成する手順には、次のものがあります。画像編集ソフトウェアで正方形のソース画像の準備が含まれます。 H5エディターにインタラクティブ性を追加し、クリックイベントを設定します。アイコン全体をカバーするホットスポットを作成します。ページにジャンプしたり、アニメーションのトリガーなど、クリックイベントのアクションを設定します。 HTML、CSS、およびJavaScriptファイルとしてH5ドキュメントをエクスポートします。エクスポートされたファイルをウェブサイトまたは他のプラットフォームに展開します。

H5プログラミング言語とは何ですか? H5プログラミング言語とは何ですか? Apr 03, 2025 am 12:16 AM

H5はスタンドアロンプ​​ログラミング言語ではなく、最新のWebアプリケーションを構築するためのHTML5、CSS3、およびJavaScriptのコレクションです。 1。HTML5は、Webページの構造とコンテンツを定義し、新しいタグとAPIを提供します。 2。CSS3はスタイルとレイアウトを制御し、アニメーションなどの新しい機能を紹介します。 3. JavaScriptは動的な相互作用を実装し、DOM操作と非同期要求を通じて機能を強化します。

H5ページの生産に適したアプリケーションシナリオ H5ページの生産に適したアプリケーションシナリオ Apr 05, 2025 pm 11:36 PM

H5(HTML5)は、マーケティングキャンペーンページ、製品ディスプレイページ、企業プロモーションマイクロウェブサイトなどの軽量アプリケーションに適しています。その利点は、クロスプラットフォームと豊富な対話性にありますが、その制限は複雑な相互作用とアニメーション、ローカルリソースアクセス、オフライン機能にあります。

H5でポップアップウィンドウの作り方 H5でポップアップウィンドウの作り方 Apr 06, 2025 pm 12:12 PM

H5ポップアップウィンドウの作成手順:1。トリガー方法(クリック、時間、終了、スクロール)を決定します。 2。設計コンテンツ(タイトル、テキスト、アクションボタン); 3。SETスタイル(サイズ、色、フォント、背景); 4.コードを実装する(HTML、CSS、JavaScript); 5。テストと展開。

H5ページの生産はフロントエンド開発ですか? H5ページの生産はフロントエンド開発ですか? Apr 05, 2025 pm 11:42 PM

はい、H5ページの生産は、HTML、CSS、JavaScriptなどのコアテクノロジーを含むフロントエンド開発のための重要な実装方法です。開発者は、< canvas>の使用など、これらのテクノロジーを巧みに組み合わせることにより、動的で強力なH5ページを構築します。グラフィックを描画するタグまたはJavaScriptを使用して相互作用の動作を制御します。

H5は何を参照していますか?コンテキストの探索 H5は何を参照していますか?コンテキストの探索 Apr 12, 2025 am 12:03 AM

H5ReferStoHtml5、apivotaltechnologyinwebdevelopment.1)html5introduceSnewelementsandapisforrich、dynamicwebapplications.2)Itupp ortsmultimediawithoutplugins、endancingurexperiencecrossdevices.3)semanticelementsimprovecontentstructurendseo.4)H5'srespo

See all articles