从0开始做一个的Vue图片/ 文件选择(上传)组件[基础向]
2017-03-30 08:48
1016 查看
之前用Vue做了一个基础的组件 vue-img-inputer ,下面就叫
上传文件很多项目都要用到,一些组件库里(ele/iview...)
样子要好看点
图片/文件选择后预览
实现拖拽选择文件
图片选择后执行某些动作(譬如uploader的上传等)
注: 下面有些地方会有些啰嗦,请选择观看
首先我们有个文件选择框,恩,长这样:
好丑啊!!我们来让它变好看点:
这里有一个 shadowDOM 的概念,简单的来说就是我们经常用到的一些HTML标准组件(例如
devtool不 设置 是看不到的):
所以呢,直接给file-input修改样式这个按钮会一直存在的!我们要么把按钮移出视线,要么就用这个按钮修改其样式。这里就修改下里面这个type=button的样式,只提供个思路,代码:
有没有想到chrome修改滚动条样式呢?哈哈,其实是一个道理,现在file-input变这样了:
好像挺简单!然而我们看到
是这样,我们可以可以把file-input整个移出视线,再找个找几个元素,通过点击这些个元素来代理原file-input的点击,呼出文件选择框呢?
自然是可以的,
所以我们可以这么写:
这样子做之后,就有一个组件的影子了,其中
因为
同时有了box,可以往里面填充任何元素,譬如一个icon
好了,基础基本上啰嗦完了,正式进入vue的实现(Vue 2.x):
这块讲
如果问你vue里你想要组件绑定一个输入值的最粗暴的方式是什么?
这样选中的文件就会传给target了,接着说图片预览
思路有两种:
选择文件后直上传然后得到网络url
用HTML5的
然后将url赋值给一个img标签
我们这里肯定选择第二种,所以先介绍下:
照例贴 MDN文档先 ,然后是代码:
当然了,这东西的兼容性有点捉鸡: IE10+, 移动端可以快乐的使用。
预览就这么完成了,下一个我们来说拖拽!
首先,放
当拖动的元素或选择文本输入有效的放置目标时,会触发此事件。
当拖动的元素或文本选择离开有效的放置目标时,会触发此事件。
当将元素或文本选择拖动到有效放置目标(每几百毫秒)上时,会触发此事件。
当在有效放置目标上放置元素或选择文本时触发此事件。
以及dataTransfer对象:在拖放交互期间传输的数据。
获取方法:
为什么要关注着几个呢?因为 浏览器是自身监听这几个拖放事件的 !!譬如你把图片或者pdf拖进浏览器里。浏览器是会试图打开这个文件的,所以我们要干掉默认行为,很简单
做完这一步,我们只需监听目标上的
其实到这里重要的点都讲了,接下来说些其他的
在父组件里获取值该怎么传怎么传
当页面中需要多个
input原本可以指定接收的文件格式,会在选择框出来的时候默认无法选择非指定格式的文件
移动端允许拍照选择
暂时就这么多了,完整的 源码在这里
有任何讲的不对不好的地方请大力指正!
vii,记录下在开发过程中遇到的 知识点 (都算比较基础,具体代码不会贴太多,都可以在 项目 仓库里看到)。
上传文件很多项目都要用到,一些组件库里(ele/iview...)
文件上传组件都是做成了标配,虽然
vii和
uploader定位还是有些差别,但实现上都有几个共同要点:
样子要好看点
图片/文件选择后预览
实现拖拽选择文件
图片选择后执行某些动作(譬如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了,接着说图片预览
图片预览
思路有两种:选择文件后直上传然后得到网络url
用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图片/ 文件选择(上传)组件[基础向]
- Vue封装一个简单轻量的上传文件组件的示例
- 一个基于Vue的移动端多文件上传插件,支持常见图片的上传。
- 解决eWebEditor上传图片提示:请选择一个有效的文件,支持的格式有(GIF|JPG|JPEG|BMP|PNG)!
- 利用vue-cropper做的关于图片裁剪、压缩、上传、预览等做的一个公共组件
- 点击一个图片弹出文件选择进行上传
- 一个支持图片上传前预览,裁剪的vue组件
- 用asp.net 多文件上传 (并且图片即使显示)并隐藏上一个文件选择器
- 解决eWebEditor上传图片提示:请选择一个有效的文件,支持的格式有(GIF|JPG|JPEG|BMP|PNG)!
- 运用SmartUpload 组件实现文件和图片的上传
- 一个自己写的真正判断文件格式,文件大小,而并不仅仅从扩展名来进行判断的上传方法,进一步加强防止软件或恶意的木马图片上传
- 一个大文件上传组件RadUpLoad(带进度条时间显示)
- 一个javascript文件上传组件.
- ASP上传漏洞 无组件上传图片文件(转载)
- 无组件上传图片之文件采用方案
- 验证上传文件的格式是否是图片的一个函数
- 文件批量上传组件分享(C# asp.net Ajax)上传图片
- eWebEditor 解决上传100K的限制及在上传文件时提示请选择一个有效的文件的解决方法
- 补充,加水印程序有一个文件过滤器,可以选择指定的图片文件
- 一个自己写的真正判断文件格式,文件大小,而并不仅仅从扩展名来进行判断的上传方法,进一步加强防止软件或恶意的木马图片上传