HTML5图片拖拽预览原理及实现
一、前言
这两天恰好有一位同事问我怎样做一个图片预览功能。作为现代人的我们首先想到的当然是HTML5啦,其实HTML5做图片预览已经是一个老生常谈的问题了。我在这里就简单说说其中相关的一些东西,当然会附上我们的源码。在 HTML5 之前我们做图片预览主流做法有两种,第一种是通过 Flash 插件来做预览,第二种是 Ajax 实现的假预览,也就是说选择图片文件后,图片其实已经异步上传到服务器,服务器处理后返回图片路径,前端得到响应结果做出处理从而使图片显示在界面上。而有了 HTML5 之后就可以强烈鄙视上面两种做法了。
二、FileReader
要做图片预览功能,就不得不介绍一下 FileReader,顾名思义,它是用来读取文件的。当然新东西总会有一些顽固派排斥的,我们先来看看其兼容性如何(这不是本文讨论的重点)。
PC端兼容列表
移动端兼容列表
兼容性的话大家根据自己的需求参考一下上面的对照表,我们接着来看看 FileReader 的几个常用属性和常用方法
属性
- FileReader.onload 读取完成
- FileReader.result 读取结果
- FileReader.error 读取错误
- FileReader.readyState 前文档的状态
方法
- FileReader.abort() 中断读取-无参数
- FileReader.readAsArrayBuffer(file) 将文件读取为ArrayBuffer 对象 参数:文件
- FileReader.readAsBinaryString(file) 将文件读取为二进制码 - 参数:文件
- FileReader.readAsDataURL(file) 将文件读取为DataURL 参数:文件
- FileReader.readAsText(file) 将文件读取为文本 参数:文件
废话不多说,我们通过代码来更直观点认识上面的属性和方法。回归到需求,做一个图片预览功能。首先理一理我们需要有的东西,第一要素当然是文件(文件选择器),第二当然是预览(容器)。
html 代码 (样式我顺手加上了)
- <!DOCTYPE html>
- <html>
- <head>
- <title>Cboyce-HTML5图片预览</title>
- <style type="text/css">
- /*主容器*/
- .container{
- width: 90%;
- margin-top: 20px;
- }
- /*图片预览容器*/
- .container .img-prev-container{
- width: 200px;
- height: 100px;
- margin:10px auto;
- border:1px solid #ccc;
- }
- /*预览图片样式*/
- .container .img-prev-container img{
- width: 100%;
- height: 100%;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="img-prev-container">
- </div>
- <input type="file" value="请选择图片" id="fileSelecter" />
- </div>
- </body>
- </html>
接下来该 FileReader 出场了
- window.onload = function(){
- //触发 change 事件
- GetDomById('fileSelecter').onchange = function(event){
- //获取文件对象
- var file = event.target.files[0];
- //创建reader对象
- var reader = new FileReader();
- //读取完成后触发
- reader.onload = function(ev){
- //获取图片的url
- var _img_src = ev.target.result;
- console.log(_img_src)
- //添加预览图片到容器框
- var img = document.createElement('img');
- img.setAttribute('src',_img_src);
- GetDomById('img-perv-div').appendChild(img);
- }
- //获取到数据的url 图片将转成 base64 格式
- reader.readAsDataURL(file);
- }
- }
- //简化 document.getElementById() 函数
- function GetDomById(id){
- return document.getElementById(id);
- }
细节注意:这里的图片格式默认转为 base64
补充说明:
event.target 属性,其特点在我们的代码中其实不忙看出来 "捕获当前事件作用的对象",通俗点来讲就是,谁触发了该事件,我就能通过该事件的 target 拿到谁。
其实上述代码还有一个小 bug "换图变成多图"。 请看下图
修复:改造 onload
- reader.onload = function(ev){
- //获取图片的url
- var _img_src = ev.target.result;
- //预览图的容器
- var _img_container = GetDomById('img-perv-div')
- //添加预览图片到容器框
- var _imgs = _img_container.getElementsByTagName('img');
- //容器中没有则创建,有则修改 src 属性
- if(!_imgs.lenght){
- _imgs[0] = document.createElement('img');
- _imgs[0].setAttribute('src',_img_src);
- _img_container.appendChild(_imgs[0]);
- }else{
- _imgs[0].setAttribute('src',_img_src);
- }
- }
解决bug
三、实现拖拽预览
上面我们已经把基础功能给完成了,接下来我们给该程序加个拓展--拖拽图片到预览框自动加载。 要完成该功能还是得靠 HTML5 的 Drag 和 drop。如果你还搞不清楚我们要做什么,那我们先来看下最终效果。
在代码开始之前我们先来了解两个实现该功能最为关键的事件。 1. dragover 拖拽一个对象到目标对象上面触发该事件 2. drop 拖放事件结束时触发。通俗来讲就是当我们拖拽一个对象到目标对象上后放开(松开鼠标左键)该对象的时候触发
接下来我们来看下代码,这里也对之前的代码做出了一些改造
- window.onload = function(){
- //预览图的容器
- var _img_container = getDomById('img-perv-div')
- //创建reader对象
- var reader = new FileReader();
- //触发 change 事件
- getDomById('fileSelecter').onchange = function(event){
- //获取文件对象
- var file = event.target.files[0];
- //读取完成后触发
- reader.onload = function(ev){
- //获取图片的url
- var _img_src = ev.target.result;
- //添加预览图片到容器框
- showPrevImg(_img_container,_img_src);
- }
- //获取到数据的url 图片将转成 base64 格式
- reader.readAsDataURL(file);
- }
- //添加拖放支持
- _img_container.addEventListener('dragover',function(ev){
- ev.preventDefault();//阻止默认事件。比如说Chrome是直接将图片用浏览器打开
- },false)
- _img_container.addEventListener('drop',function(ev){
- ev.preventDefault();
- reader.onload = function(ev){
- //获取图片的url
- var _img_src = ev.target.result;
- //图片预览处理
- showPrevImg(_img_container,_img_src);
- }
- reader.readAsDataURL(ev.dataTransfer.files[0])
- },false)
- }
- //简化 document.getElementById() 函数
- function getDomById(id){
- return document.getElementById(id);
- }
- //图片预览处理函数
- function showPrevImg(_img_container,_img_src){
- //添加预览图片到容器框
- var _imgs = _img_container.getElementsByTagName('img');
- //容器中没有则创建,有则修改 src 属性
- if(!_imgs.lenght){
- _imgs[0] = document.createElement('img');
- _imgs[0].setAttribute('src',_img_src);
- _img_container.appendChild(_imgs[0]);
- }else{
- _imgs[0].setAttribute('src',_img_src);
- }
- }
代码分析
- addEventListener('dragover',function(ev){
- ev.preventDefault();
- },false)
这段代码重点在于 ev.preventDefault();
阻止默认行为,如果我们不阻止其默认行为将会产生下面的后果
接下来要做的就是拖放结束展示图片预览效果
- _img_container.addEventListener('drop',function(ev){
- ev.preventDefault();
- reader.onload = function(ev){
- //获取图片的url
- var _img_src = ev.target.result;
- //添加预览图片到容器框
- showPrevImg(_img_container,_img_src);
- }
- reader.readAsDataURL(ev.dataTransfer.files[0])
- },false)
这里用到 event.dataTransfer 我们补充一下,我们先来看下他的定义
dataTransfer 拖曳数据传递对象,其提供了对于预定义的剪贴板格式的访问,以便在拖曳操作中使用
通俗来讲就是,我们在拖曳操作中可以使用它来操作我们拖曳的对象。比如拖图片,通过它能拿到我们所拖曳的图片对象
最后,强迫症犯了,稍微写了点样式美化了一下完整代码如下
- <!DOCTYPE html>
- <html>
- <head>
- <title>Cboyce-HTML5图片预览</title>
- <style type="text/css">
- body{
- font-family: '微软雅黑';
- }
- /*主容器*/
- .container{
- width: 90%;
- margin-top: 20px;
- }
- /*每一个图片预览项容器*/
- .img-prev-item{
- width: 200px;
- height: 200px;
- display: inline-block;
- border:1px solid #ccc;
- text-align: center;
- border-radius: 3px;
- }
- /*图片预览容器*/
- .container .img-prev-container{
- width: 200px;
- height: 156px;
- margin: 0 auto;
- border-bottom: 1px solid #ccc;
- vertical-align: middle;
- display: table-cell;
- padding: 2px;
- color: #838383;
- text-align: center
- }
- /*预览图片样式*/
- .container .img-prev-container img{
- width: 100%;
- height: auto;
- max-height: 100%;
- }
- /*label*/
- .selfile{
- background-color: #0095ff;
- color: white;
- padding: 6px 58px;
- border-radius: 5px;
- }
- /*工具条 div*/
- .tool{
- padding-top: 9px;
- }
- /*隐藏文件选择器*/
- #fileSelecter{
- display: none;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <div class="img-prev-item">
- <div class="img-prev-container" id="img-perv-div">
- 请选择图片或者<br />将图片拖拽至此
- </div>
- <div class="tool">
- <label for="fileSelecter" class="selfile">请选择图片</label>
- <input type="file" value="请选择图片" id="fileSelecter" />
- </div>
- </div>
- </div>
- <script type="text/javascript">
- window.onload = function(){
- //预览图的容器
- var _img_container = getDomById('img-perv-div')
- //创建reader对象
- var reader = new FileReader();
- //触发 change 事件
- getDomById('fileSelecter').onchange = function(event){
- //获取文件对象
- var file = event.target.files[0];
- //读取完成后触发
- reader.onload = function(ev){
- //获取图片的url
- var _img_src = ev.target.result;
- //添加预览图片到容器框
- showPrevImg(_img_container,_img_src);
- }
- //获取到数据的url 图片将转成 base64 格式
- reader.readAsDataURL(file);
- }
- //添加拖放支持
- _img_container.addEventListener('dragover',function(ev){
- //ev.stopPropagation();
- ev.preventDefault();//阻止默认事件。比如说Chrome是直接将图片用浏览器打开
- console.log('dragover')
- },false)
- // _img_container.addEventListener('dragend',function(ev){
- // ev.stopPropagation();
- // ev.preventDefault();
- // console.log('dragend')
- // },false)
- _img_container.addEventListener('drop',function(ev){
- //ev.stopPropagation();
- ev.preventDefault();
- console.log('drop')
- //console.log(ev.dataTransfer.files[0])
- reader.onload = function(ev){
- //获取图片的url
- var _img_src = ev.target.result;
- //添加预览图片到容器框
- showPrevImg(_img_container,_img_src);
- }
- reader.readAsDataURL(ev.dataTransfer.files[0])
- },false)
- }
- //简化 document.getElementById() 函数
- function getDomById(id){
- return document.getElementById(id);
- }
- function showPrevImg(_img_container,_img_src){
- _img_container.innerHTML="";
- //添加预览图片到容器框
- var _imgs = _img_container.getElementsByTagName('img');
- //容器中没有则创建,有则修改 src 属性
- if(!_imgs.lenght){
- _imgs[0] = document.createElement('img');
- _imgs[0].setAttribute('src',_img_src);
- _img_container.appendChild(_imgs[0]);
- }else{
- _imgs[0].setAttribute('src',_img_src);
- }
- }
- //接下来要做的就是拖放结束展示图片预览效果
- </script>
- </body>
- </html>
展开完整代码
运行效果如下
四、结语
基本上实现以及代码的原理也就解释到这了。其实前端做的图片预览功能大多数需求是用来上传到服务器的。不得不提到的是这里的拖拽预览虽然看起来体验不错,但是要将该文件上传就得做一些特殊处理。这个我就留到后面的博客再讲了,有问题的朋友可以直接留言。
限于笔者技术,文章观点难免有不当之处,希望发现问题的朋友帮忙指正,笔者将会及时更新。也请转载的朋友注明文章出处并附上原文链接,以便读者能及时获取到文章更新后的内容,以免误导读者。笔者力求避免写些晦涩难懂的文章(虽然也有人说这样显得高逼格,专业),尽量使用简单的用词和例子来帮助理解。如果表达上有好的建议的话也希望朋友们在评论处指出。
本文为作者原创,转载请注明出处! Cboyce
HTML5图片拖拽预览原理及实现的更多相关文章
- JavaScript实现拖拽预览,AJAX小文件上传
本地上传,提前预览(图片,视频) 1.html中div标签预览显示,button标签触发上传事件. <div id="drop_area" style="bord ...
- html5 图片转base64预览显示
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title> ...
- HTML5 图片上传预览
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8& ...
- HTML5图片上传预览
HTML5实现图片的上传预览,需要使用FileReader对象. FileReader: The FileReader object lets web applications asynchronou ...
- [javascript]——移动端 HTML5 图片上传预览和压缩
在开发移动端web网页中,我们不可避免的会遇到文件上传的功能,但由于手机图片尺寸太大,上传时间过长导致用户体验太差,就需要在上传前对图片进行一定的压缩. 在代码之前,有必要先了解我们即将使用到的几个A ...
- html5 图片上传 预览
<html><body><fieldset> <legend>测试</legend> <div class="form-gr ...
- Html5选择图片并及时预览图片
以往想要实现图片预览基本都是先传至服务器后等返回链接地址才能进行预览,使用Html5选择图片并及时预览图片的代码如下,使用起来更爽了. <!DOCTYPE html> <html l ...
- MWeb 1.4 新功能介绍一:引入文件夹到 MWeb 中管理,支持 Octpress、Jekyll 等静态博客拖拽插入图片和实时预览
之前在 MWeb 中打开非文档库中的 Markdown 文档,如果文档中有引用到本机图片,是没办法在 MWeb 中显示出来和预览的.这是因为 Apple 规定在 Mac App Store(MAS) ...
- CSS 奇思妙想 | 使用 resize 实现强大的图片拖拽切换预览功能
本文将介绍一个非常有意思的功能,使用纯 CSS 利用 resize 实现强大的图片切换预览功能.类似于这样: 思路 首先,要实现这样一个效果如果不要求可以拖拽,其实有非常多的办法. 将两张图片叠加在一 ...
随机推荐
- 关于python all(itrable)的使用 和列表表达式使用以及复习一下短路效应。
其实现在来看 并不是什么高级特性. 但是当时看到
- webrtc 的回声抵消(aec、aecm)算法简介(转)
webrtc 的回声抵消(aec.aecm)算法简介 webrtc 的回声抵消(aec.aecm)算法主要包括以下几个重要模块:1.回声时延估计 2.NLMS(归一化最小均方自适应算法) ...
- Android问题-XE5提示"[DCC Fatal Error] Project1.dpr(1): F1027 Unit not found: 'System.pas' or binary equivalents (.dcu/.o)"
问题现象:Checking project dependencies...Compiling Project1.dproj (Debug, Android)dcc command line for & ...
- 网上关于sort结构体排序都不完整,我来写一个完整版的 2014-08-09 16:50 60人阅读 评论(0) 收藏
主要参考sort函数_百度文库, 但是那篇有错误 2.结构体排序,a升,b降,c降 平板视图 打印? 01 #include <iostream> 02 #include <algo ...
- CISCO3560 VLAN配置实例
1.注意事项 1.1.交换机启动需要大约4-5分钟: 1.2.网线插入交换机接口从黄变为绿需要大约1-2分钟,即进入正常工作模式: 1.3.建议使用XP系统进行操作,2003默认没有安装超级终端,需要 ...
- TcxDBVerticalGrid优秀的编辑控件
- HDU 5438 Ponds (DFS,并查集)
题意:给定一个图,然后让你把边数为1的结点删除,然后求连通块结点数为奇的权值和. 析:这个题要注意,如果删除一些结点后,又形成了新的边数为1的结点,也应该要删除,这是坑,其他的,先用并查集判一下环,然 ...
- ASP.NET验证控件RegularExpressionValidator的常见表达式
可以输入非0和0开头的数字:“^(0*[1-9][0-9]*|[1-9][0-9]*)$”只能输入数字:“^[0-9]*$”只能输入n位的数字:“^\d{n}$”只能输入至少n位数字:“^\d{n,} ...
- DbHelperSQL和Dapper数据访问的性能对比
http://www.cnblogs.com/finesite/archive/2012/08/23/2652491.html
- 常见MFC UI界面库[转]
Xtrme toolkit,BCGControlBar,SkinMagic,AppFace,Skin++,Uskin++,SYGUI,LibUIDK,GuiToolkit,GardenUI等等,除了后 ...