vue下實現input實現圖片上傳,壓縮,拼接以及旋轉

背景

做爲一名前端工做人員,相信你們在開發系統的時候,常常有遇到須要這麼一種需求,就是須要爲用戶保存上傳的圖片,不少小白遇到這個問題的時候,都會虎軀一震,覺得會是一個棘手的問題,當你讀完這篇文章的時候,你會發現都是你瞎操做了,真相就是這麼簡單,下面進入正題:html

圖片文件上傳

如今不少項目實如今系統內保存圖片,大多數只是在系統數據庫內保存對應圖片的url,而實際的圖片資源會放在阿里等圖片服務器上,固然,也有一些項目會選擇在本身的數據庫中保存圖片base64格式的字符串,下面講一些這兩種方法的具體實現,實現以一個vue實例來講明:前端

clipboard.png

首先,咱們先要從用戶那裏獲取圖片資源,這個時候,咱們須要用到html的<input>標籤,type值爲file,指定input標籤爲文件類型的表單輸入,並將其 accept屬性設置爲"image/*",指定只接受圖片資源的文件;vue

<input type="file" accept="image/*" />

接下來,咱們就要獲取用戶選擇的文件,當用戶選擇完文件的時候,就會觸發input標籤的change事件,咱們能夠經過監聽該事件,並獲取事件對象event,來獲取圖片文件:數據庫

<input accept="image/*" style="display: none;" :name="'img-'+index" type="file" :id="'img-'+index"
  @change="fileChange($event,index)"/>

當點擊獲取文件後,咱們能夠經過$event對象,獲取$event.target.files[0]來獲取圖片資源文件對象,至於爲何要加索引值,是由於文件上傳input表單是支持多文件上傳的,只須要在input標籤上增長multiple屬性;咱們能夠看看下圖文件對象的一些屬性:canvas

clipboard.png

觀察後發現,文件對象中存在一個size屬性,代表圖片的大小,咱們能夠經過驗證該屬性的值是否爲空,來達到檢驗文件是否已經被咱們獲取到指定操做;promise

fileChange(el, index) {
    if (!el.target.files[0].size) return;
}

至此,咱們已經獲取到咱們想要的文件對象,接下來,咱們實現圖片壓縮功能,命名爲compress函數:服務器

圖片壓縮

首先,咱們要對咱們的圖片資源進行壓縮,第一步確定是獲取圖片資源吶,獲取後對其簡單的校驗;異步

compress(event) {
        var file = event.target.files;
        var reader = new FileReader(), imgFile = file[0];
        if (imgFile.type.indexOf('image') == 0) {
          reader.readAsDataURL(imgFile);
        } else {
          this.$Message.infor('文件類型僅爲圖片')
        }
 }

這裏可能有些人對FileReader對象不瞭解,FileReader 對象容許Web應用程序異步讀取存儲在用戶計算機上的文件(或原始數據緩衝區)的內容,使用 File 或 Blob 對象指定要讀取的文件或數據,這裏咱們主要是用於監聽onload來判斷是否讀取完成,讀取完成時,咱們把讀取的結果賦值給咱們新建立的Image對象,做爲後面壓縮的對象;這是時候,咱們會發現,咱們讀取後的結果實際上是一個base64格式的字符串
(很長..,我就意思意思)ide

clipboard.png

此時,咱們會發現,base64的字符串在這裏就出現了,它能夠做爲一個值複製給<img>標籤的src屬性,一樣能夠達到渲染圖片的目標,所以也有人選擇保存該格式的圖片;可是着並不是主流的方式,同時也會形成咱們數據庫過於冗餘;函數

compress(event) {
        var file = event.target.files;
        var reader = new FileReader(), imgFile = file[0];
        if (imgFile.type.indexOf('image') == 0) {
          reader.readAsDataURL(imgFile);
        } else {
          this.$Message.infor('文件類型僅爲圖片')
        }
        let img = new Image();
        reader.onload = function (e) {
          img.src = e.target.result;
        };
 }

圖片進行壓縮,咱們主要是利用canvas是實現該功能,經過canvas.getContext('2d').drawImage()方法從新繪製圖片,並利用canvas.toDataURL(type, encoderOptions)方法返回一個包含圖片展現的 dataURI,type爲圖片格式,encoderOptions爲圖片的清晰度,0到1遞增,這個壓縮的過程不難理解,思路就是獲取圖片的高寬,計算其像素大小,並與以一個本身設定的界限值進行比較,來看一下咱們大小是否須要壓縮,如例子中的ratio表示圖片寬高的壓縮比例 ,咱們是能夠實現不改寬高來修改圖片的文件大小,經過drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)從新繪製圖片,他能夠傳進九個參數,分別表明着繪製到上下文的元素,源圖像的矩形選擇框的左上角 X 座標,源圖像的矩形選擇框的左上角 Y 座標,源圖像的矩形選擇框的寬度,源圖像的矩形選擇框的高度,目標畫布的左上角在目標canvas上 X 軸的位置,目標畫布的左上角在目標canvas上 Y 軸的位置,在目標畫布上繪製圖像的寬度,在目標畫布上繪製圖像的高度;

clipboard.png

整個函數實現以下:

