原文:http://blog.csdn.net/sinat_17775997/article/details/58585142

之前用Vue做了一个基础的组件 vue-img-inputer ,下面就叫 vii ,记录下在开发过程中遇到的 知识点 (都算比较基础,具体代码不会贴太多,都可以在 项目 仓库里看到)。

上传文件很多项目都要用到,一些组件库里(ele/iview...) 文件上传组件 都是做成了标配,虽然 vii 和 uploader 定位还是有些差别,但实现上都有几个共同要点:

  1. 样子要好看点

  2. 图片/文件选择后预览

  3. 实现拖拽选择文件

  4. 图片选择后执行某些动作(譬如uploader的上传等)

先上 demo

注: 下面有些地方会有些啰嗦,请选择观看

基础

首先我们有个文件选择框,恩,长这样:

好丑啊!!我们来让它变好看点:

第一个方法:修改自身CSS

这里有一个 shadowDOM 的概念,简单的来说就是我们经常用到的一些HTML标准组件(例如viedo ,甚至 滚动条 )其实是由若干个更基础的DOM由浏览器封装成的,使得我们调用只要一个标签就够了,这类也就是 WebComponent ,这里具体不展开了。我们先来看下file-input的内部是如何的(chrome devtool不 设置 是看不到的):

所以呢,直接给file-input修改样式这个按钮会一直存在的!我们要么把按钮移出视线,要么就用这个按钮修改其样式。这里就修改下里面这个type=button的样式,只提供个思路,代码:

<input type="file"/>
<style>
input {
font-size: 0; /* 为了去掉‘未选择任何文件’这几个字,也可以随便弄到哪里*/
}
/* 注意不是直接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>

有没有想到chrome修改滚动条样式呢?哈哈,其实是一个道理,现在file-input变这样了:

好像挺简单!然而我们看到 -webkit- 就应该知道兼容性了,这种方法只有safari和chrome笑笑,其他GG,所以自然不能这么干。

第二个方法:给file-input找个替身

是这样,我们可以可以把file-input整个移出视线,再找个找几个元素,通过点击这些个元素来代理原file-input的点击,呼出文件选择框呢?

自然是可以的, label 标注标签, 给label一个 for 属性指向input的唯一 id ,这样点击label就相当于点击input, 所以我们可以这么写:

<div class="box">
<input id="id" type="file" />
<label for="id"></label>
<!-- other element-->
</div>
.box {
position: relative;
}
input {
position: absolute;
left: -9999px;
}
/* 使label充满整个box*/
label {
position: absolute;
top: 0;left: 0;right: 0;bottom: 0;
z-index: 10; /* 这个z-index之后说到*/
}

这样子做之后,就有一个组件的影子了,其中

  • 因为 label 充满了整个box,所以点击box就可以选择文件

  • 同时有了box,可以往里面填充任何元素,譬如一个icon

<div class="box">
<input id="id" type="file" />
<label for="id"></label>
<i class="iconfont">:)</i>
<!-- ...发挥你的想象力-->
</div>

好了,基础基本上啰嗦完了,正式进入vue的实现(Vue 2.x):

文件选择的处理

这块讲 文件数据 的获取和处理:

v-model

如果问你vue里你想要组件绑定一个输入值的最粗暴的方式是什么? v-model 啊!但是这条指令其实是一个语法糖:

<imgInputer v-model="target"></imgInputer>
<!-- 默认等同于下面几行-->
<imgInputer ref="x" :value="target"></imgInputer>
<script>
...
// 默认给这个组件对象绑定input事件!
this.$refs.x.$on('input', value => {this.target = value})
...
</script>

所以文件选择传值的实现方式:

<template>
<div>
<input @change="handleFileChange" ref="inputer" .../>
...
</div>
</template>
<script>
...
props: {
value: {
// 绑定默认的value prop
default: undefined
},
},
...
// input的change回调第一个参数是event对象
methods: {
handleFileChange (e) {
let inputDOM = this.$refs.inputer;
// 通过DOM取文件数据
this.file = inputDOM.files[0];
this.errText = ''; let size = Math.floor(this.file.size / 1024);
if (size > ...) {
// 这里可以加个文件大小控制
return false
} // 触发这个组件对象的input事件
this.$emit('input', this.file); // 这里就可以获取到文件的名字了
this.fileName = this.file.name; // 这里加个回调也是可以的
this.onChange && this.onChange(this.file, inputDOM.value); },
}
...
</script>
<!-- 调用-->
<imgInputer v-model="target"></imgInputer>

这样选中的文件就会传给target了,接着说图片预览

图片预览

