I used Vue to make a basic component vuE-IMg-inputer, which is called VII below, and recorded the knowledge points encountered in the development process (all are relatively basic, and the specific codes will not be posted too much, which can be seen in the project warehouse).
Upload file many projects need to use, some component library (ele/iview…) The file upload components are standard, and while VII and Uploader are somewhat different in orientation, they both share a few common features:
Look good
Preview images/files after selection
Implement drag and drop to select files
Perform some action after image selection (upload to Uploader, etc.)
On the firstdemo
Note: Some places below will be a little wordy, please choose to watch
basis
First we have a file selection box that looks like this:
How ugly!! Let’s make it nice:
The first method: modify your CSS
There is a concept of shadowDOM here, which simply means that some of the standard HTML components we often use (such as viedo and even scrollbars) are actually made up of several more basic DOM wrapped by the browser, which allows us to call only one tag, namely WebComponent. I’m not going to do that. Let’s take a look at what file-input looks like inside (not visible without chrome devTool) :
So, styling file-Input directly will always be there! We can either move the button out of sight or use it to style it. Type =button type=button type=button
<input type="file"/>
<style>
input {
font-size: 0; /* To remove the word 'not selected for any file', can also be arbitrarily placed */
}
/* Input > input[type=button
input::-webkit-file-upload-button {
background: #efeeee;
color: # 333;
border: 0;
padding: 40px 100px;
border-radius: 5px;
font-size: 12px;
box-shadow: 1px 1px 5px rgba(0.0.0.1), 0 0 10px rgba(0.0.0.12);
}
</style>Copy the code
Have you ever thought of chrome changing scrollbar styles? Now file-input looks like this:
Sounds simple! However, when we look at – WebKit -we should know about compatibility. This method is only available in Safari and Chrome, but not in other areas.
The second method: find a proxy for file-input
Well, what if we move file-input out of sight, find a few more elements, and click on those elements to substitute the original file-input click-to-call file selection box?
Label labels the label, give label a for attribute pointing to the unique ID of the input, so clicking on label is the same as clicking on input, so we can say:
<div class="box">
<input id="id" type="file" />
<label for="id"></label>
<! -- other element-->
</div>
Copy the code
.box {
position: relative;
}
input {
position: absolute;
left: -9999px;
}
/* Make the label fill the entire box*/
label {
position: absolute;
top: 0;left: 0;right: 0;bottom: 0;
z-index: 10; /* The z-index comes after */
}Copy the code
When you do that, you have the shadow of a component where
-
Because label fills the entire box, clicking on box selects the file
-
And with box, you can fill it with any element, like an icon
<div class="box">
<input id="id" type="file" />
<label for="id"></label>
<i class="iconfont">:)</i>
<! -... Use your imagination -->
</div>
Copy the code
Vue 2.x (vue 2.x)
File selection processing
This section talks about the acquisition and processing of file data:
v-model
What is the roughest way you can bind a value to a component in VUE? V – model! This command is actually a syntactic sugar:
<imgInputer v-model="target"></imgInputer>
<! -- defaults to the following lines -->
<imgInputer ref="x" :value="target"></imgInputer>
<script>.// Bind the input event to this component object by default!
this.$refs.x.$on('input', value => {this.target = value})
...
</script>Copy the code
So the file selects the implementation of the value transfer:
<template>
<div>
<input @change="handleFileChange" ref="inputer" ./>.</div>
</template>
<script>. props: { value: {// Bind the default value prop
default: undefined}},...// The first argument to the input change callback is an event object
methods: {
handleFileChange (e) {
let inputDOM = this.$refs.inputer;
// Retrieve file data via DOM
this.file = inputDOM.files[0];
this.errText = ' ';
let size = Math.floor(this.file.size / 1024);
if (size > ...) {
// Add a file size control
return false
}
// Fires the input event of this component object
this.$emit('input'.this.file);
// Get the name of the file
this.fileName = this.file.name;
// Add a callback here
this.onChange && this.onChange(this.file, inputDOM.value); }}...</script>Copy the code
<! - call - >
<imgInputer v-model="target"></imgInputer>Copy the code
The selected file will then be passed to Target. Image preview
Preview picture
There are two ideas:
-
Select the file and upload it directly to get the web URL
-
FileReader images using HTML5’s File API are locally converted to Base64 urls
The URL is then assigned to an IMG tag
Definitely choose the second option
FileReader
As usual, paste the MDN document first, then the code:
<template>
<div ref="box">... <input ... />// Give an img to do the preview work
<img :src="dataUrl" />
...
</div>
</template>
<sctipt>
data () {
return {
// Convert base64 code to data field
dataUrl: ' '
}
},
methods: {,
imgPreview (file) {
let self = this;
// Check whether FileReader is supported
if(! file || ! window.FileReader)return;
if (/^image/.test(file.type)) {
// Create a reader
var reader = new FileReader();
// Convert the image to base64 format
reader.readAsDataURL(file);
// Callback after successful reading
reader.onloadend = function () {
self.dataUrl = this.result;
}
}
},
handleFileChange (e) {
...
this.file = inputDOM.files[0]; .// Get the file object and preview it!
this.imgPreview(this.file); . } } </script>Copy the code
Of course, the compatibility of this stuff is a bit tricky: IE10+, mobile can be used happily.
That’s the preview done. Next, drag and drop!
Drag to select
Browser drag events
First, put the MDN document of DragEVent, focusing on the following four events and their explanation:
Dragenter Raises this event when the dragged element or selection text enters a valid drop target.
Dragleave This event is emitted when the dragged element or text chooses to leave a valid drop target.
Dragover This event is triggered when an element or text selection is dragged to a valid drop target every few hundred milliseconds.
Drop Triggers this event when an element is placed or text is selected on a valid drop target.
And dataTransfer objects: data transferred during drag-and-drop interactions. Obtain this method: event.datatransfer
Why focus on a few? Because the browser itself is listening for several drag-and-drop events!! Say you drag an image or PDF into your browser. Browsers will try to open this file, so we’ll disable the default behavior, which is very simple e.preventDefault() :
. methods: { preventDefaultEvent (eventName) {document.addEventListener(eventName, function (e) {
e.preventDefault();
}, false)
},
},
mounted () {
// Block the browser's default drag-and-drop events
['dragleave'.'drop'.'dragenter'.'dragover'].forEach(e => {
this.preventDefaultEvent(e); }); }...Copy the code
To do this, we just need to listen for the drop event on the target and modify the code slightly:
<template>
<div ref="box">.</div>
</template>
<script>. addDropSupport () {let BOX = this.$refs.box;
BOX.addEventListener('drop', (e) => {
e.preventDefault();
this.errText = ' ';
// dataTransfer carries drag data
let fileList = e.dataTransfer.files; // This is a list of file objects
// You have to drag a file
if (fileList.length === 0) {
return false
}
// Format restriction
if (fileList[0].type.indexOf('image') = = = -1) {
this.errText = 'Please select image file';
return false;
}
// Only one file can be dragged this time
if (fileList.length > 1) {
this.errText = 'Multiple files are not supported yet';
return false
}
this.handleFileChange(null, fileList[0]); })},// Add a second argument
handleFileChange (e, FILE) {
// Data copy changes, so that both options are compatible
this.file = FILE || inputDOM.files[0]; }...</script>Copy the code
I’ve actually covered all the important points here, but let’s move on to the others
upload
-
Uploader to select the image in the handleFileChange directly perform a request upload
-
How do I pass values from the parent component
Something else
-
When multiple inputers are required on a page, the id of the same input will conflict, so a unique ID is required if no input id is specified:
<template>. vue<input :id="inputId" . />.</template>
<script>. methods: { gengerateID () {let nonstr = Math.random().toString(36).substring(3.8);
if (!document.getElementById(nonstr)) {
return nonstr
} else {
return this.gengerateID()
}
},
},
mounted () {
this.inputId = this.id || this.gengerateID(); }...</script>Copy the code
-
Input can specify the format of the file to be received, but the selection box will default to a non-specified file format
<! -- Accept attribute -->
<input accept="image/*,video/*;" ./>Copy the code
-
Mobile allows photo selection
<! -- Capture attribute -->
<input capture="video" ./>Copy the code
The last
-
That’s all for now, the complete source code is here
-
Have any say wrong bad place please point out energetically!