JavaScript實現手機拍攝圖片的旋轉、壓縮

如今的手機拍攝的照片大小基本都在5M~10M之間。對於大圖片的上傳,不只慢,並且對用戶體驗有嚴重的影響。若是咱們對圖片清晰度的要求不是很高,能夠經過前端的壓縮能夠達到兩個目的:1.節省流量。 2.提升用戶體驗。html

1.把系統中的圖片呈如今瀏覽器中前端

html代碼:
<input id="J_takepic" accept="image/*" type="file">
<img id="J_showpic" src="" alt="show-picture">
<canvas id="J_canvas"></canvas>
js代碼:
let takePic = document.querySelecotr('#J_takepic')
let showPic = document.querySelecotr('#J_showpic')
複製代碼

獲取圖片: 目前獲取input圖片的方法主要有兩種:git

(1)FileReader (2)createObjectURLgithub

if(tackPic && showPic) {
    takePic.onchange = function(event) {
     let files = event.target.files
     let file = ''
     if (files && files.lenght > 0) {
         file = files[0]
         try {
            let URL = window.URL || window.webkitURL
            let imgURL = URL.createObjectURL(file)
            showPic.src = imgURL
            showPic.onload = function () {
                URL.revokeObjectURL(imgURL)
            }
         }
         catch (e) {
             try {
                 let fileReader = new FileReader()
                 fileReader.onload = function (event) {
                     showPic.src = event.target.result
                 }
                 fileReader.readAsDataURL(file)
             }
             catch (e) {
                 console.error('Neither createObjectURL or FileReader are supported')
             }
         }
     }
    }
}
複製代碼

2.獲取圖片旋轉度web

並非全部的手機拍攝的圖片在img標籤中均可以正常展現,在測試不一樣手機的過程當中你會驚訝的發現有些圖片居然被旋轉了90度。做爲一名程序猿,這種問題怎麼能忍。 正常狀況下,手機拍攝的照片都會攜帶地址、旋轉角度、大小等信息,經過必定的方法均可以獲取到。這裏咱們經過EXIF來獲取圖片旋轉角度。ajax

let Orientation = 1
EXIF.getData(file, function() {  
    Orientation = EXIF.getTag(this, 'Orientation');
})
Orientation的值分別爲:1(無旋轉)6(旋轉90度)3(旋轉180度)8(旋轉-90度)
複製代碼

3.旋轉並壓縮圖片canvas

旋轉圖片的實現基於canvas的rotate()方法。旋轉的中心點默認在canvas的(0,0)點。promise

利用canvas.toDataURL()進行圖片壓縮,獲得圖片的data uri的值。瀏覽器

function rotateAndCompress (image, Orientation) {
    let imgWidthOrigin = image.width
    let imgHeightOrigin = image.height
    // 壓縮圖片
    let ratio = imgWidthOrigin / imgHeightOrigin
    // 假設壓縮後的圖片的寬度爲500px
    let canvasWidth = 500
    let canvasHeight = Math.ceil(500 / ratio)
    // 旋轉並壓縮
    let canvas = document.getElementById('canvas')
    let ctx = canvas.getContext('2d')
    canvas.width = canvasWidth
    canvas.height = canvasHeight
    if (Orientation && Orientation !== 1) {
        switch (Orientation) {
            case 6:
             canvas.width = canvasHeight
             canvas.height = canvasWidth
             ctx.rotate(90 * Math.PI / 180)
             ctx.drawImage(image, 0, -canvasHeight, canvasWidth, canvasHeight)
             break
            case 3:
             ctx.rotate(Math.PI)
             ctx.drawImage(image, -canvasWidth, -canvasHeight, canvasWidth,canvasHeight)
             break
            case 8:
            // 旋轉-90度至關於旋轉了270度
             canvas.width = canvasHeight
             canvas.height = canvasWidth
             ctx.rotate(270 * Math.PI / 180)
             ctx.drawImage(image, -canvasWidth, 0, canvasWidth, canvasHeight)
             break
        }
    } esle {
        ctx.drawImage(image, 0, 0, canvasWidth, canvasHeight)
    }
}

複製代碼

到了這裏,咱們基本完成了對圖片的旋轉與壓縮。一般咱們的寫法以下:bash

