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)
        }
    }
    
    
}
复制代码
相关文章
相关标签/搜索