1. 程式人生 > >上傳圖片裁剪外掛(基於cropper.js的封裝)

上傳圖片裁剪外掛(基於cropper.js的封裝)

如題,這樣的功能在開發中是非常常見的,cropper.js是一款很好的工具,網上都有詳細的介紹,這篇文章提示很多友好的API和事件cropper.js 裁剪圖片並上傳(文件翻譯+demo)

cropper.js需要藉助jquery來實現,所以我這裡的樣式抄襲了jquery頭像上傳剪裁外掛cropper這個工具,具體功能沒有copy,只是覺得這一款介面符合使用者體驗,可以實時看到自己的裁剪效果(利用css3特性)

不多說上程式碼,樣式上我用scss寫的,反正最終是webpack打包的^_^,不支援ie9以下

html主要程式碼

<div class="update-bg" id
="J-update-wrapper"> <input type="file" id="J-update-img-file" style="display: none"> <div class="update-wrapper"> <div class="update-header clearfix"> <h3 class="text">上傳圖片</h3> <button class="btn close J-close">x</button> </div
> <div class="update-main"> <button class="btn update-btn J-update-btn">請選擇圖片</button> <div class="update-body clearfix"> <div class="body-left"> <img id="J-update-img" class="update-img" src="" alt=""
> </div> <div class="body-right"> <img src="" class="user-header" id="J-update-user-header"> </div> </div> <div class="operation-wrapper clearfix"> <div class="operation-left"> <div class="left-container"> <button class="btn J-operation-btn" data-type="rote-left">向左旋轉</button> <button class="btn J-operation-btn" data-type="rote-right">向右旋轉</button> </div> <div class="right-container"> <button class="btn J-operation-btn" data-type="scale-b">放大</button> <button class="btn J-operation-btn" data-type="scale-s">縮小</button> </div> </div> <div class="operation-right"> <button class="btn J-operation-save">儲存圖片</button> </div> </div> </div> </div> </div>

scss程式碼

// 主色調
$btnColor: $basic_color;
.update-bg {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    background-color: rgba(0, 0, 0, .7);
    display: none;
    z-index: 999;
    .btn {
        display: inline-block;
        text-align: center;
        vertical-align: middle;
        touch-action: manipulation;
        cursor: pointer;
        padding: 6px 12px;
        background: transparent;
        border: 0;
        border-radius: 4px;
    }
    .clearfix:after {
        display: block;
        content: '';
        clear: both;
    }
    .update-wrapper {
        position: relative;
        margin: 50px auto;
        width: 900px;
        min-height: 580px;
        background-color: #fff;
        border: 1px solid rgba(0, 0, 0, .2);
        box-shadow: 0 5px 15px rgba(0, 0, 0, .5);
        border-radius: 6px;
        .update-header {
            padding: 15px;
            min-height: 20px;
            border-bottom: 1px solid #e5e5e5;
            .text {
                font-size: 18px;
                font-weight: 500;
                float: left;
            }
            .close {
                float: right;
                padding: 0;
                font-size: 22px;
                color: #000;
                text-shadow: 0 1px 0 #fff;
                font-weight: bold;
                opacity: .2;
            }
        }
        .update-main {
            padding: 15px 30px;
            .update-btn {
                height: 35px;
                background-color: $btnColor;
                border: $btnColor;
                color: #fff;
            }
            .update-body {
                margin-top: 15px;                    
                .body-left {
                    float: left;
                    width: 620px;
                    height: 360px;
                    box-shadow: inset 0 0 5px rgba(0,0,0,.25);
                    background-color: #fcfcfc;
                    overflow: hidden;
                    .update-img {
                        display: none;
                        width: 0;
                        height: 0;
                    }
                }
                .body-right {
                    float: right;
                    width: 180px;
                    height: 180px;
                    border: 1px solid #eee;
                    border-radius: 4px;
                    background-color: #fff;
                    overflow: hidden;
                    .user-header {
                        display: none;
                        max-width: none;
                        height: auto;
                    }
                }
            }
            .operation-wrapper {
                margin-top: 15px;      
                .operation-left {
                    float: left;
                    width: 620px;
                    .btn {
                        height: 35px;
                        background-color: $btnColor;
                        border: $btnColor;
                        color: #fff;
                    }
                    .left-container {
                        float: left;
                        .btn {
                            margin-right: 10px;
                        }
                    }
                    .right-container {
                        float: right;
                        .btn {
                            margin-left: 10px;
                        }
                    }
                }
                .operation-right {
                    float: right;
                    width: 180px;
                    .btn {
                        width: 100%;
                        height: 35px;
                        background-color: $btnColor;
                        border: $btnColor;
                        color: #fff;
                    }
                }
            }
        }
    }
}

js部分

