【jQuery插件分享】Cropper——一個簡單方便的圖片裁剪插件

插件介紹

這是一個我在寫之前的項目的途中發現的一個國人寫的jQuery圖像裁剪插件,當時想實現用戶資料的頭像上傳功能,而且可以預覽圖片,和對圖片進行簡單的裁剪、旋轉,花了很多時間纔看到了這個插件,感受功能挺全面,代碼實現起來也挺簡單,再加上用的是Bootstrap,對移動端操做也有適配,因而就用了。如今稍微有點時間就記錄一下,方便之後再用的時候查閱。另外也有對應的js版本。javascript

官方文檔(英文)

兼容性

兼容全部支持了Canvas的瀏覽器(IE9+),一小部分功能例外,具體請查看官方文檔。java

參數

viewMode

  • Type: Number
  • Default: 0
  • Options: 0,1,2,3

這個具體每一個值對應的效果我也不是很清楚,推薦在上面的官方示例裏都試一試,我都是比較喜歡2。jquery

dragMode

  • Type: String
  • Default: 'crop'
  • Options:git

    • 'crop': 在裁剪框外拖動鼠標會生成一個新的裁剪框。
    • 'move': 在裁剪框外拖動鼠標會移動原圖。
    • 'none': 在裁剪框外拖動鼠標則什麼也不作。

aspectRatio

  • Type: Number
  • Default: NaN

這個是裁剪框的縱橫比,默認是不限制的。例如1:1的頭像就寫1,16:9可寫成16 / 9github

data

  • Type: Object
  • Default: null

The previous cropped data if you had stored, will be passed to setData method automatically.web

(沒怎麼用過,都是直接用setData方法)ajax

preview

  • Type: String (jQuery selector)
  • Default: ''

預覽圖的位置,用jQuery選擇器表示。json

responsive

  • Type: Boolean
  • Default: true

在更改窗口大小後是否從新渲染cropper。

restore

  • Type: Boolean
  • Default: true

在更改窗口大小後是否恢復裁剪區域。

checkCrossOrigin

  • Type: Boolean
  • Default: true

檢查圖像是不是跨域圖像。(具體查看官方文檔)

checkOrientation

  • Type: Boolean
  • Default: true

(具體查看官方文檔)

modal

  • Type: Boolean
  • Default: true

非裁剪區域是否用黑罩遮蓋。

guides

  • Type: Boolean
  • Default: true

裁剪區域是否顯示虛線。

center

  • Type: Boolean
  • Default: true

裁剪區域正中央是否顯示+號。

highlight

  • Type: Boolean
  • Default: true

裁剪區域是否高亮顯示。

background

  • Type: Boolean
  • Default: true

是否顯示背景的黑白方格(相似PS裏透明圖層的顯示方式)。

autoCrop

  • Type: Boolean
  • Default: true

cropper初始化完成後是否自動顯示裁剪框

autoCropArea

  • Type: Number
  • Default: 0.8 (80% of the image)

自動顯示的裁剪框的大小。所以,數字應當在0~1之間。

movable

  • Type: Boolean
  • Default: true

是否容許移動原圖。(若是這裏填false那麼儘管dragMode的值是move,在裁剪框外拖動也不會移動原圖)

rotatable

  • Type: Boolean
  • Default: true

是否能夠旋轉原圖。

scalable

  • Type: Boolean
  • Default: true

是否能夠對原圖進行縱橫拉伸。

例如把原圖寬度拉長爲原來的2倍或者拉長爲原來的-1倍(即水平翻轉)。

zoomable

  • Type: Boolean
  • Default: true

是否能夠對原圖進行縮小放大。

zoomOnTouch

  • Type: Boolean
  • Default: true

是否容許在移動端上使用雙指觸摸縮放原圖。

zoomOnWheel

  • Type: Boolean
  • Default: true

是否容許使用鼠標滾輪縮放原圖。

wheelZoomRatio

  • Type: Number
  • Default: 0.1

當使用鼠標滾輪縮放時的比例。

cropBoxMovable

  • Type: Boolean
  • Default: true

是否容許移動裁剪框。

cropBoxResizable

  • Type: Boolean
  • Default: true

是否容許經過拖動裁剪框的邊框來調整裁剪框的大小。

toggleDragModeOnDblclick

  • Type: Boolean
  • Default: true

是否容許經過雙擊來在cropmove之間切換dragMode

minContainerWidth

  • Type: Number
  • Default: 200

