基于layui+cropper.js实现上传图片的裁剪功能

  最近因项目需求,需要在上传图片的时候先对图片裁剪,然后在上传,所以就有了本文的出现。

  开始正文之前,要提一下这个图片的裁剪:图片的裁剪,有前端裁剪,也可以后端裁剪

  前端的裁剪我知道的可以分为这么两种:flash一种,canvas一种。现在用的多的是canvas这种。

  其实裁剪最本质的原理:通过工具获取到图片(不是JS那种获取DOM的方式,比如flash获取到图片,对图片任意操作,canvas也是一样,将图片放在画布上,任意操作)

  本文使用的是canvas这种方式,借助的是cropper.js实现图片的裁剪,详细可以去官网看看,个人认为官网就是做好的demo。

  官网http://fengyuanchen.github.io/cropper/ 
  文档https://github.com/fengyuanchen/cropper/blob/master/README.md –v3.x版本

  但是官方文档是英文的,英文不好的同学个人推荐一篇特别好的关于cropper的用法的说明,详细又易懂,特别适合新手

  地址:https://blog.csdn.net/weixin_38023551/article/details/78792400

  

  说了这么多,接下来说正文,由于前端页面使用的是layui这个框架,所有使用cropper时,最好的能够将cropper这个包作为一个layui的扩展嵌入到layui中,这样都省事,嵌入很简单,具体可以参考下面我封装好的代码

  备注:cropper默认裁剪后的图片格式为png格式,如果需要自定义上传图片的格式,可以参考下面这段代码,如果不需要请直接忽略

cropper修改上传图片的格式

var cas=imageEle.cropper('getCroppedCanvas');
var base64url=cas.toDataURL('image/jpeg');
console.log(base64url); //生成base64图片的格式

// 展示裁剪的图片的两种方式
// 方式一
$('.cavans').html(cas)  //在body显示出canvas元素

// 方式二
$('.canvans').attr("src", base64url);
View Code

 封装好的cropper源码如下,包括cropper-jQuery的

具体代码:

cropper-css

/*!
 * Cropper.js v1.4.3
 * https://fengyuanchen.github.io/cropperjs
 *
 * Copyright 2015-present Chen Fengyuan
 * Released under the MIT license
 *
 * Date: 2018-10-24T13:07:11.429Z
 */

