mand-mobile/components/image-reader/image-processor.js

231 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {dataURIToArrayBuffer, dataURItoBlob} from './image-dataurl'
import {noop, requireRemoteScript} from '../_util'
const UA = (userAgent => {
const isOldIos = /OS (\d)_.* like Mac OS X/g.exec(userAgent)
const isOldAndroid = /Android (\d.*?);/g.exec(userAgent) || /Android\/(\d.*?) /g.exec(userAgent)
// IOS8.3-
// ndroid4.5-
// ios
// android
// QQ Browser
return {
oldIOS: isOldIos ? +isOldIos.pop() < 8.3 : false,
oldAndroid: isOldAndroid ? +isOldAndroid.pop().substr(0, 3) < 4.5 : false,
ios: /\(i[^;]+;( U;)? CPU.+Mac OS X/.test(userAgent),
android: /Android/g.test(userAgent),
mQQBrowser: /MQQBrowser/g.test(userAgent),
}
})(navigator.userAgent)
/**
* Get Orientation of EXIF
* @param {Object} dataURL
* @souce http://stackoverflow.com/questions/7584794/accessing-jpeg-exif-rotation-data-in-javascript-on-the-client-side
*/
/* istanbul ignore next */
function getOrientation(dataURL) {
const buffer = dataURIToArrayBuffer(dataURL)
const view = new DataView(buffer)
if (view.getUint16(0, false) !== 0xffd8) {
return -2
}
const length = view.byteLength
let offset = 2
while (offset < length) {
const marker = view.getUint16(offset, false)
offset += 2
if (marker === 0xffe1) {
const tmp = view.getUint32((offset += 2), false)
if (tmp !== 0x45786966) {
return -1
}
const little = view.getUint16((offset += 6), false) === 0x4949
offset += view.getUint32(offset + 4, little)
const tags = view.getUint16(offset, little)
offset += 2
for (let i = 0; i < tags; i++) {
if (view.getUint16(offset + i * 12, little) === 0x0112) {
return view.getUint16(offset + i * 12 + 8, little)
}
}
} else if ((marker & 0xff00) !== 0xff00) {
break
} else {
offset += view.getUint16(offset, false)
}
}
return -1
}
/* istanbul ignore next */
function getImageSize(img, orientation, maxWidth, maxHeight) {
const ret = {
width: img.width,
height: img.height,
}
if ('5678'.indexOf(orientation) > -1) {
ret.width = img.height
ret.height = img.width
}
// 如果原图小于设定,采用原图
if (ret.width < maxWidth || ret.height < maxHeight) {
return ret
}
const scale = ret.width / ret.height
if (maxWidth && maxHeight) {
if (scale >= maxWidth / maxHeight) {
if (ret.width > maxWidth) {
ret.width = maxWidth
ret.height = Math.ceil(maxWidth / scale)
}
} else {
if (ret.height > maxHeight) {
ret.height = maxHeight
ret.width = Math.ceil(maxHeight * scale)
}
}
} else if (maxWidth) {
if (maxWidth < ret.width) {
ret.width = maxWidth
ret.height = Math.ceil(maxWidth / scale)
}
} else if (maxHeight < ret.height) {
ret.width = Math.ceil(maxHeight * scale)
ret.height = maxHeight
}
// 超过这个值base64无法生成在IOS上
if (ret.width >= 3264 || ret.height >= 2448) {
ret.width *= 0.8
ret.height *= 0.8
}
return ret
}
/* istanbul ignore next */
function makeCanvas(img, orientation, maxWidth, maxHeight, quality) {
const {width, height} = getImageSize(img, orientation, maxWidth, maxHeight)
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = width
canvas.height = height
ctx.drawImage(img, 0, 0, width, height)
let base64 = null
if (CSS && CSS.supports && !CSS.supports('image-orientation:none')) {
switch (orientation) {
case 3:
ctx.rotate(180 * Math.PI / 180)
ctx.drawImage(img, -width, -height, width, height)
break
case 6:
ctx.rotate(90 * Math.PI / 180)
ctx.drawImage(img, 0, -width, height, width)
break
case 8:
ctx.rotate(270 * Math.PI / 180)
ctx.drawImage(img, -height, 0, height, width)
break
case 2:
ctx.translate(width, 0)
ctx.scale(-1, 1)
ctx.drawImage(img, 0, 0, width, height)
break
case 4:
ctx.translate(width, 0)
ctx.scale(-1, 1)
ctx.rotate(180 * Math.PI / 180)
ctx.drawImage(img, -width, -height, width, height)
break
case 5:
ctx.translate(width, 0)
ctx.scale(-1, 1)
ctx.rotate(90 * Math.PI / 180)
ctx.drawImage(img, 0, -width, height, width)
break
case 7:
ctx.translate(width, 0)
ctx.scale(-1, 1)
ctx.rotate(270 * Math.PI / 180)
ctx.drawImage(img, -height, 0, height, width)
break
default:
ctx.drawImage(img, 0, 0, width, height)
}
} else {
if ('5678'.includes(orientation)) {
ctx.drawImage(img, 0, 0, height, width)
} else {
ctx.drawImage(img, 0, 0, width, height)
}
}
if ((UA.oldIOS || UA.oldAndroid || UA.mQQBrowser || !navigator.userAgent) && window.JPEGEncoder) {
/* global JPEGEncoder */
const encoder = new JPEGEncoder()
const newImg = ctx.getImageData(0, 0, canvas.width, canvas.height)
base64 = encoder.encode(newImg, quality * 100)
} else {
base64 = canvas.toDataURL('image/jpeg', quality)
}
return base64
}
/**
* Image Process
* @param options Object: { dataUrl, width, height, quality}
* @param fn dataUrl => void
*/
export default function(options, fn = noop) {
return new Promise((resolve, reject) => {
const {dataUrl, width, height, quality} = options
const orientation = getOrientation(dataUrl)
const blob = dataURItoBlob(dataUrl)
/* istanbul ignore next */
if (orientation > 1 || quality < 1 || width || height) {
const img = new Image()
img.src = dataUrl
img.onload = () => {
const newDataUrl = makeCanvas(img, orientation, width, height, quality)
const newBlob = dataURItoBlob(newDataUrl)
fn(newDataUrl, newBlob)
resolve({
dataUrl: newDataUrl,
blob: newBlob,
})
}
img.onerror = () => {
fn(null)
reject(new Error('image load error'))
}
} else {
fn(dataUrl, blob)
resolve({
dataUrl,
blob,
})
}
})
}
// Import jpeg_encoder_basic for compatibility if necessary
if ((UA.oldIOS || UA.oldAndroid || UA.mQQBrowser || !navigator.userAgent) && !window.JPEGEncoder) {
/* istanbul ignore next */
requireRemoteScript('//manhattan.didistatic.com/static/manhattan/mfd/image-reader/jpeg_encoder_basic.js')
}