compress(event) {
        var file = event.target.files;
        var reader = new FileReader(), imgFile = file[0];
        if (imgFile.type.indexOf('image') == 0) {
          reader.readAsDataURL(imgFile);
        } else {
          this.$Message.infor('文件類型僅爲圖片')
        }
        let img = new Image();
        reader.onload = function (e) {
          img.src = e.target.result;
        };
        var imgP = new Promise((resolve, reject) => {
          img.onload = () => {
            var canvas = document.createElement("canvas");
            var ctx = canvas.getContext('2d');
            //    瓦片canvas
            var tCanvas = document.createElement("canvas");
            var tctx = tCanvas.getContext("2d");
            var initSize = img.src.length;
            var width = img.width;
            var height = img.height;
            //圖片像素大於400萬像素,計算壓縮到400萬如下
            var ratio;
            if ((ratio = width * height / 4000000) > 1) {
              ratio = Math.sqrt(ratio);
              width /= ratio;
              height /= ratio;
            } else {
              ratio = 1;
            }
            canvas.width = width;
            canvas.height = height;
            ctx.fillStyle = "#fff";
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            //若是圖片太大則使用瓦片繪製
            var count;
            if ((count = width * height / 1000000 > 1)) {
              count = ~~(Math.sqrt(count) + 1);//計算分紅的瓦片數
              var nw = ~~(width / count);
              var nh = ~~(height / count);
              tCanvas.width = nw;
              tCanvas.height = nh;
              for (var i = 0; i < count; i++) {
                for (var j = 0; j < count; j++) {
                  tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
                  ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh)
                }
              }
            } else {
              ctx.drawImage(img, 0, 0, width, height)
            }
            //進行最小壓縮
            var ndata = canvas.toDataURL('image/jpeg', 0.3);
            tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
            resolve(ndata)
          }
        })
        return Promise.all([imgP])
      }

圖片拼接

須要注意的一點了,上面壓縮的過程使用了瓦片繪製,可能會致使拼接過程當中不緊湊而產生一條間隙,這個只須要調整一下繪製瓦片的座標位置便可,該思想一樣能夠用於處理圖片拼接的問題,可按照上面思路進行拼接,這裏就再也不舉例子說明了,瓦片繪製就是圖片拼接的過程;

圖片旋轉

壓縮和拼接都講完啦,在對圖片進行處理,你們都有本身的看法了,或許大家還會這麼說,那若是我上傳圖片的時候,像把那些橫着排的照片,也放成豎起來,要怎麼處理,竟調整圖片放置的方向,該怎麼處理,這就須要用到canvas的rotate方法去實現了,老方法,咱們先獲取圖片對象,由於以前的壓縮是放回一個promise對象,data參數爲img的base64格式,因此咱們把旋轉函數的參數定義爲圖片來源;

rotate(imgData) {
        var img = new Image();
        img.src = imgData;
        var imgR = new Promise((resolve, reject) => {
         img.onload = ()=>{
            console.log(img.width)
             console.log(img.naturalWidth)
          }
        })
      },

這裏須要注意的是,每次咱們新建一個image對象,想要獲取其一些響應的屬性值,必定要在onload方法中,確保圖片已經加載完畢,上面的console中輸出了兩個值,width和naturalWidth,在某中條件下,他們會是相等的,好比咱們上面,也會存在不一致的時候,由於naturalWidth返回的依然是圖片的真實尺寸,而width返回的是給img標籤規定的尺寸,因此咱們須要獲取的是naturalWidth;

rotate(imgData) {
        var img = new Image();
        img.src = imgData;
        var imgR = new Promise((resolve, reject) => {
          img.onload = () => {
            let degree = 0, drawHeight, drawWidth;
            drawHeight = img.naturalHeight;
            drawWidth = img.naturalWidth;
            let maxSide = Math.max(drawWidth, drawHeight);
            if (maxSide === drawWidth) {//判斷須要旋轉的角度
              degree = 90;
            } else {
              degree = 360;
            }
            var canvas = document.createElement('canvas');
            canvas.width = drawWidth;
            canvas.height = drawHeight;
            var context = canvas.getContext('2d');
            context.translate(drawWidth/2,drawHeight/2)//這一行和下下一行的做用是修改選擇中心
            context.rotate(degree*Math.PI/180);//旋轉圖片
            context.translate(-drawWidth/2,-drawHeight/2)//這一行和上上一行的做用是修改選擇中心
            context.drawImage(img, 0, 0, drawWidth, drawHeight);
            var ndata = canvas.toDataURL('image/jpeg', 1);
            context.width = context.height = 0;
            resolve(ndata)

          }
        })

        return Promise.all([imgR])
      }

旋轉效果以下,寬大於高的,便是橫排的圖片,就會發生旋轉;
clipboard.png

總結

在vue下利用canvas實現上述功能後,發現了canvas在圖片處理這塊的強大功能,對於前端上傳圖片性能的優化會有很大的幫助;通過上述的時間,發現要實現用戶的上傳圖片前的裁剪功能,以及能夠利用canvas來實現,主要是利用drawImage控制裁剪的長度,起點座標就能夠實現,着實好用!

相關文章
相關標籤/搜索