基于HTML5的图片裁剪&上传组件

图片裁剪&上传功能简单实现

Posted by wanmei_lee on December 6, 2018

最近一个项目有图片裁剪并上传的需求(无需兼容IE低版本浏览器),因产品框架比较老,页面使用react、vue等框架的话,迁移成本较高,故无法直接使用这些框架中的组件。google后,找到了一个适合的第三方组件cropper.js,一个jQuery插件,使用简单。

你可能也有类似需求但不想去研究组件源码,在这里分享一下简单实现方法:

目录

  • 先上demo代码
  • 插件的使用
  • 参考文档链接

先上demo代码

<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>上传头像</title>
<link href="https://cdn.bootcss.com/cropper/3.1.3/cropper.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<style type="text/css">
    body{
        text-align: center;
    }
    #user-photo {
        width:300px;
        height:300px;
        margin-top: 10px;
    }
    #photo {
        max-width:100%;
        max-height:350px;
    }
</style>
</head>
<body>
<button class="btn btn-primary" data-target="#changeModal" data-toggle="modal">打开</button><br/>
<div class="user-photo-box">
    <img id="user-photo" src="http://cp01-ocean-400.epc.baidu.com:8082/doc/pic/item/b812c8fcc3cec3fdcd4e084dd688d43f87942791.jpg">
</div>
</div>

<div class="modal fade" id="changeModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
    <div class="modal-content">
        <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
            <h4 class="modal-title text-primary">
            <i class="fa fa-pencil"></i>
                        更换头像
            </h4>
        </div>
        <div class="modal-body">
            <div class="img-container">
                <img src="http://cp01-ocean-400.epc.baidu.com:8082/doc/pic/item/b812c8fcc3cec3fdcd4e084dd688d43f87942791.jpg" alt="" id="photo">
            </div>
        </div>
        <div class="modal-footer">
            <button class="btn btn-primary submitImgBtn">提交</button>
            <button class="btn btn-close" aria-hidden="true" data-dismiss="modal">取消</button>
        </div>
    </div>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/cropper/3.1.3/cropper.min.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script type="text/javascript">
    var initCropperInModal = function(img, modal){
        var $image = img;
        var $modal = modal;
        var options = {
            aspectRatio: 1, // 纵横比
            viewMode: 2 // 可选的显示模式
        };
        // 模态框隐藏后需要保存的数据对象
        var saveData = {};
        var blobURL;
        $modal.on('shown.bs.modal', function () {
            // 重新创建
            $image.cropper( $.extend(options, {
                ready: function () {
                    // 当剪切界面就绪后,恢复数据
                    if(saveData.canvasData){
                        $image.cropper('setCanvasData', saveData.canvasData);
                        $image.cropper('setCropBoxData', saveData.cropBoxData);
                    }
                }
            }));
        }).on('hidden.bs.modal', function () {
            // 保存相关数据
            saveData.cropBoxData = $image.cropper('getCropBoxData');
            saveData.canvasData = $image.cropper('getCanvasData');
            // 销毁并将图片保存在img标签
            $image.cropper('destroy').attr('src',blobURL);
        });
    }

    $('.submitImgBtn').on('click', function () {
        $('#photo').cropper('getCroppedCanvas',{
            width:300,
            height:300
        }).toBlob(function(blob){
            var fd = new FormData();
            fd.append("file", blob ,"file_"+Date.parse(new Date())+".png");
            $.ajax({
                url: "/user/api/editorimg", // 图片服务器地址
                type: "POST",
                data: fd,
                processData: false,  // 不处理数据
                contentType: false   // 不设置内容类型
            }).done(function (data) {
                var imgData = JSON.parse(data);
                if (imgData && imgData.link) {
                    // 防止裂图,延迟赋值操作
                    setTimeout(() => {
                        $('#user-photo').attr('src', imgData.link);
                        $('#changeModal').modal('hide');
                    }, 300);
                }
            });
        });
    });

    $(function(){
        initCropperInModal($('#photo'),$('#changeModal'));
    });
</script>
</body>
</html>

demo中写死了一个图片地址,可根据自己需要修改。图片裁剪后上传时,需要一个图片接收的地址,不写或者没有更改demo中url的话,裁剪完无法上传图片,会报错,请注意。

下面开始讲解插件具体使用。

插件的使用

因为这次需求需要在弹窗中进行裁剪,方便起见,我使用了bootstrap模态框.

依赖:

  • jQuery [https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js]
  • bootstrap [https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js]
  • FormData (将裁剪后的图片作为file上传到服务器)

引用:

<link href="https://cdn.bootcss.com/cropper/3.1.3/cropper.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/cropper/3.1.3/cropper.min.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

基础html结构

<button class="btn btn-primary" data-target="#changeModal" data-toggle="modal">打开</button><br/>
<div class="user-photo-box">
    <img id="user-photo" src="http://cp01-ocean-400.epc.baidu.com:8082/doc/pic/item/b812c8fcc3cec3fdcd4e084dd688d43f87942791.jpg">
</div>

user-photo元素为裁切图片前后显示的dom元素,可设置初始默认图片,裁剪完成后进行替换。

bootstrap模态框结构

<div class="modal fade" id="changeModal" tabindex="-1" role="dialog" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
                <h4 class="modal-title text-primary">
                <i class="fa fa-pencil"></i>
                    更换头像
                </h4>
            </div>
            <div class="modal-body">
                <div class="img-container">
                    <img src="http://cp01-ocean-400.epc.baidu.com:8082/doc/pic/item/b812c8fcc3cec3fdcd4e084dd688d43f87942791.jpg" alt="" id="photo">
                </div>
            </div>
            <div class="modal-footer">
                <button class="btn btn-primary submitImgBtn">提交</button>
                <button class="btn btn-close" aria-hidden="true" data-dismiss="modal">取消</button>
            </div>
        </div>
    </div>
</div>

注意基础html中button中data-target属性值与模态框id值一致。

img-container中的img元素的src,为弹窗show时显示的图片。项目中,根据点击button时获取应该显示的原始图片地址赋值给img-container中img的src。

关键的关键

裁剪上擦的整个操作逻辑为:

点击button -> 弹出模态框 -> 调整裁剪样式后,点提交 -> 图片上传服务器 -> 成功后赋值最新图片url -> 关闭模态框

弹出模态框
  • 关联button和modal的id
  • 监听shown.bs.modal事件,模态框show的时候,初始化cropper
提交

通过getCroppedCanvas获取裁剪后图片,利用toBlob转为blob,通过FormData的append()方法,

formData.append(name, value, filename);

将图片blob对象添加到formData对象中,然后,利用ajax上传到服务器

图片赋值

刚生成图片url时,有时候图片可能还没有完成上传到服务器,所以直接拿url赋值,很有可能裂图,故此处延迟赋值显示

关闭模态框

监听hidden.bs.modal事件,存储图片信息,并将之前初始化的cropper销毁。

至此,一个简单的裁剪上传的组件开发完成。

快动起手来,试一下吧。【实践是检验真理的唯一标准】

参考文档链接