// 重置樣式
import 'common/style/reset.styl';
import './index.scss';
// cropper引入 樣式和js檔案都需要哦
import '../../../node_modules/cropper/dist/cropper.min.css';
import '../../../node_modules/cropper/dist/cropper.min.js';
// 儲存上傳圖片的資訊
const imgInfo = {
    width: null,
    height: null,
    rotate: 0
};
// 哨兵變數 對按鈕的響應作判斷
let cropperIng;
// jq物件 cropper的宿主
let cropperTarget;
// jq物件 裁剪實時呈現的效果影象
let userImg;
// 裁剪圖片功能封裝類
export default class UpdateCropper {
    // 回撥方法
    constructor(callBack) {
        this.callBack = callBack;
    }
    // 初始化
    init() {
        imgInfo.width = null;
        imgInfo.height = null;
        imgInfo.rotate = 0;
        imgInfo.scale = 1;
        cropperIng = false;
        cropperTarget = $('#J-update-img');
        userImg = $('#J-update-user-header');
        $('#J-update-wrapper').fadeIn('slow');
        this.addEvent();
    }
    // 新增事件
    addEvent() {
        const _this = this;
        $('#J-update-img-file').on('change', function (e) {
            // 這樣的操作不支援ie9及以下
            if (!this.files[0].type.match(/image.*/)) {
                alert('請選擇正確的圖片!');
            }
            const imgSrc = URL.createObjectURL(this.files[0]);
            _this.createCropper(imgSrc);
        });
        $('#J-update-wrapper').on('click', '.J-close', () => {
            this.destroy();
        });
        $('#J-update-wrapper').on('click', '.J-update-btn', function() {
            document.getElementById('J-update-img-file').click();
        });
        // 操作按鈕
        $('#J-update-wrapper').on('click', '.J-operation-btn', function() {
            _this.operation($(this));
        });
        $('#J-update-wrapper').on('click', '.J-operation-save', () => {
            this.getCroppedCanvas();
        });
        // J-close
    }
    // 銷燬
    destroy() {
        this.removeEvent();
        cropperTarget.cropper('destroy');
        userImg.attr('src', '').hide();
        cropperTarget.attr('src', '').hide();
        $('#J-update-wrapper').fadeOut('slow');
        // $('#J-update-wrapper').hide();
    }
    // 移除事件
    removeEvent() {
        $('#J-update-img-file').off();
        $('#J-update-wrapper').off();
    }
    /**
     * 建立cropper容器 巴拉巴拉
     * @param {string} src 
     */
    createCropper(src) {
        document.getElementById('J-update-img').onload = () => {
            const elem = document.getElementById('J-update-img');
            // 獲取圖片真實寬高,用於後期比例計算
            imgInfo.width = elem.naturalWidth;
            imgInfo.height = elem.naturalHeight;
            userImg.attr('src', src).show();
            cropperIng = true;
            cropperTarget.cropper('destroy');
            cropperTarget.cropper({
                aspectRatio: 9 / 9,
                viewMode: 1,
                // 建立 使用者操作都會改變顯示物件
                crop: (e) => {
                    this.changeImg(e.detail);
                }
            });
        };
        cropperTarget.attr('src', src).show();
    }
    // 右側顯示物件改變
    changeImg(detail) {
        const num = 180 / detail.width;
        userImg.css({
            transform: `translate(-${detail.x * num}px, -${detail.y * num}px) rotate(${imgInfo.rotate}deg)`,
            width: `${imgInfo.width * num}px`,
            height: `${imgInfo.heigh * num}px`
        });
    }
    // 操作方法 旋轉縮放都是在基於上一次操作的基礎上
    operation(target) {
        if (!cropperIng) {
            return;
        }
        const type = target.data('type');
        switch (type) {
            // 左旋90case 'rote-left':
                imgInfo.rotate -= 90;
                $('#J-update-img').cropper('rotate', -90);
                break;
            // 右旋90case 'rote-right':
                imgInfo.rotate += 90;
                $('#J-update-img').cropper('rotate', 90);
                break;
            // 放大一下下
            case 'scale-b':
                $('#J-update-img').cropper('zoom', 0.1);
                break;
            case 'scale-s':
            // 縮小一下下
                $('#J-update-img').cropper('zoom', -0.1);
                break;
        }
    }
    // 轉換成base64輸出
    getCroppedCanvas() {
        if (!cropperIng) {
            return;
        }
        const cas = cropperTarget.cropper('getCroppedCanvas');
        const base64 = cas.toDataURL('image/jpeg');
        this.callBack(base64);
        this.destroy();
    }
}

演示一下效果(GIF圖)

這裡寫圖片描述

縮放,裁剪,旋轉這些基本功能都全了,如果有興趣可以拓展

github地址

專案github地址 大致思路就是這樣,如果有其它功能就自行拓展吧~