思路有两种:

  1. 选择文件后直上传然后得到网络url

  2. 用HTML5的 File API 的 FileReader 图片本地转成base64格式的url

然后将url赋值给一个img标签

我们这里肯定选择第二种,所以先介绍下:

FileReader

照例贴 MDN文档先 ,然后是代码:

<template>
<div ref="box">
...
<input ... />
// 给个img来承担预览工作就行了
<img :src="dataUrl" />
...
</div>
</template>
<sctipt>
data () {
return {
// 转base64码后的data字段
dataUrl: ''
}
},
methods: {,
imgPreview (file) {
let self = this;
// 看支持不支持FileReader
if (!file || !window.FileReader) return; if (/^image/.test(file.type)) {
// 创建一个reader
var reader = new FileReader();
// 将图片将转成 base64 格式
reader.readAsDataURL(file);
// 读取成功后的回调
reader.onloadend = function () {
self.dataUrl = this.result;
}
}
},
handleFileChange (e) {
...
this.file = inputDOM.files[0];
...
// 在获取到文件对象进行预览就行了!
this.imgPreview(this.file);
...
}
}
</script>

当然了,这东西的兼容性有点捉鸡: IE10+, 移动端可以快乐的使用。

预览就这么完成了,下一个我们来说拖拽!

拖拽选择

浏览器拖拽事件

首先,放 DragEVent 的 MDN文档 ,重点是下面四个事件及解释:

  • dragenter 
    当拖动的元素或选择文本输入有效的放置目标时,会触发此事件。

  • dragleave 
    当拖动的元素或文本选择离开有效的放置目标时,会触发此事件。

  • dragover 
    当将元素或文本选择拖动到有效放置目标(每几百毫秒)上时,会触发此事件。

  • drop 
    当在有效放置目标上放置元素或选择文本时触发此事件。

以及dataTransfer对象:在拖放交互期间传输的数据。

获取方法: event.dataTransfer

为什么要关注着几个呢?因为 浏览器是自身监听这几个拖放事件的 !!譬如你把图片或者pdf拖进浏览器里。浏览器是会试图打开这个文件的,所以我们要干掉默认行为,很简单 e.preventDefault() :

...
methods: {
preventDefaultEvent (eventName) {
document.addEventListener(eventName, function (e) {
e.preventDefault();
}, false)
},
},
mounted () {
// 阻止浏览器默认的拖拽时事件,测试阻止这几个就够了,不放心就全阻止一遍吧
['dragleave', 'drop', 'dragenter', 'dragover'].forEach(e => {
this.preventDefaultEvent(e);
});
}
...

做完这一步,我们只需监听目标上的 drop 事件就行了,稍微改造下代码:

<template>
<div ref="box">
...
</div>
</template>
<script>
...
addDropSupport () {
let BOX = this.$refs.box;
BOX.addEventListener('drop', (e) => {
e.preventDefault();
this.errText = '';
// 上面给的MDN文档里有讲到dataTransfer承载拖拽数据
let fileList = e.dataTransfer.files; // 其实这就是文件对象列表了
// 总得拖一个文件吧
if (fileList.length === 0) {
return false
}
// 格式限制
if (fileList[0].type.indexOf('image') === -1) {
this.errText = '请选择图片文件';
return false;
}
// 这次限制下只能拖一个文件
if (fileList.length > 1) {
this.errText = '暂不支持多文件';
return false
}
this.handleFileChange(null, fileList[0]);
})
},
// 加入第二个参数
handleFileChange (e, FILE) {
// 数据赋值改动,这样就兼容两种选择方式了
this.file = FILE || inputDOM.files[0];
}
...
</script>

其实到这里重要的点都讲了,接下来说些其他的

上传

  • uploader 的话选择完图片在 handleFileChange 里直接执行个请求上传

  • 在父组件里获取值该怎么传怎么传

其他一些东西

  • 当页面中需要多个 inputer 时,同一个input的id会冲突,所以不指定的情况下需要个唯一id:

<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>
  • input原本可以指定接收的文件格式,会在选择框出来的时候默认无法选择非指定格式的文件

<!-- accept属性-->
<input accept="image/*,video/*;" .../>
  • 移动端允许拍照选择

<!-- capture属性-->
<input capture="video" .../>

最后

  • 暂时就这么多了,完整的 源码在这里

  • 有任何讲的不对不好的地方请大力指正!