容器寬度最小值。

minContainerHeight

  • Type: Number
  • Default: 100

容器高度最小值。

minCanvasWidth

  • Type: Number
  • Default: 0

canvas(原圖)寬度最小值。

minCanvasHeight

  • Type: Number
  • Default: 0

canvas(原圖)高度最小值。

minCropBoxWidth

  • Type: Number
  • Default: 0

剪切框寬度最小值。

Note: This size is relative to the page, not the image.

minCropBoxHeight

  • Type: Number
  • Default: 0

剪切框高度最小值。

Note: This size is relative to the page, not the image.

ready

  • Type: Function
  • Default: null

A shortcut of the "ready" event.

cropstart

  • Type: Function
  • Default: null

A shortcut of the "cropstart" event.

cropmove

  • Type: Function
  • Default: null

A shortcut of the "cropmove" event.

cropend

  • Type: Function
  • Default: null

A shortcut of the "cropend" event.

crop

  • Type: Function
  • Default: null

A shortcut of the "crop" event.

zoom

  • Type: Function
  • Default: null

A shortcut of the "zoom" event.

經常使用方法

除了"setAspectRatio","replace"和"destroy"之外,全部的方法都要在ready後才能使用。這裏只介紹幾個經常使用的方法,所有的方法請到官方文檔查閱。

方法的使用格式爲

$().cropper('method',arg0,arg1,arg2,...);

crop()

手動顯示裁剪框。

$().cropper({
  autoCrop: false,
  ready: function () {
    // Do something here
    // ...

    // And then
    $(this).cropper('crop');
  }
});

reset()

恢復所有到初始狀態。

replace(url[, onlyColorChanged])

  • url:

    • Type: String
    • A new image url.
  • onlyColorChanged (optional):

    • Type: Boolean
    • If only change the color, not the size, then the cropper only need to change the srcs of all related images, not need to rebuild the cropper. This can be used for applying filters.
    • If not present, its default value is false.

替換cropper中的圖像文件,一般第二個參數無論。

destroy()

銷燬cropper,而且會移除img標籤的src屬性的值。

getCroppedCanvas([options])

  • options (optional):

    • Type: Object
    • Properties:

      • width: the destination width of the output canvas.
      • height: the destination height of the output canvas.
      • minWidth: the minimum destination width of the output canvas, the default value is 0.
      • minHeight: the minimum destination height of the output canvas, the default value is 0.
      • maxWidth: the maximum destination width of the output canvas, the default value is Infinity.
      • maxHeight: the maximum destination height of the output canvas, the default value is Infinity.
      • fillColor: a color to fill any alpha values in the output canvas, the default value is transparent.
      • imageSmoothingEnabled: set to change if images are smoothed (true, default) or not (false).
      • imageSmoothingQuality: set the quality of image smoothing, one of "low" (default), "medium", or "high".
  • (return value):

    • Type: HTMLCanvasElement
    • A canvas drawn the cropped image.
  • Notes:

    • 輸出的canvas的縱橫比會自動適應於裁剪框的縱橫比.
    • 若是打算獲得JPEG圖像,那麼應該先設置fillColor參數,不然裁剪後的透明部分默認會由黑色填充。
  • Browser support:

獲得裁剪到的圖像的canvas,若是沒有裁剪,那麼就返回的是整個原圖圖像的canvas。

這是最重要的一個方法,經過這個方法就能夠獲得裁剪後的圖像,再使用toDataURL()獲得base64 dataURL(不指定格式的話會是png格式)或者toBlob()獲得Blob,而後就能夠很輕鬆地將圖片上傳至服務器上或者顯示在某個img標籤中了。例如:

// 轉換爲png格式的dataURL
var dataURL = $().cropper('getCroppedCanvas', {
    width:100,
    height:100
}).toDataURL('image/png');

// 轉換爲Blob後顯示在img標籤中
var URL = window.URL || window.webkitURL;
$().cropper('getCroppedCanvas', {
    width:100,
    height:100
}).toBlob(function (blob) {
    $().attr('src',URL.createObjectURL(blob));
});

簡單實例

在頁面直接使用cropper

接下來只是實現一個簡單的功能:網頁中能夠上傳圖片,而後對圖片進行裁剪,點擊肯定後會顯示出裁剪後的圖片。

代碼以下:

<!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>
        .row{
            margin-bottom: 5px;
        }
        #photo {
            max-width: 100%;
        }
        .img-preview {
            width: 100px;
            height: 100px;
            overflow: hidden;
        }
        button {
            margin-top:10px;
        }
        #result {
            width: 150px;
            height: 150px;
        }
</style>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-sm-12 text-center">
            <label for="input" class="btn btn-danger" id="">
            <span>選擇圖片</span>
            <input type="file" id="input" class="sr-only">
            </label>
        </div>
    </div>
    <div class="row">
        <div class="col-sm-6 col-sm-offset-2">
            <img src="" id="photo">
        </div>
        <div class="col-sm-2">
            <div>
                <p>
                    預覽(100*100):
                </p>
                <div class="img-preview">
                </div>
            </div>
            <button class="btn btn-primary" onclick="crop()">裁剪圖片</button>
            <div>
                <br/>
                <p>
                    結果:
                </p>
                <img src="" alt="裁剪結果" id="result">
            </div>
        </div>
    </div>
</div>
<!-- Scripts -->
<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>
        // 修改自官方demo的js
        var initCropper = function (img, input){
            var $image = img;
            var options = {
                aspectRatio: 1, // 縱橫比
                viewMode: 2,
                preview: '.img-preview' // 預覽圖的class名
            };
            $image.cropper(options);
            var $inputImage = input;
            var uploadedImageURL;
            if (URL) {
                // 給input添加監聽
                $inputImage.change(function () {
                    var files = this.files;
                    var file;
                    if (!$image.data('cropper')) {
                        return;
                    }
                    if (files && files.length) {
                        file = files[0];
                        // 判斷是不是圖像文件
                        if (/^image\/\w+$/.test(file.type)) {
                            // 若是URL已存在就先釋放
                            if (uploadedImageURL) {
                                URL.revokeObjectURL(uploadedImageURL);
                            }
                            uploadedImageURL = URL.createObjectURL(file);
                            // 銷燬cropper後更改src屬性再從新建立cropper
                            $image.cropper('destroy').attr('src', uploadedImageURL).cropper(options);
                            $inputImage.val('');
                        } else {
                          window.alert('請選擇一個圖像文件!');
                      }
                  }
              });
            } else {
                $inputImage.prop('disabled', true).addClass('disabled');
            }
        }
        var crop = function(){
            var $image = $('#photo');
            var $target = $('#result');
            $image.cropper('getCroppedCanvas',{
                width:300, // 裁剪後的長寬
                height:300
            }).toBlob(function(blob){
                // 裁剪後將圖片放到指定標籤
                $target.attr('src', URL.createObjectURL(blob));
            });
        }
        $(function(){
            initCropper($('#photo'),$('#input'));
        });
    </script>
</body>
</html>

剪切圖片

在bootstrap模態框中使用cropper

雖然在模態框中能夠像上面同樣使用cropper,甚至我之前寫的項目也是跟上面同樣,可是此次整理的時候忽然發現了一個bug:當隱藏模態框後調整瀏覽器大小(甚至按f12),再打開模態框後cropper的容器會改變,致使難以使用。因而,我在GitHub中翻找了issue,在官方的example中找到了對應的解決方法。但其實這個解決方法也是一種暴力解法,即模態框隱藏後銷燬cropper,打開後從新建立cropper,可能會有別的方法,由於不肯定會不會有別的bug,因此暫時仍是用官方的方法比較好。

代碼以下:

<!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;
    }
    .img-preview-box {
        text-align: center;
    }
    .img-preview-box > div {
        display: inline-block;;
        margin-right: 10px;
    }
    .img-preview {
        overflow: hidden;
    }
    .img-preview-box .img-preview-lg {
        width: 150px;
        height: 150px;
    }
    .img-preview-box .img-preview-md {
        width: 100px;
        height: 100px;
    }
    .img-preview-box .img-preview-sm {
        width: 50px;
        height: 50px;
        border-radius: 50%;
    }