let Orientation = 1
EXIF.getData(file, function() {  
    Orientation = EXIF.getTag(this, 'Orientation');
})
rotateAndCompress(Orientation) // 當即調用旋轉和壓縮的方法
複製代碼

在本身的手機上測試以後,發現圖片正常的進行了旋轉,查看壓縮後的圖片,圖片從6.8M壓縮到了1.1M,壓縮效果顯著。內心美滋滋...

換了臺手機再次測試,居然發現圖片沒有旋轉的狀況依舊沒有改變。what?明明不是已經獲取到圖片的旋轉角度了麼,爲何有的手機能夠正常旋轉,有的手機卻依舊存在問題呢?debug...

調式以後居然發現Orientation居然沒有獲取到,原來是回調函數EXIF.getTag()尚未返回結果就已經執行了rotateAndCompress()方法(高性能的手機執行速度確實快)。最終的解決方案以下:利用promise解決了異步回調的問題,等待獲取到Orientation以後再執行rotateAndCompress()

let promise = new Promise((resolve,reject) => {
  EXIF.getData(file, function() {  
    Orientation = EXIF.getTag(this, 'Orientation');
    resolve(Orientation)
  })
})
promise.then((Orientation) => {
    rotateAndCompress(Orientation)
})
複製代碼

問題順利解決,繼續心中美滋滋...

4.把canvas畫布轉換成img圖像,目前經常使用的方法有兩種 (1)toDataURL(2)toBolb

showPic.src = canvas.toDataURL(mimeType, qualityArgument)

showPic.src = canvas.toBolb(callback,mineType, qualityArgument)
複製代碼

區別: (1)toDataURL:是把圖片轉換成base64格式信息,純字符的圖片表示法。mimeType表示導出的base64圖片類型默認是png,即'image/png',也能夠爲 'image/jpeg'或webp等格式。qualityArgument表示導出圖片的質量,只有導出圖片爲jpg和webp時纔有效果,默認是0.92. (2)toBlob:是把canvas轉換成Blob文件(二進制文件),一般用於文件上傳。 XMLHttpRequest 2.0的家臣們對這些進行了詳細的講解。

function dataURItoBlob (base64Data) {
    //去掉url的頭,並轉換爲byte
    let bytes = window.atob(base64Data.split(',')[1])
    // 處理異常,將ascii碼小於0的轉換爲大於0
    let ab = new ArrayBuffer(bytes.length)
    // 生成視圖(直接針對內存):8位無符號整數,長度1個字節
    let ia = new Uint8Array(ab)
    for (let i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i)
    }
    return new Blob([ab], {
        type: 'image/jpg'
    })
}
複製代碼

5.使用FormaData將生成的blob文件上傳

利用FormData對像,咱們能夠經過js來模擬一系列表單控件。FormData的最大優勢是能夠異步上傳二進制文件。

let formData = new FormData()
formData.append('photo', blob, imageName)

$.ajax({
type: 'post',
url: "xxx/file/upload",
data: formData,
processData: false,
traditional: true,
contentType: false,
}).success(function (res) {
    console.log(res);
}).error(function () {
    console.log("upload fail");
})

複製代碼

注: EXIF獲取圖片旋轉度代碼

EXIF.getData = function(img, callback) {
    if (((self.Image && img instanceof self.Image) || (self.HTMLImageElement && img instanceof self.HTMLImageElement)) && !img.complete)
    return false
    
    if (!imageHasData(img)) {
        getImageData(img, callback)
    } else {
        if (callback) {
            callback.call(img)
        }
    }
    return true
}
EXIF.getTag = function (img, tag) {
    if (!imageHasData(img)) return
    return img.exifdata[tag]
}

function imageHasData(img) {
    return !!(img.exifdata)
}

function getImageData(img, callback) {
    function handleBinaryFile(binFile) {
        var data = findEXIFinJPEG(binFile)
        img.exifdata = data || {}
        var iptcdata = iptcdata || {}
        if (EXIF.isXmpEnabled) {
            var xmpdata = findXMPinJPEG(bindFile)
            img.xmpdata = xmpdata || {}
        }
        if (callback) {
            callback.call(img)
        }
    }
    
    
}
複製代碼
相關文章
相關標籤/搜索