从0开始做一个的Vue图片/ 文件选择(上传)组件[基础向]的更多相关文章

  1. HTML5实现图片文件异步上传

    原文:HTML5实现图片文件异步上传 利用HTML5的新特点做文件异步上传非常简单方便,本文主要展示JS部分,html结构.下面的代码并未使用第三发库,如果有参照,请注意一些未展现出来的代码片段.我这 ...

  2. vue+大文件分片上传

    最近公司在使用vue做工程项目,实现大文件分片上传. 网上找了一天,发现网上很多代码都存在很多问题,最后终于找到了一个符合要求的项目. 工程如下: 对项目的大文件上传功能做出分析,怎么实现大文件分片上 ...

  3. Angular4 后台管理系统搭建(10) - 做一个通用的可跨域上传文件的组件

    写的很慢,不知不觉这是第十篇了.但是我其他事情太多,只能抽空写下.现在angular4或angular2流行的上传方式是ng2-file-upload.它的功能很强大.但是我没有配置成可以跨域上传的. ...

  4. vue大文件分片上传插件

    最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表 ...

  5. 封装Vue Element的upload上传组件

    本来昨天就想分享封装的这个upload组件,结果刚写了两句话,就被边上的同事给偷窥上了,于是在我全神贯注地写分享的时候他就神不知鬼不觉地突然移动到我身边,腆着脸问我在干啥呢.卧槽你妈,当场就把我吓了一 ...

  6. ssh整合问题总结--在添加商品模块实现图片(文件)的上传

    今天在做毕设(基于SSH的网上商城项目)中碰到了一个文件上传的需求,就是在后台管理员的商品模块中,有一个添加商品,需要将磁盘上的图片上传到tomcat保存图片的指定目录中: 完成这个功能需要两个步,第 ...

  7. vue文件夹上传组件选哪个好?

    一. 功能性需求与非功能性需求 要求操作便利,一次选择多个文件和文件夹进行上传:支持PC端全平台操作系统,Windows,Linux,Mac 支持文件和文件夹的批量下载,断点续传.刷新页面后继续传输. ...

  8. div中粘贴图片并上传服务器 div中拖拽图片文件并上传服务器

    应用简介:此文主要是描述如何在前端div中直接ctrl+v 粘贴图片,并上传到服务器,包括拖拽图片文件到div中 应用场景描述:用QQ或者其它切图软件截图,在指定的div中ctrl+v 粘贴并显示,点 ...

  9. Vue 实现文件的上传

    要把文件上传的web,需要分几步? 答:三步 第一步:创建一个上传文件的标签 <input type="file" id="fileExport" @ch ...

随机推荐

  1. 【RF库XML测试】测试的XML文件说明

    文件存放路径:C:\workspace\robotframework\test_rf_api\testdata\XML.xml 文件内容: <example> <first id=& ...

  2. ch3:文件处理与异常

    如何从文件读入数据? python中的基本输入机制是基于行的: python中标准的“打开-处理-关闭”代码: the_file=open('文件全称') #处理文件中的数据 the_file.clo ...

  3. iOS开发-- 使用VVDocumenter-Xcode添加代码注释

    在开发Java代码过程中,我们只需在Eclipse中敲/**即可生成字段.方法对应的文档,简单便捷. 在Xcode如果想添加文档注释,需要花费很多时间,有没有简单.快速的方法搞定这一切? 在网上搜索了 ...

  4. 利用DB实现简单去重处理

    业务需要针对某文件进行判重操作,用Perl实现如下 #!/usr/bin/perl my %h; s/\s+$// and $h{$_}++ while <>; print "$ ...

  5. python对oracle数据库的操作

    1          Oracle数据库 1.1       Oracle环境配置&客户端连接 1.1.1          下载安装Oracle绿色版客户端instantclient: 到o ...

  6. 深入浅出MFC——MFC程序的生死因果(三)

    1. 本章主要目的:从MFC程序代码中检验出一个Windows程序原本该有的程序进入点(WinMain).窗口类注册(RegisterClass).窗口产生(CreateWindow).消息循环(Me ...

  7. 解决安装laravel/homestead vagrant环境报"A VirtualBox machine with the name 'homestead' already exists."的错误

    之前在mac上安装laravel/homestead vagrant虚拟机环境时由于参照的教程是: 每次都必须在~/Homestead目录下边运行vagrant up/halt命令,觉得实在是不方便, ...

  8. Linux命令 uname:查看系统与内核相关信息

    zh@zh:~$uname --help zh@zh:~$uname -a //所有系统相关的信息

  9. 部署OpenStack问题汇总(三)--Failed to add image

    使用glance add 上传完img文件的时候出现了下面的错误 ------------------------------------------------------------------- ...

  10. linux route命令详解

    考试题一:linux下如何添加路由(百度面试题) 以上是原题,老男孩老师翻译成如下3道题. a.如何用命令行方式给linux机器添加一个默认网关,假设网关地址为10.0.0.254? b. 192.1 ...