web前端之HTML5壓縮圖片compress image with canvas

Dusan_杜小菜發表於2016-05-24

在android開發中上傳圖片(頭像)到伺服器,壓縮圖片,直接用對應的API,獲取原圖的長寬,然後取得壓縮比例,compress到指定的質量,輸出儲存就行了,下文介紹。今天因為web活動頁廣告中需要上傳圖片,如果太大直接上傳,使用者體驗和伺服器壓力,簡直不能直視。so,一起學習下前端壓縮圖片的方式吧。

1,實現原理

compress jpeg or png files using angularjs on client side - using HTML5 Canvas & File API (based off of a J-I-C project on github).

本案例可以在github上面找到,我fork了:
https://github.com/duqian291902259/ng-image-compress
選擇一張圖片,用FileReader讀取,然後計算圖片的寬和高,確定縮放比例,在canvas上面drawImage,最後用toDataURL獲得壓縮後的圖片。
親測ok

2,web HTML頁面

<div>
    <input id="inputImage" type="file" accept="image/*" image="image1" resize-max-height="1000" resize-max-width="1000" resize-quality="0.7" resize-type="image/jpg" /> 
    <img ng-src="{{image1.compressed.dataURL}}" /> <!-- just do a console.log of {{image1}} to see what other options are in the file object -->
  </div>
  <!--<script src="http://apps.bdimg.com/libs/angular.js/1.2.16/angular.min.js"></script>-->
  <script src="scripts/angular.min.js"></script>
  <script src="scripts/app.js"></script>
  <script src="scripts/ng-image-compress.js"></script>

3,angular.min.js

angularJs,你懂得,很火的前端框架,適合移動端web頁面,可以去官網下載,也可以這下載,不是最新,湊合著用吧:
http://apps.bdimg.com/libs/angular.js/1.2.16/angular.min.js

4,app.js

可以拿到app變數,方便在在其他檔案中定義controller directive。

'use strict';
var app = angular.module('angNewsApp', []);

5,ng-image-compress.js

頁面中沒有canvas,就建立canvas控制元件,並將其隱藏。

