192 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Vue
		
	
	
	
| <template>
 | |
|   <div class="md-image-reader">
 | |
|     <input
 | |
|       class="md-image-reader-file"
 | |
|       type="file"
 | |
|       :key="inputTmpKey"
 | |
|       :name="name"
 | |
|       :accept="mimeType"
 | |
|       :capture="isCameraOnly"
 | |
|       :multiple="isMultiple"
 | |
|       @change="$_onFileChange"/>
 | |
|   </div>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| import createImageReader from './image-reader'
 | |
| import {dataURItoBlob} from './image-dataurl'
 | |
| import {functionToUrl, randomId} from '../_util'
 | |
| 
 | |
| const ERROR = {
 | |
|   '100': 'browser does not support',
 | |
|   '101': 'picture size is beyond the preset',
 | |
|   '102': 'picture read failure',
 | |
|   '103': 'the number of pictures exceeds the limit',
 | |
| }
 | |
| 
 | |
| export default {
 | |
|   name: 'md-image-reader',
 | |
| 
 | |
|   props: {
 | |
|     name: {
 | |
|       type: String,
 | |
|       default() {
 | |
|         return randomId('image-reader')
 | |
|       },
 | |
|     },
 | |
|     size: {
 | |
|       type: [String, Number],
 | |
|       default: 0,
 | |
|     },
 | |
|     mime: {
 | |
|       type: Array,
 | |
|       default() {
 | |
|         return []
 | |
|       },
 | |
|     },
 | |
|     isCameraOnly: {
 | |
|       type: Boolean,
 | |
|       default: false,
 | |
|     },
 | |
|     isMultiple: {
 | |
|       type: Boolean,
 | |
|       default: false,
 | |
|     },
 | |
|     amount: {
 | |
|       type: Number,
 | |
|       default: 0,
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   data() {
 | |
|     return {
 | |
|       inputTmpKey: Date.now(), // trigger of refreshing input file
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   computed: {
 | |
|     mimeType() {
 | |
|       if (this.mime.length) {
 | |
|         let mimeStr = ''
 | |
|         this.mime.forEach(type => {
 | |
|           mimeStr += `image/${type},`
 | |
|         })
 | |
|         return mimeStr.substring(0, mimeStr.length - 1)
 | |
|       } else {
 | |
|         return 'image/*'
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   methods: {
 | |
|     // MARK: private methods
 | |
|     $_emitter(event, data) {
 | |
|       this.$emit(event, this.name, data)
 | |
|     },
 | |
|     $_openWebWorker(fn) {
 | |
|       /* istanbul ignore next */
 | |
|       return new Worker(functionToUrl(fn))
 | |
|     },
 | |
|     $_closeWebWorker(worker) {
 | |
|       /* istanbul ignore next */
 | |
|       worker.terminate()
 | |
|     },
 | |
|     $_readFile(fileElement) {
 | |
|       const size = +this.size * 1000
 | |
|       const files = fileElement.files
 | |
|       let worker
 | |
|       let count = 0
 | |
| 
 | |
|       /* istanbul ignore if */
 | |
|       if (window.Worker) {
 | |
|         worker = this.$_openWebWorker(createImageReader)
 | |
|         // worker send
 | |
|         worker.postMessage({
 | |
|           files,
 | |
|           size: size,
 | |
|           isWebWorker: true,
 | |
|         })
 | |
|         // worker response
 | |
|         worker.onmessage = evt => {
 | |
|           this.$_onReaderComplete(evt.data)
 | |
| 
 | |
|           count++
 | |
| 
 | |
|           // shut down worker after all files have been read
 | |
|           if (count === files.length) {
 | |
|             this.$_closeWebWorker(worker)
 | |
|           }
 | |
|         }
 | |
|       } else {
 | |
|         // generate imageReader
 | |
|         const imageReader = createImageReader(window)
 | |
|         imageReader({
 | |
|           files,
 | |
|           size: size,
 | |
|           isWebWorker: false,
 | |
|           complete: this.$_onReaderComplete,
 | |
|         })
 | |
|       }
 | |
|     },
 | |
|     $_cleaeFile() {
 | |
|       this.inputTmpKey = Date.now()
 | |
|     },
 | |
| 
 | |
|     // MARK: events handler
 | |
|     $_onFileChange(event) {
 | |
|       const fileElement = event.target
 | |
| 
 | |
|       /* istanbul ignore next */
 | |
|       if (fileElement.files && fileElement.files.length) {
 | |
|         this.$_emitter('select', {
 | |
|           files: Array.prototype.slice.call(fileElement.files),
 | |
|         })
 | |
| 
 | |
|         // error 超出每次上传最大张数
 | |
|         if (this.amount && fileElement.files.length > this.amount) {
 | |
|           this.$_emitter('error', {
 | |
|             code: '103',
 | |
|             msg: ERROR['103'],
 | |
|           })
 | |
|           this.$_cleaeFile()
 | |
|           return
 | |
|         }
 | |
| 
 | |
|         this.$_readFile(fileElement)
 | |
|       }
 | |
|     },
 | |
|     $_onReaderComplete({errorCode, dataUrl, file}) {
 | |
|       if (errorCode) {
 | |
|         this.$_emitter('error', {
 | |
|           code: errorCode,
 | |
|           msg: ERROR[errorCode],
 | |
|         })
 | |
|         return
 | |
|       }
 | |
| 
 | |
|       this.$_emitter('complete', {
 | |
|         blob: dataURItoBlob(dataUrl),
 | |
|         dataUrl: dataUrl,
 | |
|         file,
 | |
|       })
 | |
|       this.$_cleaeFile()
 | |
|     },
 | |
|   },
 | |
| }
 | |
| 
 | |
| </script>
 | |
| 
 | |
| <style lang="stylus">
 | |
| .md-image-reader
 | |
|   position absolute
 | |
|   width 100%
 | |
|   height 100%
 | |
|   z-index 100
 | |
|   -webkit-tap-highlight-color rgba(0,0,0,0)
 | |
| 
 | |
| .md-image-reader-file
 | |
|   position absolute
 | |
|   width 100%
 | |
|   height 100%
 | |
|   opacity 0
 | |
|   -webkit-tap-highlight-color rgba(0,0,0,0)
 | |
| </style>
 |