</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="">
</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">
            <p class="tip-info text-center">
                未選擇圖片
            </p>
            <div class="img-container hidden">
                <img src="" alt="" id="photo">
            </div>
            <div class="img-preview-box hidden">
                <hr>
                <span>150*150:</span>
                <div class="img-preview img-preview-lg">
                </div>
                <span>100*100:</span>
                <div class="img-preview img-preview-md">
                </div>
                <span>30*30:</span>
                <div class="img-preview img-preview-sm">
                </div>
            </div>
        </div>
        <div class="modal-footer">
            <label class="btn btn-danger pull-left" for="photoInput">
            <input type="file" class="sr-only" id="photoInput" accept="image/*">
            <span>打開圖片</span>
            </label>
            <button class="btn btn-primary disabled" disabled="true" onclick="sendPhoto();">提交</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, input, modal){
        var $image = img;
        var $inputImage = input;
        var $modal = modal;
        var options = {
            aspectRatio: 1, // 縱橫比
            viewMode: 2,
            preview: '.img-preview' // 預覽圖的class名
        };
        // 模態框隱藏後須要保存的數據對象
        var saveData = {};
        var URL = window.URL || window.webkitURL;
        var blobURL;
        $modal.on('show.bs.modal',function () {
            // 若是打開模態框時沒有選擇文件就點擊「打開圖片」按鈕
            if(!$inputImage.val()){
                $inputImage.click();
            }
        }).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);
        });
        if (URL) {
            $inputImage.change(function() {
                var files = this.files;
                var file;
                if (!$image.data('cropper')) {
                    return;
                }
                if (files && files.length) {
                    file = files[0];
                    if (/^image\/\w+$/.test(file.type)) {
    
                        if(blobURL) {
                            URL.revokeObjectURL(blobURL);
                        }
                        blobURL = URL.createObjectURL(file);
    
                        // 重置cropper,將圖像替換
                        $image.cropper('reset').cropper('replace', blobURL);
    
                        // 選擇文件後,顯示和隱藏相關內容
                        $('.img-container').removeClass('hidden');
                        $('.img-preview-box').removeClass('hidden');
                        $('#changeModal .disabled').removeAttr('disabled').removeClass('disabled');
                        $('#changeModal .tip-info').addClass('hidden');
    
                    } else {
                        window.alert('請選擇一個圖像文件!');
                    }
                }
            });
        } else {
            $inputImage.prop('disabled', true).addClass('disabled');
        }
    }

    var sendPhoto = function(){
        $('#photo').cropper('getCroppedCanvas',{
            width:300,
            height:300
        }).toBlob(function(blob){
            // 轉化爲blob後更改src屬性,隱藏模態框
            $('#user-photo').attr('src',URL.createObjectURL(blob));
            $('#changeModal').modal('hide');
        });
    }

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

打開模態框

打開圖片

使用cropper來上傳圖片到服務器

因爲cropper能夠獲得兩種裁剪後圖片的數據(即blob和dataURL),因此對應的上傳到後臺也會有兩種方法,在這裏我只寫一種使用ajax上傳base64 dataURL的,另外一種方法若是有興趣,能夠本身嘗試。

頁面中,將上面的sendPhoto方法改成:

var sendPhoto = function () {
    // 獲得PNG格式的dataURL
    var photo = $('#photo').cropper('getCroppedCanvas', {
        width: 300,
        height: 300
    }).toDataURL('image/png');

    $.ajax({
        url: '上傳地址', // 要上傳的地址
        type: 'post',
        data: {
            'imgData': photo
        },
        dataType: 'json',
        success: function (data) {
            if (data.status == 0) {
                // 將上傳的頭像的地址填入,爲保證不載入緩存加個隨機數
                $('.user-photo').attr('src', '頭像地址?t=' + Math.random());
                $('#changeModal').modal('hide');
            } else {
                alert(data.info);
            }
        }
    });
}

後臺中,Java的主要代碼以下:(使用了jdk8的Base64,,若是是低版本請自行替換)

/**
     * 將Base64位編碼的圖片進行解碼,並保存到指定目錄
     */
    public static void decodeBase64DataURLToImage(String dataURL, String path, String imgName) throws IOException {
        // 將dataURL開頭的非base64字符刪除
        String base64 = dataURL.substring(dataURL.indexOf(",") + 1);
        FileOutputStream write = new FileOutputStream(new File(path + imgName));
        byte[] decoderBytes = Base64.getDecoder().decode(base64);
        write.write(decoderBytes);
        write.close();
    }

小結

cropper能作到的事情還不少,這裏只是簡單使用了一下,更多功能能夠在有想法的再研究下。

這是針對之前項目用的cropper的一個整理,結果由於當初沒有看官方例子,途中發現了在模態框中使用的一個bug,之後會注意這方面。另外,整理這部分資料時也參考了很多的網絡資料,在這裏就不一一記錄了。

最後,因爲本人能力有限,若發現錯誤但願能指出,本人會及時改正,很是感謝。

相關文章
相關標籤/搜索