223 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| 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
 | ||
|   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)
 | ||
|   }
 | ||
| 
 | ||
|   if (UA.oldIOS || UA.oldAndroid || UA.mQQBrowser || !navigator.userAgent) {
 | ||
|     /* 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) {
 | ||
|   /* istanbul ignore next */
 | ||
|   requireRemoteScript('//manhattan.didistatic.com/static/manhattan/mfd/image-reader/jpeg_encoder_basic.js')
 | ||
| }
 |