'use strict';
app.directive('image', ['$q',
    function($q) {
        var URL = window.URL || window.webkitURL;
        var getResizeArea = function() {
            var resizeAreaId = 'fileupload-resize-area';
            var resizeArea = document.getElementById(resizeAreaId);
            if (!resizeArea) {
                resizeArea = document.createElement('canvas');
                resizeArea.id = resizeAreaId;
                resizeArea.style.visibility = 'hidden';
                document.body.appendChild(resizeArea);
            }
            return resizeArea;
        };

        /**
         * 接收JPG OR PNG圖片,返回壓縮圖片物件
         * @param {Image} sourceImgObj 原圖
         * @param {Integer} quality 輸出圖片的質量,最高100
         * @return {Image} result_image_obj 壓縮後的圖片
         */

        var jicCompress = function(sourceImgObj, options) {
            var outputFormat = options.resizeType;
            var quality = options.resizeQuality * 100 || 70;
            var mimeType = 'image/jpeg';
            if (outputFormat !== undefined && outputFormat === 'png') {
                mimeType = 'image/png';
            }
            var maxHeight = options.resizeMaxHeight || 300;
            var maxWidth = options.resizeMaxWidth || 250;
            var height = sourceImgObj.height;
            var width = sourceImgObj.width;
            // 計算寬高,限制壓縮後的最大尺寸
            if (width > height) {
                if (width > maxWidth) {
                    height = Math.round(height *= maxWidth / width);
                    width = maxWidth;
                }
            }
            else {
                if (height > maxHeight) {
                    width = Math.round(width *= maxHeight / height);
                    height = maxHeight;
                }
            }
            //畫布繪製圖片
            var cvs = document.createElement('canvas');
            cvs.width = width; //sourceImgObj.naturalWidth;
            cvs.height = height; //sourceImgObj.naturalHeight;
            var ctx = cvs.getContext('2d').drawImage(sourceImgObj, 0, 0, width, height);
            var newImageData = cvs.toDataURL(mimeType, quality / 100);
            var resultImageObj = new Image();
            resultImageObj.src = newImageData;
            return resultImageObj.src;
        };

        var resizeImage = function(origImage, options) {
            var maxHeight = options.resizeMaxHeight || 300;
            var maxWidth = options.resizeMaxWidth || 250;
            var quality = options.resizeQuality || 0.7;
            var type = options.resizeType || 'image/jpg';
            var canvas = getResizeArea();
            var height = origImage.height;
            var width = origImage.width;
            if (width > height) {
                if (width > maxWidth) {
                    height = Math.round(height *= maxWidth / width);
                    width = maxWidth;
                }
            }
            else {
                if (height > maxHeight) {
                    width = Math.round(width *= maxHeight / height);
                    height = maxHeight;
                }
            }
            //畫布的寬高與指定的相同
            canvas.width = width;
            canvas.height = height;
            //canvas上繪製圖片
            var ctx = canvas.getContext('2d');
            ctx.drawImage(origImage, 0, 0, width, height);

            // 獲取指定格式和質量的圖片資料
            return canvas.toDataURL(type, quality);
        };

        var createImage = function(url, callback) {
            var image = new Image();
            image.onload = function() {
                callback(image);
            };
            image.src = url;
        };

        var fileToDataURL = function(file) {
            var deferred = $q.defer();
            var reader = new FileReader();
            reader.onload = function(e) {
                deferred.resolve(e.target.result);
            };
            reader.readAsDataURL(file);
            return deferred.promise;
        };


        return {
            restrict: 'A',
            scope: {
                image: '=',
                resizeMaxHeight: '@?',
                resizeMaxWidth: '@?',
                resizeQuality: '@?',
                resizeType: '@?'
            },
            link: function postLink(scope, element, attrs) {

                var doResizing = function(imageResult, callback) {
                    createImage(imageResult.url, function(image) {
                        //var dataURL = resizeImage(image, scope);
                        var dataURLcompressed = jicCompress(image, scope);
                        // imageResult.resized = {
                        //  dataURL: dataURL,
                        //  type: dataURL.match(/:(.+\/.+);/)[1]
                        // };
                        imageResult.compressed = {
                            dataURL: dataURLcompressed,
                            type: dataURLcompressed.match(/:(.+\/.+);/)[1]
                        };
                        callback(imageResult);
                    });
                };

                var applyScope = function(imageResult) {
                    scope.$apply(function() {
                        console.log(imageResult);
                        if (attrs.multiple) {
                            scope.image.push(imageResult);
                        }
                        else {
                            scope.image = imageResult;
                        }
                    });
                };


                element.bind('change', function(evt) {
                    //選中檔案後,如果有多個檔案,則遍歷取出
                    if (attrs.multiple)
                        {scope.image = [];}

                    var files = evt.target.files;
                    for (var i = 0; i < files.length; i++) {
                        //create a result object for each file in files
                        var imageResult = {
                            file: files[i],
                            url: URL.createObjectURL(files[i])
                        };

                        fileToDataURL(files[i]).then(function(dataURL) {
                            console.log(dataURL)
                            imageResult.dataURL = dataURL;
                        });

                        if (scope.resizeMaxHeight || scope.resizeMaxWidth) { //resize image
                            doResizing(imageResult, function(imageResult) {
                                applyScope(imageResult);
                            });
                        }
                        else { //no resizing
                            applyScope(imageResult);
                        }
                    }
                });
            }
        };
    }
]);

6,總結

之前做了一年多的web前端。現在的前端技術,越來越6666了。js是不能寫壓縮圖片的功能的,只能用HTML5的canvas,有一定的相容性。足見HTML5的強大,此文,我只是分享的搬運工,拿去研究吧。
Dusan,291902259,OpenDeveloper。

相關文章