• HTML5图片拖拽预览原理及实现


    一、前言

    这两天恰好有一位同事问我怎样做一个图片预览功能。作为现代人的我们首先想到的当然是HTML5啦,其实HTML5做图片预览已经是一个老生常谈的问题了。我在这里就简单说说其中相关的一些东西,当然会附上我们的源码。在 HTML5 之前我们做图片预览主流做法有两种,第一种是通过 Flash 插件来做预览,第二种是 Ajax 实现的假预览,也就是说选择图片文件后,图片其实已经异步上传到服务器,服务器处理后返回图片路径,前端得到响应结果做出处理从而使图片显示在界面上。而有了 HTML5 之后就可以强烈鄙视上面两种做法了。

    二、FileReader

    要做图片预览功能,就不得不介绍一下 FileReader,顾名思义,它是用来读取文件的。当然新东西总会有一些顽固派排斥的,我们先来看看其兼容性如何(这不是本文讨论的重点)。

    PC端兼容列表

    移动端兼容列表 

    兼容性的话大家根据自己的需求参考一下上面的对照表,我们接着来看看 FileReader 的几个常用属性和常用方法

    属性

    1. FileReader.onload 读取完成
    2. FileReader.result 读取结果
    3. FileReader.error 读取错误
    4. FileReader.readyState 前文档的状态

    方法

    1. FileReader.abort() 中断读取-无参数
    2. FileReader.readAsArrayBuffer(file) 将文件读取为ArrayBuffer 对象 参数:文件
    3. FileReader.readAsBinaryString(file) 将文件读取为二进制码 - 参数:文件
    4. FileReader.readAsDataURL(file) 将文件读取为DataURL 参数:文件
    5. 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{
                 90%;
                margin-top: 20px;
            }
            /*每一个图片预览项容器*/
            .img-prev-item{
                 200px;
                height: 200px;
                display: inline-block;
                border:1px solid #ccc;
                text-align: center;
                border-radius: 3px;
            }
            /*图片预览容器*/
            .container .img-prev-container{
                 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{
                 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

  • 相关阅读:
    100到简单加减乘除算法的程序
    安卓日程管理系统中的bug
    绑定到Collection与绑定到CollectionViewSource的不同及解决方案
    【WPF】完美的布局不留白——解决WrapPanel右侧留白问题
    WPF里最简单的控件的Style你能写对么?(默认Style是有问题的)
    WPF Bug清单之(13)——应该出现却没有出现的ListView水平滚动条
    [WPF Bug清单]之(12)——与自定义Attached Property相关的Binding Path运行时错误
    请争取你可以拥有的——即使你不在乎
    C#编码风格——using语句的位置
    【WPF】实现QQ中的分组面板(2)——添加动画
  • 原文地址:https://www.cnblogs.com/cboyce/p/6163495.html
Copyright © 2020-2023  润新知