.cropper-container {
    
    direction: ltr;
    font-size: 0;
    line-height: 0;
    position: relative;
    -ms-touch-action: none;
    touch-action: none;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

.cropper-container img {
    
    display: block;
    height: 100%;
    image-orientation: 0deg;
    max-height: none !important;
    max-width: none !important;
    min-height: 0 !important;
    min-width: 0 !important;
    width: 100%;
}

.cropper-wrap-box,
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal {
    
    bottom: 0;
    left: 0;
    position: absolute;
    right: 0;
    top: 0;
}

.cropper-wrap-box,
.cropper-canvas {
    
    overflow: hidden;
}

.cropper-drag-box {
    
    background-color: #fff;
    opacity: 0;
}

.cropper-modal {
    
    background-color: #000;
    opacity: .5;
}

.cropper-view-box {
    
    display: block;
    height: 100%;
    outline-color: rgba(51, 153, 255, 0.75);
    outline: 1px solid #39f;
    overflow: hidden;
    width: 100%;
}

.cropper-dashed {
    
    border: 0 dashed #eee;
    display: block;
    opacity: .5;
    position: absolute;
}

.cropper-dashed.dashed-h {
    
    border-bottom-width: 1px;
    border-top-width: 1px;
    height: calc(100% / 3);
    left: 0;
    top: calc(100% / 3);
    width: 100%;
}

.cropper-dashed.dashed-v {
    
    border-left-width: 1px;
    border-right-width: 1px;
    height: 100%;
    left: calc(100% / 3);
    top: 0;
    width: calc(100% / 3);
}

.cropper-center {
    
    display: block;
    height: 0;
    left: 50%;
    opacity: .75;
    position: absolute;
    top: 50%;
    width: 0;
}

.cropper-center:before,
.cropper-center:after {
    
    background-color: #eee;
    content: ' ';
    display: block;
    position: absolute;
}

.cropper-center:before {
    
    height: 1px;
    left: -3px;
    top: 0;
    width: 7px;
}

.cropper-center:after {
    
    height: 7px;
    left: 0;
    top: -3px;
    width: 1px;
}

.cropper-face,
.cropper-line,
.cropper-point {
    
    display: block;
    height: 100%;
    opacity: .1;
    position: absolute;
    width: 100%;
}

.cropper-face {
    
    background-color: #fff;
    left: 0;
    top: 0;
}

.cropper-line {
    
    background-color: #39f;
}

.cropper-line.line-e {
    
    cursor: ew-resize;
    right: -3px;
    top: 0;
    width: 5px;
}

.cropper-line.line-n {
    
    cursor: ns-resize;
    height: 5px;
    left: 0;
    top: -3px;
}

.cropper-line.line-w {
    
    cursor: ew-resize;
    left: -3px;
    top: 0;
    width: 5px;
}

.cropper-line.line-s {
    
    bottom: -3px;
    cursor: ns-resize;
    height: 5px;
    left: 0;
}

.cropper-point {
    
    background-color: #39f;
    height: 5px;
    opacity: .75;
    width: 5px;
}

.cropper-point.point-e {
    
    cursor: ew-resize;
    margin-top: -3px;
    right: -3px;
    top: 50%;
}

.cropper-point.point-n {
    
    cursor: ns-resize;
    left: 50%;
    margin-left: -3px;
    top: -3px;
}

.cropper-point.point-w {
    
    cursor: ew-resize;
    left: -3px;
    margin-top: -3px;
    top: 50%;
}

.cropper-point.point-s {
    
    bottom: -3px;
    cursor: s-resize;
    left: 50%;
    margin-left: -3px;
}

.cropper-point.point-ne {
    
    cursor: nesw-resize;
    right: -3px;
    top: -3px;
}

.cropper-point.point-nw {
    
    cursor: nwse-resize;
    left: -3px;
    top: -3px;
}

.cropper-point.point-sw {
    
    bottom: -3px;
    cursor: nesw-resize;
    left: -3px;
}

.cropper-point.point-se {
    
    bottom: -3px;
    cursor: nwse-resize;
    height: 20px;
    opacity: 1;
    right: -3px;
    width: 20px;
}

@media (min-width: 768px) {
    
    .cropper-point.point-se {
        height: 15px;
        width: 15px;
    }
}

@media (min-width: 992px) {
    
    .cropper-point.point-se {
        height: 10px;
        width: 10px;
    }
}

@media (min-width: 1200px) {
    
    .cropper-point.point-se {
        height: 5px;
        opacity: .75;
        width: 5px;
    }
}

.cropper-point.point-se:before {
    
    background-color: #39f;
    bottom: -50%;
    content: ' ';
    display: block;
    height: 200%;
    opacity: 0;
    position: absolute;
    right: -50%;
    width: 200%;
}

.cropper-invisible {
    
    opacity: 0;
}

.cropper-bg {
    
    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMzTjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
}

.cropper-hide {
    
    display: block;
    height: 0;
    position: absolute;
    width: 0;
}

.cropper-hidden {
    
    display: none !important;
}

.cropper-move {
    
    cursor: move;
}

.cropper-crop {
    
    cursor: crosshair;
}

.cropper-disabled .cropper-drag-box,
.cropper-disabled .cropper-face,
.cropper-disabled .cropper-line,
.cropper-disabled .cropper-point {
    
    cursor: not-allowed;
}
cropper-css

cropper-js

/*!
 * Cropper.js v1.4.3
 * https://fengyuanchen.github.io/cropperjs
 *
 * Copyright 2015-present Chen Fengyuan
 * Released under the MIT license
 *
 * Date: 2018-10-24T13:07:15.032Z
 */

(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
        typeof define === 'function' && define.amd ? define(factory) :
            global.layui && layui.define ? layui.define(function (exports) {
                    exports('cropper', factory())
                }) :
                (global.Cropper = factory());
}(this, (function () {
    'use strict';

    function _typeof(obj) {
        if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
            _typeof = function (obj) {
                return typeof obj;
            };
        } else {
            _typeof = function (obj) {
                return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
            };
        }

        return _typeof(obj);
    }

    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    }

    function _defineProperties(target, props) {
        for (var i = 0; i < props.length; i++) {
            var descriptor = props[i];
            descriptor.enumerable = descriptor.enumerable || false;
            descriptor.configurable = true;
            if ("value" in descriptor) descriptor.writable = true;
            Object.defineProperty(target, descriptor.key, descriptor);
        }
    }

    function _createClass(Constructor, protoProps, staticProps) {
        if (protoProps) _defineProperties(Constructor.prototype, protoProps);
        if (staticProps) _defineProperties(Constructor, staticProps);
        return Constructor;
    }

    function _toConsumableArray(arr) {
        return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread();
    }

    function _arrayWithoutHoles(arr) {
        if (Array.isArray(arr)) {
            for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];

            return arr2;
        }
    }

    function _iterableToArray(iter) {
        if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
    }

    function _nonIterableSpread() {
        throw new TypeError("Invalid attempt to spread non-iterable instance");
    }

    var IN_BROWSER = typeof window !== 'undefined';
    var WINDOW = IN_BROWSER ? window : {};
    var NAMESPACE = 'cropper'; // Actions

    var ACTION_ALL = 'all';
    var ACTION_CROP = 'crop';
    var ACTION_MOVE = 'move';
    var ACTION_ZOOM = 'zoom';
    var ACTION_EAST = 'e';
    var ACTION_WEST = 'w';
    var ACTION_SOUTH = 's';
    var ACTION_NORTH = 'n';
    var ACTION_NORTH_EAST = 'ne';
    var ACTION_NORTH_WEST = 'nw';
    var ACTION_SOUTH_EAST = 'se';
    var ACTION_SOUTH_WEST = 'sw'; // Classes

    var CLASS_CROP = "".concat(NAMESPACE, "-crop");
    var CLASS_DISABLED = "".concat(NAMESPACE, "-disabled");
    var CLASS_HIDDEN = "".concat(NAMESPACE, "-hidden");
    var CLASS_HIDE = "".concat(NAMESPACE, "-hide");
    var CLASS_INVISIBLE = "".concat(NAMESPACE, "-invisible");
    var CLASS_MODAL = "".concat(NAMESPACE, "-modal");
    var CLASS_MOVE = "".concat(NAMESPACE, "-move"); // Data keys

    var DATA_ACTION = "".concat(NAMESPACE, "Action");
    var DATA_PREVIEW = "".concat(NAMESPACE, "Preview"); // Drag modes

    var DRAG_MODE_CROP = 'crop';
    var DRAG_MODE_MOVE = 'move';
    var DRAG_MODE_NONE = 'none'; // Events

    var EVENT_CROP = 'crop';
    var EVENT_CROP_END = 'cropend';
    var EVENT_CROP_MOVE = 'cropmove';
    var EVENT_CROP_START = 'cropstart';
    var EVENT_DBLCLICK = 'dblclick';
    var EVENT_POINTER_DOWN = WINDOW.PointerEvent ? 'pointerdown' : 'touchstart mousedown';
    var EVENT_POINTER_MOVE = WINDOW.PointerEvent ? 'pointermove' : 'touchmove mousemove';
    var EVENT_POINTER_UP = WINDOW.PointerEvent ? 'pointerup pointercancel' : 'touchend touchcancel mouseup';
    var EVENT_READY = 'ready';
    var EVENT_RESIZE = 'resize';
    var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll';
    var EVENT_ZOOM = 'zoom'; // Mime types

    var MIME_TYPE_JPEG = 'image/jpeg'; // RegExps

    var REGEXP_ACTIONS = /^(?:e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/;
    var REGEXP_DATA_URL = /^data:/;
    var REGEXP_DATA_URL_JPEG = /^data:image\/jpeg;base64,/;
    var REGEXP_TAG_NAME = /^(?:img|canvas)$/i;

    var DEFAULTS = {
        // Define the view mode of the cropper
        viewMode: 0,
        // 0, 1, 2, 3
        // Define the dragging mode of the cropper
        dragMode: DRAG_MODE_CROP,
        // 'crop', 'move' or 'none'
        // Define the initial aspect ratio of the crop box
        initialAspectRatio: NaN,
        // Define the aspect ratio of the crop box
        aspectRatio: NaN,
        // An object with the previous cropping result data
        data: null,
        // A selector for adding extra containers to preview
        preview: '',
        // Re-render the cropper when resize the window
        responsive: true,
        // Restore the cropped area after resize the window
        restore: true,
        // Check if the current image is a cross-origin image
        checkCrossOrigin: false,
        // Check the current image's Exif Orientation information
        checkOrientation: true,
        // Show the black modal
        modal: true,
        // Show the dashed lines for guiding
        guides: true,
        // Show the center indicator for guiding
        center: true,
        // Show the white modal to highlight the crop box
        highlight: true,
        // Show the grid background
        background: true,
        // Enable to crop the image automatically when initialize
        autoCrop: true,
        // Define the percentage of automatic cropping area when initializes
        autoCropArea: 0.8,
        // Enable to move the image
        movable: true,
        // Enable to rotate the image
        rotatable: true,
        // Enable to scale the image
        scalable: true,
        // Enable to zoom the image
        zoomable: true,
        // Enable to zoom the image by dragging touch
        zoomOnTouch: true,
        // Enable to zoom the image by wheeling mouse
        zoomOnWheel: true,
        // Define zoom ratio when zoom the image by wheeling mouse
        wheelZoomRatio: 0.1,
        // Enable to move the crop box
        cropBoxMovable: true,
        // Enable to resize the crop box
        cropBoxResizable: true,
        // Toggle drag mode between "crop" and "move" when click twice on the cropper
        toggleDragModeOnDblclick: true,
        // Size limitation
        minCanvasWidth: 0,
        minCanvasHeight: 0,
        minCropBoxWidth: 0,
        minCropBoxHeight: 0,
        minContainerWidth: 200,
        minContainerHeight: 100,
        // Shortcuts of events
        ready: null,
        cropstart: null,
        cropmove: null,
        cropend: null,
        crop: null,
        zoom: null
    };

    var TEMPLATE = '<div class="cropper-container" touch-action="none">' + '<div class="cropper-wrap-box">' + '<div class="cropper-canvas"></div>' + '</div>' + '<div class="cropper-drag-box"></div>' + '<div class="cropper-crop-box">' + '<span class="cropper-view-box"></span>' + '<span class="cropper-dashed dashed-h"></span>' + '<span class="cropper-dashed dashed-v"></span>' + '<span class="cropper-center"></span>' + '<span class="cropper-face"></span>' + '<span class="cropper-line line-e" data-cropper-action="e"></span>' + '<span class="cropper-line line-n" data-cropper-action="n"></span>' + '<span class="cropper-line line-w" data-cropper-action="w"></span>' + '<span class="cropper-line line-s" data-cropper-action="s"></span>' + '<span class="cropper-point point-e" data-cropper-action="e"></span>' + '<span class="cropper-point point-n" data-cropper-action="n"></span>' + '<span class="cropper-point point-w" data-cropper-action="w"></span>' + '<span class="cropper-point point-s" data-cropper-action="s"></span>' + '<span class="cropper-point point-ne" data-cropper-action="ne"></span>' + '<span class="cropper-point point-nw" data-cropper-action="nw"></span>' + '<span class="cropper-point point-sw" data-cropper-action="sw"></span>' + '<span class="cropper-point point-se" data-cropper-action="se"></span>' + '</div>' + '</div>';

    /**
     * Check if the given value is not a number.
     */

    var isNaN = Number.isNaN || WINDOW.isNaN;

    /**
     * Check if the given value is a number.
     * @param {*} value - The value to check.
     * @returns {boolean} Returns `true` if the given value is a number, else `false`.
     */

    function isNumber(value) {
        return typeof value === 'number' && !isNaN(value);
    }

    /**
     * Check if the given value is undefined.
     * @param {*} value - The value to check.
     * @returns {boolean} Returns `true` if the given value is undefined, else `false`.
     */

    function isUndefined(value) {
        return typeof value === 'undefined';
    }

    /**
     * Check if the given value is an object.
     * @param {*} value - The value to check.
     * @returns {boolean} Returns `true` if the given value is an object, else `false`.
     */

    function isObject(value) {
        return _typeof(value) === 'object' && value !== null;
    }

    var hasOwnProperty = Object.prototype.hasOwnProperty;

    /**
     * Check if the given value is a plain object.
     * @param {*} value - The value to check.
     * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.
     */

    function isPlainObject(value) {
        if (!isObject(value)) {
            return false;
        }

        try {
            var _constructor = value.constructor;
            var prototype = _constructor.prototype;
            return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');
        } catch (e) {
            return false;
        }
    }

    /**
     * Check if the given value is a function.
     * @param {*} value - The value to check.
     * @returns {boolean} Returns `true` if the given value is a function, else `false`.
     */

    function isFunction(value) {
        return typeof value === 'function';
    }

    /**
     * Iterate the given data.
     * @param {*} data - The data to iterate.
     * @param {Function} callback - The process function for each element.
     * @returns {*} The original data.
     */

    function forEach(data, callback) {
        if (data && isFunction(callback)) {
            if (Array.isArray(data) || isNumber(data.length)
            /* array-like */
            ) {
                var length = data.length;
                var i;

                for (i = 0; i < length; i += 1) {
                    if (callback.call(data, data[i], i, data) === false) {
                        break;
                    }
                }
            } else if (isObject(data)) {
                Object.keys(data).forEach(function (key) {
                    callback.call(data, data[key], key, data);
                });
            }
        }

        return data;
    }

    /**
     * Extend the given object.
     * @param {*} obj - The object to be extended.
     * @param {*} args - The rest objects which will be merged to the first object.
     * @returns {Object} The extended object.
     */

    var assign = Object.assign || function assign(obj) {
        for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
            args[_key - 1] = arguments[_key];
        }

        if (isObject(obj) && args.length > 0) {
            args.forEach(function (arg) {
                if (isObject(arg)) {
                    Object.keys(arg).forEach(function (key) {
                        obj[key] = arg[key];
                    });
                }
            });
        }

        return obj;
    };
    var REGEXP_DECIMALS = /\.\d*(?:0|9){12}\d*$/;

    /**
     * Normalize decimal number.
     * Check out {@link http://0.30000000000000004.com/}
     * @param {number} value - The value to normalize.
     * @param {number} [times=100000000000] - The times for normalizing.
     * @returns {number} Returns the normalized number.
     */

    function normalizeDecimalNumber(value) {
        var times = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100000000000;
        return REGEXP_DECIMALS.test(value) ? Math.round(value * times) / times : value;
    }

    var REGEXP_SUFFIX = /^(?:width|height|left|top|marginLeft|marginTop)$/;

    /**
     * Apply styles to the given element.
     * @param {Element} element - The target element.
     * @param {Object} styles - The styles for applying.
     */

    function setStyle(element, styles) {
        var style = element.style;
        forEach(styles, function (value, property) {
            if (REGEXP_SUFFIX.test(property) && isNumber(value)) {
                value += 'px';
            }

            style[property] = value;
        });
    }

    /**
     * Check if the given element has a special class.
     * @param {Element} element - The element to check.
     * @param {string} value - The class to search.
     * @returns {boolean} Returns `true` if the special class was found.
     */

    function hasClass(element, value) {
        return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;
    }

    /**
     * Add classes to the given element.
     * @param {Element} element - The target element.
     * @param {string} value - The classes to be added.
     */

    function addClass(element, value) {
        if (!value) {
            return;
        }

        if (isNumber(element.length)) {
            forEach(element, function (elem) {
                addClass(elem, value);
            });
            return;
        }

        if (element.classList) {
            element.classList.add(value);
            return;
        }

        var className = element.className.trim();

        if (!className) {
            element.className = value;
        } else if (className.indexOf(value) < 0) {
            element.className = "".concat(className, " ").concat(value);
        }
    }

    /**
     * Remove classes from the given element.
     * @param {Element} element - The target element.
     * @param {string} value - The classes to be removed.
     */

    function removeClass(element, value) {
        if (!value) {
            return;
        }

        if (isNumber(element.length)) {
            forEach(element, function (elem) {
                removeClass(elem, value);
            });
            return;
        }

        if (element.classList) {
            element.classList.remove(value);
            return;
        }

        if (element.className.indexOf(value) >= 0) {
            element.className = element.className.replace(value, '');
        }
    }

    /**
     * Add or remove classes from the given element.
     * @param {Element} element - The target element.
     * @param {string} value - The classes to be toggled.
     * @param {boolean} added - Add only.
     */

    function toggleClass(element, value, added) {
        if (!value) {
            return;
        }

        if (isNumber(element.length)) {
            forEach(element, function (elem) {
                toggleClass(elem, value, added);
            });
            return;
        } // IE10-11 doesn't support the second parameter of `classList.toggle`


        if (added) {
            addClass(element, value);
        } else {
            removeClass(element, value);
        }
    }

    var REGEXP_HYPHENATE = /([a-z\d])([A-Z])/g;

    /**
     * Transform the given string from camelCase to kebab-case
     * @param {string} value - The value to transform.
     * @returns {string} The transformed value.
     */

    function hyphenate(value) {
        return value.replace(REGEXP_HYPHENATE, '$1-$2').toLowerCase();
    }

    /**
     * Get data from the given element.
     * @param {Element} element - The target element.
     * @param {string} name - The data key to get.
     * @returns {string} The data value.
     */

    function getData(element, name) {
        if (isObject(element[name])) {
            return element[name];
        }

        if (element.dataset) {
            return element.dataset[name];
        }

        return element.getAttribute("data-".concat(hyphenate(name)));
    }

    /**
     * Set data to the given element.
     * @param {Element} element - The target element.
     * @param {string} name - The data key to set.
     * @param {string} data - The data value.
     */

    function setData(element, name, data) {
        if (isObject(data)) {
            element[name] = data;
        } else if (element.dataset) {
            element.dataset[name] = data;
        } else {
            element.setAttribute("data-".concat(hyphenate(name)), data);
        }
    }

    /**
     * Remove data from the given element.
     * @param {Element} element - The target element.
     * @param {string} name - The data key to remove.
     */

    function removeData(element, name) {
        if (isObject(element[name])) {
            try {
                delete element[name];
            } catch (e) {
                element[name] = undefined;
            }
        } else if (element.dataset) {
            // #128 Safari not allows to delete dataset property
            try {
                delete element.dataset[name];
            } catch (e) {
                element.dataset[name] = undefined;
            }
        } else {
            element.removeAttribute("data-".concat(hyphenate(name)));
        }
    }

    var REGEXP_SPACES = /\s\s*/;

    var onceSupported = function () {
        var supported = false;

        if (IN_BROWSER) {
            var once = false;

            var listener = function listener() {
            };

            var options = Object.defineProperty({}, 'once', {
                get: function get() {
                    supported = true;
                    return once;
                },

                /**
                 * This setter can fix a `TypeError` in strict mode
                 * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_o
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值