您好,欢迎访问一九零五行业门户网

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

前端的裁剪我知道的可以分为这么两种:flash一种,canvas一种。现在用的多的是canvas这种。
其实裁剪最本质的原理:通过工具获取到图片(不是js那种获取dom的方式,比如flash获取到图片,对图片任意操作,canvas也是一样,将图片放在画布上,任意操作)
本文使用的是canvas这种方式,借助的是cropper.js实现图片的裁剪。
由于前端页面使用的是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);
封装好的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/gaaaablbmvexmzmz////tjrv2aaaacxbiwxmaaarraaak6wgciw1aaaaahhrfwhrtb2z0d2fyzqbbzg9izsbgaxjld29ya3mgq1m26lyyjaaaabfjrefucjlj+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_only} * @param {boolean} value - the value to set */ set: function set(value) { once = value; } }); window.addeventlistener('test', listener, options); window.removeeventlistener('test', listener, options); } return supported; }(); /** * remove event listener from the target element. * @param {element} element - the event target. * @param {string} type - the event type(s). * @param {function} listener - the event listener. * @param {object} options - the event options. */ function removelistener(element, type, listener) { var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var handler = listener; type.trim().split(regexp_spaces).foreach(function (event) { if (!oncesupported) { var listeners = element.listeners; if (listeners && listeners[event] && listeners[event][listener]) { handler = listeners[event][listener]; delete listeners[event][listener]; if (object.keys(listeners[event]).length === 0) { delete listeners[event]; } if (object.keys(listeners).length === 0) { delete element.listeners; } } } element.removeeventlistener(event, handler, options); }); } /** * add event listener to the target element. * @param {element} element - the event target. * @param {string} type - the event type(s). * @param {function} listener - the event listener. * @param {object} options - the event options. */ function addlistener(element, type, listener) { var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var _handler = listener; type.trim().split(regexp_spaces).foreach(function (event) { if (options.once && !oncesupported) { var _element$listeners = element.listeners, listeners = _element$listeners === void 0 ? {} : _element$listeners; _handler = function handler() { delete listeners[event][listener]; element.removeeventlistener(event, _handler, options); for (var _len2 = arguments.length, args = new array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } listener.apply(element, args); }; if (!listeners[event]) { listeners[event] = {}; } if (listeners[event][listener]) { element.removeeventlistener(event, listeners[event][listener], options); } listeners[event][listener] = _handler; element.listeners = listeners; } element.addeventlistener(event, _handler, options); }); } /** * dispatch event on the target element. * @param {element} element - the event target. * @param {string} type - the event type(s). * @param {object} data - the additional event data. * @returns {boolean} indicate if the event is default prevented or not. */ function dispatchevent(element, type, data) { var event; // event and customevent on ie9-11 are global objects, not constructors if (isfunction(event) && isfunction(customevent)) { event = new customevent(type, { detail: data, bubbles: true, cancelable: true }); } else { event = document.createevent('customevent'); event.initcustomevent(type, true, true, data); } return element.dispatchevent(event); } /** * get the offset base on the document. * @param {element} element - the target element. * @returns {object} the offset data. */ function getoffset(element) { var box = element.getboundingclientrect(); return { left: box.left + (window.pagexoffset - document.documentelement.clientleft), top: box.top + (window.pageyoffset - document.documentelement.clienttop) }; } var location = window.location; var regexp_origins = /^(https?:)\/\/([^:/?#]+):?(\d*)/i; /** * check if the given url is a cross origin url. * @param {string} url - the target url. * @returns {boolean} returns `true` if the given url is a cross origin url, else `false`. */ function iscrossoriginurl(url) { var parts = url.match(regexp_origins); return parts && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port); } /** * add timestamp to the given url. * @param {string} url - the target url. * @returns {string} the result url. */ function addtimestamp(url) { var timestamp = "timestamp=".concat(new date().gettime()); return url + (url.indexof('?') === -1 ? '?' : '&') + timestamp; } /** * get transforms base on the given object. * @param {object} obj - the target object. * @returns {string} a string contains transform values. */ function gettransforms(_ref) { var rotate = _ref.rotate, scalex = _ref.scalex, scaley = _ref.scaley, translatex = _ref.translatex, translatey = _ref.translatey; var values = []; if (isnumber(translatex) && translatex !== 0) { values.push("translatex(".concat(translatex, "px)")); } if (isnumber(translatey) && translatey !== 0) { values.push("translatey(".concat(translatey, "px)")); } // rotate should come first before scale to match orientation transform if (isnumber(rotate) && rotate !== 0) { values.push("rotate(".concat(rotate, "deg)")); } if (isnumber(scalex) && scalex !== 1) { values.push("scalex(".concat(scalex, ")")); } if (isnumber(scaley) && scaley !== 1) { values.push("scaley(".concat(scaley, ")")); } var transform = values.length ? values.join(' ') : 'none'; return { webkittransform: transform, mstransform: transform, transform: transform }; } /** * get the max ratio of a group of pointers. * @param {string} pointers - the target pointers. * @returns {number} the result ratio. */ function getmaxzoomratio(pointers) { var pointers2 = assign({}, pointers); var ratios = []; foreach(pointers, function (pointer, pointerid) { delete pointers2[pointerid]; foreach(pointers2, function (pointer2) { var x1 = math.abs(pointer.startx - pointer2.startx); var y1 = math.abs(pointer.starty - pointer2.starty); var x2 = math.abs(pointer.endx - pointer2.endx); var y2 = math.abs(pointer.endy - pointer2.endy); var z1 = math.sqrt(x1 * x1 + y1 * y1); var z2 = math.sqrt(x2 * x2 + y2 * y2); var ratio = (z2 - z1) / z1; ratios.push(ratio); }); }); ratios.sort(function (a, b) { return math.abs(a) < math.abs(b); }); return ratios[0]; } /** * get a pointer from an event object. * @param {object} event - the target event object. * @param {boolean} endonly - indicates if only returns the end point coordinate or not. * @returns {object} the result pointer contains start and/or end point coordinates. */ function getpointer(_ref2, endonly) { var pagex = _ref2.pagex, pagey = _ref2.pagey; var end = { endx: pagex, endy: pagey }; return endonly ? end : assign({ startx: pagex, starty: pagey }, end); } /** * get the center point coordinate of a group of pointers. * @param {object} pointers - the target pointers. * @returns {object} the center point coordinate. */ function getpointerscenter(pointers) { var pagex = 0; var pagey = 0; var count = 0; foreach(pointers, function (_ref3) { var startx = _ref3.startx, starty = _ref3.starty; pagex += startx; pagey += starty; count += 1; }); pagex /= count; pagey /= count; return { pagex: pagex, pagey: pagey }; } /** * check if the given value is a finite number. */ var isfinite = number.isfinite || window.isfinite; /** * get the max sizes in a rectangle under the given aspect ratio. * @param {object} data - the original sizes. * @param {string} [type='contain'] - the adjust type. * @returns {object} the result sizes. */ function getadjustedsizes(_ref4) // or 'cover' { var aspectratio = _ref4.aspectratio, height = _ref4.height, width = _ref4.width; var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'contain'; var isvalidnumber = function isvalidnumber(value) { return isfinite(value) && value > 0; }; if (isvalidnumber(width) && isvalidnumber(height)) { var adjustedwidth = height * aspectratio; if (type === 'contain' && adjustedwidth > width || type === 'cover' && adjustedwidth < width) { height = width / aspectratio; } else { width = height * aspectratio; } } else if (isvalidnumber(width)) { height = width / aspectratio; } else if (isvalidnumber(height)) { width = height * aspectratio; } return { width: width, height: height }; } /** * get the new sizes of a rectangle after rotated. * @param {object} data - the original sizes. * @returns {object} the result sizes. */ function getrotatedsizes(_ref5) { var width = _ref5.width, height = _ref5.height, degree = _ref5.degree; degree = math.abs(degree) % 180; if (degree === 90) { return { width: height, height: width }; } var arc = degree % 90 * math.pi / 180; var sinarc = math.sin(arc); var cosarc = math.cos(arc); var newwidth = width * cosarc + height * sinarc; var newheight = width * sinarc + height * cosarc; return degree > 90 ? { width: newheight, height: newwidth } : { width: newwidth, height: newheight }; } /** * get a canvas which drew the given image. * @param {htmlimageelement} image - the image for drawing. * @param {object} imagedata - the image data. * @param {object} canvasdata - the canvas data. * @param {object} options - the options. * @returns {htmlcanvaselement} the result canvas. */ function getsourcecanvas(image, _ref6, _ref7, _ref8) { var imageaspectratio = _ref6.aspectratio, imagenaturalwidth = _ref6.naturalwidth, imagenaturalheight = _ref6.naturalheight, _ref6$rotate = _ref6.rotate, rotate = _ref6$rotate === void 0 ? 0 : _ref6$rotate, _ref6$scalex = _ref6.scalex, scalex = _ref6$scalex === void 0 ? 1 : _ref6$scalex, _ref6$scaley = _ref6.scaley, scaley = _ref6$scaley === void 0 ? 1 : _ref6$scaley; var aspectratio = _ref7.aspectratio, naturalwidth = _ref7.naturalwidth, naturalheight = _ref7.naturalheight; var _ref8$fillcolor = _ref8.fillcolor, fillcolor = _ref8$fillcolor === void 0 ? 'transparent' : _ref8$fillcolor, _ref8$imagesmoothinge = _ref8.imagesmoothingenabled, imagesmoothingenabled = _ref8$imagesmoothinge === void 0 ? true : _ref8$imagesmoothinge, _ref8$imagesmoothingq = _ref8.imagesmoothingquality, imagesmoothingquality = _ref8$imagesmoothingq === void 0 ? 'low' : _ref8$imagesmoothingq, _ref8$maxwidth = _ref8.maxwidth, maxwidth = _ref8$maxwidth === void 0 ? infinity : _ref8$maxwidth, _ref8$maxheight = _ref8.maxheight, maxheight = _ref8$maxheight === void 0 ? infinity : _ref8$maxheight, _ref8$minwidth = _ref8.minwidth, minwidth = _ref8$minwidth === void 0 ? 0 : _ref8$minwidth, _ref8$minheight = _ref8.minheight, minheight = _ref8$minheight === void 0 ? 0 : _ref8$minheight; var canvas = document.createelement('canvas'); var context = canvas.getcontext('2d'); var maxsizes = getadjustedsizes({ aspectratio: aspectratio, width: maxwidth, height: maxheight }); var minsizes = getadjustedsizes({ aspectratio: aspectratio, width: minwidth, height: minheight }, 'cover'); var width = math.min(maxsizes.width, math.max(minsizes.width, naturalwidth)); var height = math.min(maxsizes.height, math.max(minsizes.height, naturalheight)); // note: should always use image's natural sizes for drawing as // imagedata.naturalwidth === canvasdata.naturalheight when rotate % 180 === 90 var destmaxsizes = getadjustedsizes({ aspectratio: imageaspectratio, width: maxwidth, height: maxheight }); var destminsizes = getadjustedsizes({ aspectratio: imageaspectratio, width: minwidth, height: minheight }, 'cover'); var destwidth = math.min(destmaxsizes.width, math.max(destminsizes.width, imagenaturalwidth)); var destheight = math.min(destmaxsizes.height, math.max(destminsizes.height, imagenaturalheight)); var params = [-destwidth / 2, -destheight / 2, destwidth, destheight]; canvas.width = normalizedecimalnumber(width); canvas.height = normalizedecimalnumber(height); context.fillstyle = fillcolor; context.fillrect(0, 0, width, height); context.save(); context.translate(width / 2, height / 2); context.rotate(rotate * math.pi / 180); context.scale(scalex, scaley); context.imagesmoothingenabled = imagesmoothingenabled; context.imagesmoothingquality = imagesmoothingquality; context.drawimage.apply(context, [image].concat(_toconsumablearray(params.map(function (param) { return math.floor(normalizedecimalnumber(param)); })))); context.restore(); return canvas; } var fromcharcode = string.fromcharcode; /** * get string from char code in data view. * @param {dataview} dataview - the data view for read. * @param {number} start - the start index. * @param {number} length - the read length. * @returns {string} the read result. */ function getstringfromcharcode(dataview, start, length) { var str = ''; var i; length += start; for (i = start; i < length; i += 1) { str += fromcharcode(dataview.getuint8(i)); } return str; } var regexp_data_url_head = /^data:.*,/; /** * transform data url to array buffer. * @param {string} dataurl - the data url to transform. * @returns {arraybuffer} the result array buffer. */ function dataurltoarraybuffer(dataurl) { var base64 = dataurl.replace(regexp_data_url_head, ''); var binary = atob(base64); var arraybuffer = new arraybuffer(binary.length); var uint8 = new uint8array(arraybuffer); foreach(uint8, function (value, i) { uint8[i] = binary.charcodeat(i); }); return arraybuffer; } /** * transform array buffer to data url. * @param {arraybuffer} arraybuffer - the array buffer to transform. * @param {string} mimetype - the mime type of the data url. * @returns {string} the result data url. */ function arraybuffertodataurl(arraybuffer, mimetype) { var chunks = []; var chunksize = 8192; var uint8 = new uint8array(arraybuffer); while (uint8.length > 0) { chunks.push(fromcharcode.apply(void 0, _toconsumablearray(uint8.subarray(0, chunksize)))); uint8 = uint8.subarray(chunksize); } return "data:".concat(mimetype, ";base64,").concat(btoa(chunks.join(''))); } /** * get orientation value from given array buffer. * @param {arraybuffer} arraybuffer - the array buffer to read. * @returns {number} the read orientation value. */ function resetandgetorientation(arraybuffer) { var dataview = new dataview(arraybuffer); var orientation; // ignores range error when the image does not have correct exif information try { var littleendian; var app1start; var ifdstart; // only handle jpeg image (start by 0xffd8) if (dataview.getuint8(0) === 0xff && dataview.getuint8(1) === 0xd8) { var length = dataview.bytelength; var offset = 2; while (offset + 1 < length) { if (dataview.getuint8(offset) === 0xff && dataview.getuint8(offset + 1) === 0xe1) { app1start = offset; break; } offset += 1; } } if (app1start) { var exifidcode = app1start + 4; var tiffoffset = app1start + 10; if (getstringfromcharcode(dataview, exifidcode, 4) === 'exif') { var endianness = dataview.getuint16(tiffoffset); littleendian = endianness === 0x4949; if (littleendian || endianness === 0x4d4d /* bigendian */ ) { if (dataview.getuint16(tiffoffset + 2, littleendian) === 0x002a) { var firstifdoffset = dataview.getuint32(tiffoffset + 4, littleendian); if (firstifdoffset >= 0x00000008) { ifdstart = tiffoffset + firstifdoffset; } } } } } if (ifdstart) { var _length = dataview.getuint16(ifdstart, littleendian); var _offset; var i; for (i = 0; i < _length; i += 1) { _offset = ifdstart + i * 12 + 2; if (dataview.getuint16(_offset, littleendian) === 0x0112 /* orientation */ ) { // 8 is the offset of the current tag's value _offset += 8; // get the original orientation value orientation = dataview.getuint16(_offset, littleendian); // override the orientation with its default value dataview.setuint16(_offset, 1, littleendian); break; } } } } catch (e) { orientation = 1; } return orientation; } /** * parse exif orientation value. * @param {number} orientation - the orientation to parse. * @returns {object} the parsed result. */ function parseorientation(orientation) { var rotate = 0; var scalex = 1; var scaley = 1; switch (orientation) { // flip horizontal case 2: scalex = -1; break; // rotate left 180° case 3: rotate = -180; break; // flip vertical case 4: scaley = -1; break; // flip vertical and rotate right 90° case 5: rotate = 90; scaley = -1; break; // rotate right 90° case 6: rotate = 90; break; // flip horizontal and rotate right 90° case 7: rotate = 90; scalex = -1; break; // rotate left 90° case 8: rotate = -90; break; default: } return { rotate: rotate, scalex: scalex, scaley: scaley }; } var render = { render: function render() { this.initcontainer(); this.initcanvas(); this.initcropbox(); this.rendercanvas(); if (this.cropped) { this.rendercropbox(); } }, initcontainer: function initcontainer() { var element = this.element, options = this.options, container = this.container, cropper = this.cropper; addclass(cropper, class_hidden); removeclass(element, class_hidden); var containerdata = { width: math.max(container.offsetwidth, number(options.mincontainerwidth) || 200), height: math.max(container.offsetheight, number(options.mincontainerheight) || 100) }; this.containerdata = containerdata; setstyle(cropper, { width: containerdata.width, height: containerdata.height }); addclass(element, class_hidden); removeclass(cropper, class_hidden); }, // canvas (image wrapper) initcanvas: function initcanvas() { var containerdata = this.containerdata, imagedata = this.imagedata; var viewmode = this.options.viewmode; var rotated = math.abs(imagedata.rotate) % 180 === 90; var naturalwidth = rotated ? imagedata.naturalheight : imagedata.naturalwidth; var naturalheight = rotated ? imagedata.naturalwidth : imagedata.naturalheight; var aspectratio = naturalwidth / naturalheight; var canvaswidth = containerdata.width; var canvasheight = containerdata.height; if (containerdata.height * aspectratio > containerdata.width) { if (viewmode === 3) { canvaswidth = containerdata.height * aspectratio; } else { canvasheight = containerdata.width / aspectratio; } } else if (viewmode === 3) { canvasheight = containerdata.width / aspectratio; } else { canvaswidth = containerdata.height * aspectratio; } var canvasdata = { aspectratio: aspectratio, naturalwidth: naturalwidth, naturalheight: naturalheight, width: canvaswidth, height: canvasheight }; canvasdata.left = (containerdata.width - canvaswidth) / 2; canvasdata.top = (containerdata.height - canvasheight) / 2; canvasdata.oldleft = canvasdata.left; canvasdata.oldtop = canvasdata.top; this.canvasdata = canvasdata; this.limited = viewmode === 1 || viewmode === 2; this.limitcanvas(true, true); this.initialimagedata = assign({}, imagedata); this.initialcanvasdata = assign({}, canvasdata); }, limitcanvas: function limitcanvas(sizelimited, positionlimited) { var options = this.options, containerdata = this.containerdata, canvasdata = this.canvasdata, cropboxdata = this.cropboxdata; var viewmode = options.viewmode; var aspectratio = canvasdata.aspectratio; var cropped = this.cropped && cropboxdata; if (sizelimited) { var mincanvaswidth = number(options.mincanvaswidth) || 0; var mincanvasheight = number(options.mincanvasheight) || 0; if (viewmode > 1) { mincanvaswidth = math.max(mincanvaswidth, containerdata.width); mincanvasheight = math.max(mincanvasheight, containerdata.height); if (viewmode === 3) { if (mincanvasheight * aspectratio > mincanvaswidth) { mincanvaswidth = mincanvasheight * aspectratio; } else { mincanvasheight = mincanvaswidth / aspectratio; } } } else if (viewmode > 0) { if (mincanvaswidth) { mincanvaswidth = math.max(mincanvaswidth, cropped ? cropboxdata.width : 0); } else if (mincanvasheight) { mincanvasheight = math.max(mincanvasheight, cropped ? cropboxdata.height : 0); } else if (cropped) { mincanvaswidth = cropboxdata.width; mincanvasheight = cropboxdata.height; if (mincanvasheight * aspectratio > mincanvaswidth) { mincanvaswidth = mincanvasheight * aspectratio; } else { mincanvasheight = mincanvaswidth / aspectratio; } } } var _getadjustedsizes = getadjustedsizes({ aspectratio: aspectratio, width: mincanvaswidth, height: mincanvasheight }); mincanvaswidth = _getadjustedsizes.width; mincanvasheight = _getadjustedsizes.height; canvasdata.minwidth = mincanvaswidth; canvasdata.minheight = mincanvasheight; canvasdata.maxwidth = infinity; canvasdata.maxheight = infinity; } if (positionlimited) { if (viewmode > (cropped ? 0 : 1)) { var newcanvasleft = containerdata.width - canvasdata.width; var newcanvastop = containerdata.height - canvasdata.height; canvasdata.minleft = math.min(0, newcanvasleft); canvasdata.mintop = math.min(0, newcanvastop); canvasdata.maxleft = math.max(0, newcanvasleft); canvasdata.maxtop = math.max(0, newcanvastop); if (cropped && this.limited) { canvasdata.minleft = math.min(cropboxdata.left, cropboxdata.left + (cropboxdata.width - canvasdata.width)); canvasdata.mintop = math.min(cropboxdata.top, cropboxdata.top + (cropboxdata.height - canvasdata.height)); canvasdata.maxleft = cropboxdata.left; canvasdata.maxtop = cropboxdata.top; if (viewmode === 2) { if (canvasdata.width >= containerdata.width) { canvasdata.minleft = math.min(0, newcanvasleft); canvasdata.maxleft = math.max(0, newcanvasleft); } if (canvasdata.height >= containerdata.height) { canvasdata.mintop = math.min(0, newcanvastop); canvasdata.maxtop = math.max(0, newcanvastop); } } } } else { canvasdata.minleft = -canvasdata.width; canvasdata.mintop = -canvasdata.height; canvasdata.maxleft = containerdata.width; canvasdata.maxtop = containerdata.height; } } }, rendercanvas: function rendercanvas(changed, transformed) { var canvasdata = this.canvasdata, imagedata = this.imagedata; if (transformed) { var _getrotatedsizes = getrotatedsizes({ width: imagedata.naturalwidth * math.abs(imagedata.scalex || 1), height: imagedata.naturalheight * math.abs(imagedata.scaley || 1), degree: imagedata.rotate || 0 }), naturalwidth = _getrotatedsizes.width, naturalheight = _getrotatedsizes.height; var width = canvasdata.width * (naturalwidth / canvasdata.naturalwidth); var height = canvasdata.height * (naturalheight / canvasdata.naturalheight); canvasdata.left -= (width - canvasdata.width) / 2; canvasdata.top -= (height - canvasdata.height) / 2; canvasdata.width = width; canvasdata.height = height; canvasdata.aspectratio = naturalwidth / naturalheight; canvasdata.naturalwidth = naturalwidth; canvasdata.naturalheight = naturalheight; this.limitcanvas(true, false); } if (canvasdata.width > canvasdata.maxwidth || canvasdata.width < canvasdata.minwidth) { canvasdata.left = canvasdata.oldleft; } if (canvasdata.height > canvasdata.maxheight || canvasdata.height < canvasdata.minheight) { canvasdata.top = canvasdata.oldtop; } canvasdata.width = math.min(math.max(canvasdata.width, canvasdata.minwidth), canvasdata.maxwidth); canvasdata.height = math.min(math.max(canvasdata.height, canvasdata.minheight), canvasdata.maxheight); this.limitcanvas(false, true); canvasdata.left = math.min(math.max(canvasdata.left, canvasdata.minleft), canvasdata.maxleft); canvasdata.top = math.min(math.max(canvasdata.top, canvasdata.mintop), canvasdata.maxtop); canvasdata.oldleft = canvasdata.left; canvasdata.oldtop = canvasdata.top; setstyle(this.canvas, assign({ width: canvasdata.width, height: canvasdata.height }, gettransforms({ translatex: canvasdata.left, translatey: canvasdata.top }))); this.renderimage(changed); if (this.cropped && this.limited) { this.limitcropbox(true, true); } }, renderimage: function renderimage(changed) { var canvasdata = this.canvasdata, imagedata = this.imagedata; var width = imagedata.naturalwidth * (canvasdata.width / canvasdata.naturalwidth); var height = imagedata.naturalheight * (canvasdata.height / canvasdata.naturalheight); assign(imagedata, { width: width, height: height, left: (canvasdata.width - width) / 2, top: (canvasdata.height - height) / 2 }); setstyle(this.image, assign({ width: imagedata.width, height: imagedata.height }, gettransforms(assign({ translatex: imagedata.left, translatey: imagedata.top }, imagedata)))); if (changed) { this.output(); } }, initcropbox: function initcropbox() { var options = this.options, canvasdata = this.canvasdata; var aspectratio = options.aspectratio || options.initialaspectratio; var autocroparea = number(options.autocroparea) || 0.8; var cropboxdata = { width: canvasdata.width, height: canvasdata.height }; if (aspectratio) { if (canvasdata.height * aspectratio > canvasdata.width) { cropboxdata.height = cropboxdata.width / aspectratio; } else { cropboxdata.width = cropboxdata.height * aspectratio; } } this.cropboxdata = cropboxdata; this.limitcropbox(true, true); // initialize auto crop area cropboxdata.width = math.min(math.max(cropboxdata.width, cropboxdata.minwidth), cropboxdata.maxwidth); cropboxdata.height = math.min(math.max(cropboxdata.height, cropboxdata.minheight), cropboxdata.maxheight); // the width/height of auto crop area must large than "minwidth/height" cropboxdata.width = math.max(cropboxdata.minwidth, cropboxdata.width * autocroparea); cropboxdata.height = math.max(cropboxdata.minheight, cropboxdata.height * autocroparea); cropboxdata.left = canvasdata.left + (canvasdata.width - cropboxdata.width) / 2; cropboxdata.top = canvasdata.top + (canvasdata.height - cropboxdata.height) / 2; cropboxdata.oldleft = cropboxdata.left; cropboxdata.oldtop = cropboxdata.top; this.initialcropboxdata = assign({}, cropboxdata); }, limitcropbox: function limitcropbox(sizelimited, positionlimited) { var options = this.options, containerdata = this.containerdata, canvasdata = this.canvasdata, cropboxdata = this.cropboxdata, limited = this.limited; var aspectratio = options.aspectratio; if (sizelimited) { var mincropboxwidth = number(options.mincropboxwidth) || 0; var mincropboxheight = number(options.mincropboxheight) || 0; var maxcropboxwidth = limited ? math.min(containerdata.width, canvasdata.width, canvasdata.width + canvasdata.left, containerdata.width - canvasdata.left) : containerdata.width; var maxcropboxheight = limited ? math.min(containerdata.height, canvasdata.height, canvasdata.height + canvasdata.top, containerdata.height - canvasdata.top) : containerdata.height; // the min/maxcropboxwidth/height must be less than container's width/height mincropboxwidth = math.min(mincropboxwidth, containerdata.width); mincropboxheight = math.min(mincropboxheight, containerdata.height); if (aspectratio) { if (mincropboxwidth && mincropboxheight) { if (mincropboxheight * aspectratio > mincropboxwidth) { mincropboxheight = mincropboxwidth / aspectratio; } else { mincropboxwidth = mincropboxheight * aspectratio; } } else if (mincropboxwidth) { mincropboxheight = mincropboxwidth / aspectratio; } else if (mincropboxheight) { mincropboxwidth = mincropboxheight * aspectratio; } if (maxcropboxheight * aspectratio > maxcropboxwidth) { maxcropboxheight = maxcropboxwidth / aspectratio; } else { maxcropboxwidth = maxcropboxheight * aspectratio; } } // the minwidth/height must be less than maxwidth/height cropboxdata.minwidth = math.min(mincropboxwidth, maxcropboxwidth); cropboxdata.minheight = math.min(mincropboxheight, maxcropboxheight); cropboxdata.maxwidth = maxcropboxwidth; cropboxdata.maxheight = maxcropboxheight; } if (positionlimited) { if (limited) { cropboxdata.minleft = math.max(0, canvasdata.left); cropboxdata.mintop = math.max(0, canvasdata.top); cropboxdata.maxleft = math.min(containerdata.width, canvasdata.left + canvasdata.width) - cropboxdata.width; cropboxdata.maxtop = math.min(containerdata.height, canvasdata.top + canvasdata.height) - cropboxdata.height; } else { cropboxdata.minleft = 0; cropboxdata.mintop = 0; cropboxdata.maxleft = containerdata.width - cropboxdata.width; cropboxdata.maxtop = containerdata.height - cropboxdata.height; } } }, rendercropbox: function rendercropbox() { var options = this.options, containerdata = this.containerdata, cropboxdata = this.cropboxdata; if (cropboxdata.width > cropboxdata.maxwidth || cropboxdata.width < cropboxdata.minwidth) { cropboxdata.left = cropboxdata.oldleft; } if (cropboxdata.height > cropboxdata.maxheight || cropboxdata.height < cropboxdata.minheight) { cropboxdata.top = cropboxdata.oldtop; } cropboxdata.width = math.min(math.max(cropboxdata.width, cropboxdata.minwidth), cropboxdata.maxwidth); cropboxdata.height = math.min(math.max(cropboxdata.height, cropboxdata.minheight), cropboxdata.maxheight); this.limitcropbox(false, true); cropboxdata.left = math.min(math.max(cropboxdata.left, cropboxdata.minleft), cropboxdata.maxleft); cropboxdata.top = math.min(math.max(cropboxdata.top, cropboxdata.mintop), cropboxdata.maxtop); cropboxdata.oldleft = cropboxdata.left; cropboxdata.oldtop = cropboxdata.top; if (options.movable && options.cropboxmovable) { // turn to move the canvas when the crop box is equal to the container setdata(this.face, data_action, cropboxdata.width >= containerdata.width && cropboxdata.height >= containerdata.height ? action_move : action_all); } setstyle(this.cropbox, assign({ width: cropboxdata.width, height: cropboxdata.height }, gettransforms({ translatex: cropboxdata.left, translatey: cropboxdata.top }))); if (this.cropped && this.limited) { this.limitcanvas(true, true); } if (!this.disabled) { this.output(); } }, output: function output() { this.preview(); dispatchevent(this.element, event_crop, this.getdata()); } }; var preview = { initpreview: function initpreview() { var crossorigin = this.crossorigin; var preview = this.options.preview; var url = crossorigin ? this.crossoriginurl : this.url; var image = document.createelement('img'); if (crossorigin) { image.crossorigin = crossorigin; } image.src = url; this.viewbox.appendchild(image); this.viewboximage = image; if (!preview) { return; } var previews = preview; if (typeof preview === 'string') { previews = this.element.ownerdocument.queryselectorall(preview); } else if (preview.queryselector) { previews = [preview]; } this.previews = previews; foreach(previews, function (el) { var img = document.createelement('img'); // save the original size for recover setdata(el, data_preview, { width: el.offsetwidth, height: el.offsetheight, html: el.innerhtml }); if (crossorigin) { img.crossorigin = crossorigin; } img.src = url; /** * override img element styles * add `display:block` to avoid margin top issue * add `height:auto` to override `height` attribute on ie8 * (occur only when margin-top <= -height) */ img.style.csstext = 'display:block;' + 'width:100%;' + 'height:auto;' + 'min-width:0!important;' + 'min-height:0!important;' + 'max-width:none!important;' + 'max-height:none!important;' + 'image-orientation:0deg!important;"'; el.innerhtml = ''; el.appendchild(img); }); }, resetpreview: function resetpreview() { foreach(this.previews, function (element) { var data = getdata(element, data_preview); setstyle(element, { width: data.width, height: data.height }); element.innerhtml = data.html; removedata(element, data_preview); }); }, preview: function preview() { var imagedata = this.imagedata, canvasdata = this.canvasdata, cropboxdata = this.cropboxdata; var cropboxwidth = cropboxdata.width, cropboxheight = cropboxdata.height; var width = imagedata.width, height = imagedata.height; var left = cropboxdata.left - canvasdata.left - imagedata.left; var top = cropboxdata.top - canvasdata.top - imagedata.top; if (!this.cropped || this.disabled) { return; } setstyle(this.viewboximage, assign({ width: width, height: height }, gettransforms(assign({ translatex: -left, translatey: -top }, imagedata)))); foreach(this.previews, function (element) { var data = getdata(element, data_preview); var originalwidth = data.width; var originalheight = data.height; var newwidth = originalwidth; var newheight = originalheight; var ratio = 1; if (cropboxwidth) { ratio = originalwidth / cropboxwidth; newheight = cropboxheight * ratio; } if (cropboxheight && newheight > originalheight) { ratio = originalheight / cropboxheight; newwidth = cropboxwidth * ratio; newheight = originalheight; } setstyle(element, { width: newwidth, height: newheight }); setstyle(element.getelementsbytagname('img')[0], assign({ width: width * ratio, height: height * ratio }, gettransforms(assign({ translatex: -left * ratio, translatey: -top * ratio }, imagedata)))); }); } }; var events = { bind: function bind() { var element = this.element, options = this.options, cropper = this.cropper; if (isfunction(options.cropstart)) { addlistener(element, event_crop_start, options.cropstart); } if (isfunction(options.cropmove)) { addlistener(element, event_crop_move, options.cropmove); } if (isfunction(options.cropend)) { addlistener(element, event_crop_end, options.cropend); } if (isfunction(options.crop)) { addlistener(element, event_crop, options.crop); } if (isfunction(options.zoom)) { addlistener(element, event_zoom, options.zoom); } addlistener(cropper, event_pointer_down, this.oncropstart = this.cropstart.bind(this)); if (options.zoomable && options.zoomonwheel) { addlistener(cropper, event_wheel, this.onwheel = this.wheel.bind(this)); } if (options.toggledragmodeondblclick) { addlistener(cropper, event_dblclick, this.ondblclick = this.dblclick.bind(this)); } addlistener(element.ownerdocument, event_pointer_move, this.oncropmove = this.cropmove.bind(this)); addlistener(element.ownerdocument, event_pointer_up, this.oncropend = this.cropend.bind(this)); if (options.responsive) { addlistener(window, event_resize, this.onresize = this.resize.bind(this)); } }, unbind: function unbind() { var element = this.element, options = this.options, cropper = this.cropper; if (isfunction(options.cropstart)) { removelistener(element, event_crop_start, options.cropstart); } if (isfunction(options.cropmove)) { removelistener(element, event_crop_move, options.cropmove); } if (isfunction(options.cropend)) { removelistener(element, event_crop_end, options.cropend); } if (isfunction(options.crop)) { removelistener(element, event_crop, options.crop); } if (isfunction(options.zoom)) { removelistener(element, event_zoom, options.zoom); } removelistener(cropper, event_pointer_down, this.oncropstart); if (options.zoomable && options.zoomonwheel) { removelistener(cropper, event_wheel, this.onwheel); } if (options.toggledragmodeondblclick) { removelistener(cropper, event_dblclick, this.ondblclick); } removelistener(element.ownerdocument, event_pointer_move, this.oncropmove); removelistener(element.ownerdocument, event_pointer_up, this.oncropend); if (options.responsive) { removelistener(window, event_resize, this.onresize); } } }; var handlers = { resize: function resize() { var options = this.options, container = this.container, containerdata = this.containerdata; var mincontainerwidth = number(options.mincontainerwidth) || 200; var mincontainerheight = number(options.mincontainerheight) || 100; if (this.disabled || containerdata.width <= mincontainerwidth || containerdata.height <= mincontainerheight) { return; } var ratio = container.offsetwidth / containerdata.width; // resize when width changed or height changed if (ratio !== 1 || container.offsetheight !== containerdata.height) { var canvasdata; var cropboxdata; if (options.restore) { canvasdata = this.getcanvasdata(); cropboxdata = this.getcropboxdata(); } this.render(); if (options.restore) { this.setcanvasdata(foreach(canvasdata, function (n, i) { canvasdata[i] = n * ratio; })); this.setcropboxdata(foreach(cropboxdata, function (n, i) { cropboxdata[i] = n * ratio; })); } } }, dblclick: function dblclick() { if (this.disabled || this.options.dragmode === drag_mode_none) { return; } this.setdragmode(hasclass(this.dragbox, class_crop) ? drag_mode_move : drag_mode_crop); }, wheel: function wheel(e) { var _this = this; var ratio = number(this.options.wheelzoomratio) || 0.1; var delta = 1; if (this.disabled) { return; } e.preventdefault(); // limit wheel speed to prevent zoom too fast (#21) if (this.wheeling) { return; } this.wheeling = true; settimeout(function () { _this.wheeling = false; }, 50); if (e.deltay) { delta = e.deltay > 0 ? 1 : -1; } else if (e.wheeldelta) { delta = -e.wheeldelta / 120; } else if (e.detail) { delta = e.detail > 0 ? 1 : -1; } this.zoom(-delta * ratio, e); }, cropstart: function cropstart(e) { if (this.disabled) { return; } var options = this.options, pointers = this.pointers; var action; if (e.changedtouches) { // handle touch event foreach(e.changedtouches, function (touch) { pointers[touch.identifier] = getpointer(touch); }); } else { // handle mouse event and pointer event pointers[e.pointerid || 0] = getpointer(e); } if (object.keys(pointers).length > 1 && options.zoomable && options.zoomontouch) { action = action_zoom; } else { action = getdata(e.target, data_action); } if (!regexp_actions.test(action)) { return; } if (dispatchevent(this.element, event_crop_start, { originalevent: e, action: action }) === false) { return; } // this line is required for preventing page zooming in ios browsers e.preventdefault(); this.action = action; this.cropping = false; if (action === action_crop) { this.cropping = true; addclass(this.dragbox, class_modal); } }, cropmove: function cropmove(e) { var action = this.action; if (this.disabled || !action) { return; } var pointers = this.pointers; e.preventdefault(); if (dispatchevent(this.element, event_crop_move, { originalevent: e, action: action }) === false) { return; } if (e.changedtouches) { foreach(e.changedtouches, function (touch) { // the first parameter should not be undefined (#432) assign(pointers[touch.identifier] || {}, getpointer(touch, true)); }); } else { assign(pointers[e.pointerid || 0] || {}, getpointer(e, true)); } this.change(e); }, cropend: function cropend(e) { if (this.disabled) { return; } var action = this.action, pointers = this.pointers; if (e.changedtouches) { foreach(e.changedtouches, function (touch) { delete pointers[touch.identifier]; }); } else { delete pointers[e.pointerid || 0]; } if (!action) { return; } e.preventdefault(); if (!object.keys(pointers).length) { this.action = ''; } if (this.cropping) { this.cropping = false; toggleclass(this.dragbox, class_modal, this.cropped && this.options.modal); } dispatchevent(this.element, event_crop_end, { originalevent: e, action: action }); } }; var change = { change: function change(e) { var options = this.options, canvasdata = this.canvasdata, containerdata = this.containerdata, cropboxdata = this.cropboxdata, pointers = this.pointers; var action = this.action; var aspectratio = options.aspectratio; var left = cropboxdata.left, top = cropboxdata.top, width = cropboxdata.width, height = cropboxdata.height; var right = left + width; var bottom = top + height; var minleft = 0; var mintop = 0; var maxwidth = containerdata.width; var maxheight = containerdata.height; var renderable = true; var offset; // locking aspect ratio in "free mode" by holding shift key if (!aspectratio && e.shiftkey) { aspectratio = width && height ? width / height : 1; } if (this.limited) { minleft = cropboxdata.minleft; mintop = cropboxdata.mintop; maxwidth = minleft + math.min(containerdata.width, canvasdata.width, canvasdata.left + canvasdata.width); maxheight = mintop + math.min(containerdata.height, canvasdata.height, canvasdata.top + canvasdata.height); } var pointer = pointers[object.keys(pointers)[0]]; var range = { x: pointer.endx - pointer.startx, y: pointer.endy - pointer.starty }; var check = function check(side) { switch (side) { case action_east: if (right + range.x > maxwidth) { range.x = maxwidth - right; } break; case action_west: if (left + range.x < minleft) { range.x = minleft - left; } break; case action_north: if (top + range.y < mintop) { range.y = mintop - top; } break; case action_south: if (bottom + range.y > maxheight) { range.y = maxheight - bottom; } break; default: } }; switch (action) { // move crop box case action_all: left += range.x; top += range.y; break; // resize crop box case action_east: if (range.x >= 0 && (right >= maxwidth || aspectratio && (top <= mintop || bottom >= maxheight))) { renderable = false; break; } check(action_east); width += range.x; if (width < 0) { action = action_west; width = -width; left -= width; } if (aspectratio) { height = width / aspectratio; top += (cropboxdata.height - height) / 2; } break; case action_north: if (range.y <= 0 && (top <= mintop || aspectratio && (left <= minleft || right >= maxwidth))) { renderable = false; break; } check(action_north); height -= range.y; top += range.y; if (height < 0) { action = action_south; height = -height; top -= height; } if (aspectratio) { width = height * aspectratio; left += (cropboxdata.width - width) / 2; } break; case action_west: if (range.x <= 0 && (left <= minleft || aspectratio && (top <= mintop || bottom >= maxheight))) { renderable = false; break; } check(action_west); width -= range.x; left += range.x; if (width < 0) { action = action_east; width = -width; left -= width; } if (aspectratio) { height = width / aspectratio; top += (cropboxdata.height - height) / 2; } break; case action_south: if (range.y >= 0 && (bottom >= maxheight || aspectratio && (left <= minleft || right >= maxwidth))) { renderable = false; break; } check(action_south); height += range.y; if (height < 0) { action = action_north; height = -height; top -= height; } if (aspectratio) { width = height * aspectratio; left += (cropboxdata.width - width) / 2; } break; case action_north_east: if (aspectratio) { if (range.y <= 0 && (top <= mintop || right >= maxwidth)) { renderable = false; break; } check(action_north); height -= range.y; top += range.y; width = height * aspectratio; } else { check(action_north); check(action_east); if (range.x >= 0) { if (right < maxwidth) { width += range.x; } else if (range.y <= 0 && top <= mintop) { renderable = false; } } else { width += range.x; } if (range.y <= 0) { if (top > mintop) { height -= range.y; top += range.y; } } else { height -= range.y; top += range.y; } } if (width < 0 && height < 0) { action = action_south_west; height = -height; width = -width; top -= height; left -= width; } else if (width < 0) { action = action_north_west; width = -width; left -= width; } else if (height < 0) { action = action_south_east; height = -height; top -= height; } break; case action_north_west: if (aspectratio) { if (range.y <= 0 && (top <= mintop || left <= minleft)) { renderable = false; break; } check(action_north); height -= range.y; top += range.y; width = height * aspectratio; left += cropboxdata.width - width; } else { check(action_north); check(action_west); if (range.x <= 0) { if (left > minleft) { width -= range.x; left += range.x; } else if (range.y <= 0 && top <= mintop) { renderable = false; } } else { width -= range.x; left += range.x; } if (range.y <= 0) { if (top > mintop) { height -= range.y; top += range.y; } } else { height -= range.y; top += range.y; } } if (width < 0 && height < 0) { action = action_south_east; height = -height; width = -width; top -= height; left -= width; } else if (width < 0) { action = action_north_east; width = -width; left -= width; } else if (height < 0) { action = action_south_west; height = -height; top -= height; } break; case action_south_west: if (aspectratio) { if (range.x <= 0 && (left <= minleft || bottom >= maxheight)) { renderable = false; break; } check(action_west); width -= range.x; left += range.x; height = width / aspectratio; } else { check(action_south); check(action_west); if (range.x <= 0) { if (left > minleft) { width -= range.x; left += range.x; } else if (range.y >= 0 && bottom >= maxheight) { renderable = false; } } else { width -= range.x; left += range.x; } if (range.y >= 0) { if (bottom < maxheight) { height += range.y; } } else { height += range.y; } } if (width < 0 && height < 0) { action = action_north_east; height = -height; width = -width; top -= height; left -= width; } else if (width < 0) { action = action_south_east; width = -width; left -= width; } else if (height < 0) { action = action_north_west; height = -height; top -= height; } break; case action_south_east: if (aspectratio) { if (range.x >= 0 && (right >= maxwidth || bottom >= maxheight)) { renderable = false; break; } check(action_east); width += range.x; height = width / aspectratio; } else { check(action_south); check(action_east); if (range.x >= 0) { if (right < maxwidth) { width += range.x; } else if (range.y >= 0 && bottom >= maxheight) { renderable = false; } } else { width += range.x; } if (range.y >= 0) { if (bottom < maxheight) { height += range.y; } } else { height += range.y; } } if (width < 0 && height < 0) { action = action_north_west; height = -height; width = -width; top -= height; left -= width; } else if (width < 0) { action = action_south_west; width = -width; left -= width; } else if (height < 0) { action = action_north_east; height = -height; top -= height; } break; // move canvas case action_move: this.move(range.x, range.y); renderable = false; break; // zoom canvas case action_zoom: this.zoom(getmaxzoomratio(pointers), e); renderable = false; break; // create crop box case action_crop: if (!range.x || !range.y) { renderable = false; break; } offset = getoffset(this.cropper); left = pointer.startx - offset.left; top = pointer.starty - offset.top; width = cropboxdata.minwidth; height = cropboxdata.minheight; if (range.x > 0) { action = range.y > 0 ? action_south_east : action_north_east; } else if (range.x < 0) { left -= width; action = range.y > 0 ? action_south_west : action_north_west; } if (range.y < 0) { top -= height; } // show the crop box if is hidden if (!this.cropped) { removeclass(this.cropbox, class_hidden); this.cropped = true; if (this.limited) { this.limitcropbox(true, true); } } break; default: } if (renderable) { cropboxdata.width = width; cropboxdata.height = height; cropboxdata.left = left; cropboxdata.top = top; this.action = action; this.rendercropbox(); } // override foreach(pointers, function (p) { p.startx = p.endx; p.starty = p.endy; }); } }; var methods = { // show the crop box manually crop: function crop() { if (this.ready && !this.cropped && !this.disabled) { this.cropped = true; this.limitcropbox(true, true); if (this.options.modal) { addclass(this.dragbox, class_modal); } removeclass(this.cropbox, class_hidden); this.setcropboxdata(this.initialcropboxdata); } return this; }, // reset the image and crop box to their initial states reset: function reset() { if (this.ready && !this.disabled) { this.imagedata = assign({}, this.initialimagedata); this.canvasdata = assign({}, this.initialcanvasdata); this.cropboxdata = assign({}, this.initialcropboxdata); this.rendercanvas(); if (this.cropped) { this.rendercropbox(); } } return this; }, // clear the crop box clear: function clear() { if (this.cropped && !this.disabled) { assign(this.cropboxdata, { left: 0, top: 0, width: 0, height: 0 }); this.cropped = false; this.rendercropbox(); this.limitcanvas(true, true); // render canvas after crop box rendered this.rendercanvas(); removeclass(this.dragbox, class_modal); addclass(this.cropbox, class_hidden); } return this; }, /** * replace the image's src and rebuild the cropper * @param {string} url - the new url. * @param {boolean} [hassamesize] - indicate if the new image has the same size as the old one. * @returns {cropper} this */ replace: function replace(url) { var hassamesize = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; if (!this.disabled && url) { if (this.isimg) { this.element.src = url; } if (hassamesize) { this.url = url; this.image.src = url; if (this.ready) { this.viewboximage.src = url; foreach(this.previews, function (element) { element.getelementsbytagname('img')[0].src = url; }); } } else { if (this.isimg) { this.replaced = true; } this.options.data = null; this.uncreate(); this.load(url); } } return this; }, // enable (unfreeze) the cropper enable: function enable() { if (this.ready && this.disabled) { this.disabled = false; removeclass(this.cropper, class_disabled); } return this; }, // disable (freeze) the cropper disable: function disable() { if (this.ready && !this.disabled) { this.disabled = true; addclass(this.cropper, class_disabled); } return this; }, /** * destroy the cropper and remove the instance from the image * @returns {cropper} this */ destroy: function destroy() { var element = this.element; if (!element[namespace]) { return this; } element[namespace] = undefined; if (this.isimg && this.replaced) { element.src = this.originalurl; } this.uncreate(); return this; }, /** * move the canvas with relative offsets * @param {number} offsetx - the relative offset distance on the x-axis. * @param {number} [offsety=offsetx] - the relative offset distance on the y-axis. * @returns {cropper} this */ move: function move(offsetx) { var offsety = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : offsetx; var _this$canvasdata = this.canvasdata, left = _this$canvasdata.left, top = _this$canvasdata.top; return this.moveto(isundefined(offsetx) ? offsetx : left + number(offsetx), isundefined(offsety) ? offsety : top + number(offsety)); }, /** * move the canvas to an absolute point * @param {number} x - the x-axis coordinate. * @param {number} [y=x] - the y-axis coordinate. * @returns {cropper} this */ moveto: function moveto(x) { var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x; var canvasdata = this.canvasdata; var changed = false; x = number(x); y = number(y); if (this.ready && !this.disabled && this.options.movable) { if (isnumber(x)) { canvasdata.left = x; changed = true; } if (isnumber(y)) { canvasdata.top = y; changed = true; } if (changed) { this.rendercanvas(true); } } return this; }, /** * zoom the canvas with a relative ratio * @param {number} ratio - the target ratio. * @param {event} _originalevent - the original event if any. * @returns {cropper} this */ zoom: function zoom(ratio, _originalevent) { var canvasdata = this.canvasdata; ratio = number(ratio); if (ratio < 0) { ratio = 1 / (1 - ratio); } else { ratio = 1 + ratio; } return this.zoomto(canvasdata.width * ratio / canvasdata.naturalwidth, null, _originalevent); }, /** * zoom the canvas to an absolute ratio * @param {number} ratio - the target ratio. * @param {object} pivot - the zoom pivot point coordinate. * @param {event} _originalevent - the original event if any. * @returns {cropper} this */ zoomto: function zoomto(ratio, pivot, _originalevent) { var options = this.options, canvasdata = this.canvasdata; var width = canvasdata.width, height = canvasdata.height, naturalwidth = canvasdata.naturalwidth, naturalheight = canvasdata.naturalheight; ratio = number(ratio); if (ratio >= 0 && this.ready && !this.disabled && options.zoomable) { var newwidth = naturalwidth * ratio; var newheight = naturalheight * ratio; if (dispatchevent(this.element, event_zoom, { ratio: ratio, oldratio: width / naturalwidth, originalevent: _originalevent }) === false) { return this; } if (_originalevent) { var pointers = this.pointers; var offset = getoffset(this.cropper); var center = pointers && object.keys(pointers).length ? getpointerscenter(pointers) : { pagex: _originalevent.pagex, pagey: _originalevent.pagey }; // zoom from the triggering point of the event canvasdata.left -= (newwidth - width) * ((center.pagex - offset.left - canvasdata.left) / width); canvasdata.top -= (newheight - height) * ((center.pagey - offset.top - canvasdata.top) / height); } else if (isplainobject(pivot) && isnumber(pivot.x) && isnumber(pivot.y)) { canvasdata.left -= (newwidth - width) * ((pivot.x - canvasdata.left) / width); canvasdata.top -= (newheight - height) * ((pivot.y - canvasdata.top) / height); } else { // zoom from the center of the canvas canvasdata.left -= (newwidth - width) / 2; canvasdata.top -= (newheight - height) / 2; } canvasdata.width = newwidth; canvasdata.height = newheight; this.rendercanvas(true); } return this; }, /** * rotate the canvas with a relative degree * @param {number} degree - the rotate degree. * @returns {cropper} this */ rotate: function rotate(degree) { return this.rotateto((this.imagedata.rotate || 0) + number(degree)); }, /** * rotate the canvas to an absolute degree * @param {number} degree - the rotate degree. * @returns {cropper} this */ rotateto: function rotateto(degree) { degree = number(degree); if (isnumber(degree) && this.ready && !this.disabled && this.options.rotatable) { this.imagedata.rotate = degree % 360; this.rendercanvas(true, true); } return this; }, /** * scale the image on the x-axis. * @param {number} scalex - the scale ratio on the x-axis. * @returns {cropper} this */ scalex: function scalex(_scalex) { var scaley = this.imagedata.scaley; return this.scale(_scalex, isnumber(scaley) ? scaley : 1); }, /** * scale the image on the y-axis. * @param {number} scaley - the scale ratio on the y-axis. * @returns {cropper} this */ scaley: function scaley(_scaley) { var scalex = this.imagedata.scalex; return this.scale(isnumber(scalex) ? scalex : 1, _scaley); }, /** * scale the image * @param {number} scalex - the scale ratio on the x-axis. * @param {number} [scaley=scalex] - the scale ratio on the y-axis. * @returns {cropper} this */ scale: function scale(scalex) { var scaley = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scalex; var imagedata = this.imagedata; var transformed = false; scalex = number(scalex); scaley = number(scaley); if (this.ready && !this.disabled && this.options.scalable) { if (isnumber(scalex)) { imagedata.scalex = scalex; transformed = true; } if (isnumber(scaley)) { imagedata.scaley = scaley; transformed = true; } if (transformed) { this.rendercanvas(true, true); } } return this; }, /** * get the cropped area position and size data (base on the original image) * @param {boolean} [rounded=false] - indicate if round the data values or not. * @returns {object} the result cropped data. */ getdata: function getdata$$1() { var rounded = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var options = this.options, imagedata = this.imagedata, canvasdata = this.canvasdata, cropboxdata = this.cropboxdata; var data; if (this.ready && this.cropped) { data = { x: cropboxdata.left - canvasdata.left, y: cropboxdata.top - canvasdata.top, width: cropboxdata.width, height: cropboxdata.height }; var ratio = imagedata.width / imagedata.naturalwidth; foreach(data, function (n, i) { data[i] = n / ratio; }); if (rounded) { // in case rounding off leads to extra 1px in right or bottom border // we should round the top-left corner and the dimension (#343). var bottom = math.round(data.y + data.height); var right = math.round(data.x + data.width); data.x = math.round(data.x); data.y = math.round(data.y); data.width = right - data.x; data.height = bottom - data.y; } } else { data = { x: 0, y: 0, width: 0, height: 0 }; } if (options.rotatable) { data.rotate = imagedata.rotate || 0; } if (options.scalable) { data.scalex = imagedata.scalex || 1; data.scaley = imagedata.scaley || 1; } return data; }, /** * set the cropped area position and size with new data * @param {object} data - the new data. * @returns {cropper} this */ setdata: function setdata$$1(data) { var options = this.options, imagedata = this.imagedata, canvasdata = this.canvasdata; var cropboxdata = {}; if (this.ready && !this.disabled && isplainobject(data)) { var transformed = false; if (options.rotatable) { if (isnumber(data.rotate) && data.rotate !== imagedata.rotate) { imagedata.rotate = data.rotate; transformed = true; } } if (options.scalable) { if (isnumber(data.scalex) && data.scalex !== imagedata.scalex) { imagedata.scalex = data.scalex; transformed = true; } if (isnumber(data.scaley) && data.scaley !== imagedata.scaley) { imagedata.scaley = data.scaley; transformed = true; } } if (transformed) { this.rendercanvas(true, true); } var ratio = imagedata.width / imagedata.naturalwidth; if (isnumber(data.x)) { cropboxdata.left = data.x * ratio + canvasdata.left; } if (isnumber(data.y)) { cropboxdata.top = data.y * ratio + canvasdata.top; } if (isnumber(data.width)) { cropboxdata.width = data.width * ratio; } if (isnumber(data.height)) { cropboxdata.height = data.height * ratio; } this.setcropboxdata(cropboxdata); } return this; }, /** * get the container size data. * @returns {object} the result container data. */ getcontainerdata: function getcontainerdata() { return this.ready ? assign({}, this.containerdata) : {}; }, /** * get the image position and size data. * @returns {object} the result image data. */ getimagedata: function getimagedata() { return this.sized ? assign({}, this.imagedata) : {}; }, /** * get the canvas position and size data. * @returns {object} the result canvas data. */ getcanvasdata: function getcanvasdata() { var canvasdata = this.canvasdata; var data = {}; if (this.ready) { foreach(['left', 'top', 'width', 'height', 'naturalwidth', 'naturalheight'], function (n) { data[n] = canvasdata[n]; }); } return data; }, /** * set the canvas position and size with new data. * @param {object} data - the new canvas data. * @returns {cropper} this */ setcanvasdata: function setcanvasdata(data) { var canvasdata = this.canvasdata; var aspectratio = canvasdata.aspectratio; if (this.ready && !this.disabled && isplainobject(data)) { if (isnumber(data.left)) { canvasdata.left = data.left; } if (isnumber(data.top)) { canvasdata.top = data.top; } if (isnumber(data.width)) { canvasdata.width = data.width; canvasdata.height = data.width / aspectratio; } else if (isnumber(data.height)) { canvasdata.height = data.height; canvasdata.width = data.height * aspectratio; } this.rendercanvas(true); } return this; }, /** * get the crop box position and size data. * @returns {object} the result crop box data. */ getcropboxdata: function getcropboxdata() { var cropboxdata = this.cropboxdata; var data; if (this.ready && this.cropped) { data = { left: cropboxdata.left, top: cropboxdata.top, width: cropboxdata.width, height: cropboxdata.height }; } return data || {}; }, /** * set the crop box position and size with new data. * @param {object} data - the new crop box data. * @returns {cropper} this */ setcropboxdata: function setcropboxdata(data) { var cropboxdata = this.cropboxdata; var aspectratio = this.options.aspectratio; var widthchanged; var heightchanged; if (this.ready && this.cropped && !this.disabled && isplainobject(data)) { if (isnumber(data.left)) { cropboxdata.left = data.left; } if (isnumber(data.top)) { cropboxdata.top = data.top; } if (isnumber(data.width) && data.width !== cropboxdata.width) { widthchanged = true; cropboxdata.width = data.width; } if (isnumber(data.height) && data.height !== cropboxdata.height) { heightchanged = true; cropboxdata.height = data.height; } if (aspectratio) { if (widthchanged) { cropboxdata.height = cropboxdata.width / aspectratio; } else if (heightchanged) { cropboxdata.width = cropboxdata.height * aspectratio; } } this.rendercropbox(); } return this; }, /** * get a canvas drawn the cropped image. * @param {object} [options={}] - the config options. * @returns {htmlcanvaselement} - the result canvas. */ getcroppedcanvas: function getcroppedcanvas() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (!this.ready || !window.htmlcanvaselement) { return null; } var canvasdata = this.canvasdata; var source = getsourcecanvas(this.image, this.imagedata, canvasdata, options); // returns the source canvas if it is not cropped. if (!this.cropped) { return source; } var _this$getdata = this.getdata(), initialx = _this$getdata.x, initialy = _this$getdata.y, initialwidth = _this$getdata.width, initialheight = _this$getdata.height; var ratio = source.width / math.floor(canvasdata.naturalwidth); if (ratio !== 1) { initialx *= ratio; initialy *= ratio; initialwidth *= ratio; initialheight *= ratio; } var aspectratio = initialwidth / initialheight; var maxsizes = getadjustedsizes({ aspectratio: aspectratio, width: options.maxwidth || infinity, height: options.maxheight || infinity }); var minsizes = getadjustedsizes({ aspectratio: aspectratio, width: options.minwidth || 0, height: options.minheight || 0 }, 'cover'); var _getadjustedsizes = getadjustedsizes({ aspectratio: aspectratio, width: options.width || (ratio !== 1 ? source.width : initialwidth), height: options.height || (ratio !== 1 ? source.height : initialheight) }), width = _getadjustedsizes.width, height = _getadjustedsizes.height; width = math.min(maxsizes.width, math.max(minsizes.width, width)); height = math.min(maxsizes.height, math.max(minsizes.height, height)); var canvas = document.createelement('canvas'); var context = canvas.getcontext('2d'); canvas.width = normalizedecimalnumber(width); canvas.height = normalizedecimalnumber(height); context.fillstyle = options.fillcolor || 'transparent'; context.fillrect(0, 0, width, height); var _options$imagesmoothi = options.imagesmoothingenabled, imagesmoothingenabled = _options$imagesmoothi === void 0 ? true : _options$imagesmoothi, imagesmoothingquality = options.imagesmoothingquality; context.imagesmoothingenabled = imagesmoothingenabled; if (imagesmoothingquality) { context.imagesmoothingquality = imagesmoothingquality; } // https://developer.mozilla.org/en-us/docs/web/api/canvasrenderingcontext2d.drawimage var sourcewidth = source.width; var sourceheight = source.height; // source canvas parameters var srcx = initialx; var srcy = initialy; var srcwidth; var srcheight; // destination canvas parameters var dstx; var dsty; var dstwidth; var dstheight; if (srcx <= -initialwidth || srcx > sourcewidth) { srcx = 0; srcwidth = 0; dstx = 0; dstwidth = 0; } else if (srcx <= 0) { dstx = -srcx; srcx = 0; srcwidth = math.min(sourcewidth, initialwidth + srcx); dstwidth = srcwidth; } else if (srcx <= sourcewidth) { dstx = 0; srcwidth = math.min(initialwidth, sourcewidth - srcx); dstwidth = srcwidth; } if (srcwidth <= 0 || srcy <= -initialheight || srcy > sourceheight) { srcy = 0; srcheight = 0; dsty = 0; dstheight = 0; } else if (srcy <= 0) { dsty = -srcy; srcy = 0; srcheight = math.min(sourceheight, initialheight + srcy); dstheight = srcheight; } else if (srcy <= sourceheight) { dsty = 0; srcheight = math.min(initialheight, sourceheight - srcy); dstheight = srcheight; } var params = [srcx, srcy, srcwidth, srcheight]; // avoid "indexsizeerror" if (dstwidth > 0 && dstheight > 0) { var scale = width / initialwidth; params.push(dstx * scale, dsty * scale, dstwidth * scale, dstheight * scale); } // all the numerical parameters should be integer for `drawimage` // https://github.com/fengyuanchen/cropper/issues/476 context.drawimage.apply(context, [source].concat(_toconsumablearray(params.map(function (param) { return math.floor(normalizedecimalnumber(param)); })))); return canvas; }, /** * change the aspect ratio of the crop box. * @param {number} aspectratio - the new aspect ratio. * @returns {cropper} this */ setaspectratio: function setaspectratio(aspectratio) { var options = this.options; if (!this.disabled && !isundefined(aspectratio)) { // 0 -> nan options.aspectratio = math.max(0, aspectratio) || nan; if (this.ready) { this.initcropbox(); if (this.cropped) { this.rendercropbox(); } } } return this; }, /** * change the drag mode. * @param {string} mode - the new drag mode. * @returns {cropper} this */ setdragmode: function setdragmode(mode) { var options = this.options, dragbox = this.dragbox, face = this.face; if (this.ready && !this.disabled) { var croppable = mode === drag_mode_crop; var movable = options.movable && mode === drag_mode_move; mode = croppable || movable ? mode : drag_mode_none; options.dragmode = mode; setdata(dragbox, data_action, mode); toggleclass(dragbox, class_crop, croppable); toggleclass(dragbox, class_move, movable); if (!options.cropboxmovable) { // sync drag mode to crop box when it is not movable setdata(face, data_action, mode); toggleclass(face, class_crop, croppable); toggleclass(face, class_move, movable); } } return this; } }; var anothercropper = window.cropper; var cropper = /*#__pure__*/ function () { /** * create a new cropper. * @param {element} element - the target element for cropping. * @param {object} [options={}] - the configuration options. */ function cropper(element) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classcallcheck(this, cropper); if (!element || !regexp_tag_name.test(element.tagname)) { throw new error('the first argument is required and must be an <img> or <canvas> element.'); } this.element = element; this.options = assign({}, defaults, isplainobject(options) && options); this.cropped = false; this.disabled = false; this.pointers = {}; this.ready = false; this.reloading = false; this.replaced = false; this.sized = false; this.sizing = false; this.init(); } _createclass(cropper, [{ key: "init", value: function init() { var element = this.element; var tagname = element.tagname.tolowercase(); var url; if (element[namespace]) { return; } element[namespace] = this; if (tagname === 'img') { this.isimg = true; // e.g.: "img/picture.jpg" url = element.getattribute('src') || ''; this.originalurl = url; // stop when it's a blank image if (!url) { return; } // e.g.: "http://example.com/img/picture.jpg" url = element.src; } else if (tagname === 'canvas' && window.htmlcanvaselement) { url = element.todataurl(); } this.load(url); } }, { key: "load", value: function load(url) { var _this = this; if (!url) { return; } this.url = url; this.imagedata = {}; var element = this.element, options = this.options; if (!options.rotatable && !options.scalable) { options.checkorientation = false; } // only ie10+ supports typed arrays if (!options.checkorientation || !window.arraybuffer) { this.clone(); return; } // xmlhttprequest disallows to open a data url in some browsers like ie11 and safari if (regexp_data_url.test(url)) { if (regexp_data_url_jpeg.test(url)) { this.read(dataurltoarraybuffer(url)); } else { this.clone(); } return; } var xhr = new xmlhttprequest(); var clone = this.clone.bind(this); this.reloading = true; this.xhr = xhr; xhr.ontimeout = clone; xhr.onabort = clone; xhr.onerror = clone; xhr.onprogress = function () { if (xhr.getresponseheader('content-type') !== mime_type_jpeg) { xhr.abort(); } }; xhr.onload = function () { _this.read(xhr.response); }; xhr.onloadend = function () { _this.reloading = false; _this.xhr = null; }; // bust cache when there is a "crossorigin" property to avoid browser cache error if (options.checkcrossorigin && iscrossoriginurl(url) && element.crossorigin) { url = addtimestamp(url); } xhr.open('get', url); xhr.responsetype = 'arraybuffer'; xhr.withcredentials = element.crossorigin === 'use-credentials'; xhr.send(); } }, { key: "read", value: function read(arraybuffer) { var options = this.options, imagedata = this.imagedata; var orientation = resetandgetorientation(arraybuffer); var rotate = 0; var scalex = 1; var scaley = 1; if (orientation > 1) { // generate a new data url with the orientation value set to 1 // as some ios browsers will render image with its orientation this.url = arraybuffertodataurl(arraybuffer, mime_type_jpeg); var _parseorientation = parseorientation(orientation); rotate = _parseorientation.rotate; scalex = _parseorientation.scalex; scaley = _parseorientation.scaley; } if (options.rotatable) { imagedata.rotate = rotate; } if (options.scalable) { imagedata.scalex = scalex; imagedata.scaley = scaley; } this.clone(); } }, { key: "clone", value: function clone() { var element = this.element, url = this.url; var crossorigin; var crossoriginurl; if (this.options.checkcrossorigin && iscrossoriginurl(url)) { crossorigin = element.crossorigin; if (crossorigin) { crossoriginurl = url; } else { crossorigin = 'anonymous'; // bust cache when there is not a "crossorigin" property crossoriginurl = addtimestamp(url); } } this.crossorigin = crossorigin; this.crossoriginurl = crossoriginurl; var image = document.createelement('img'); if (crossorigin) { image.crossorigin = crossorigin; } image.src = crossoriginurl || url; this.image = image; image.onload = this.start.bind(this); image.onerror = this.stop.bind(this); addclass(image, class_hide); element.parentnode.insertbefore(image, element.nextsibling); } }, { key: "start", value: function start() { var _this2 = this; var image = this.isimg ? this.element : this.image; image.onload = null; image.onerror = null; this.sizing = true; var is_safari = window.navigator && /(macintosh|iphone|ipod|ipad).*applewebkit/i.test(window.navigator.useragent); var done = function done(naturalwidth, naturalheight) { assign(_this2.imagedata, { naturalwidth: naturalwidth, naturalheight: naturalheight, aspectratio: naturalwidth / naturalheight }); _this2.sizing = false; _this2.sized = true; _this2.build(); }; // modern browsers (except safari) if (image.naturalwidth && !is_safari) { done(image.naturalwidth, image.naturalheight); return; } var sizingimage = document.createelement('img'); var body = document.body || document.documentelement; this.sizingimage = sizingimage; sizingimage.onload = function () { done(sizingimage.width, sizingimage.height); if (!is_safari) { body.removechild(sizingimage); } }; sizingimage.src = image.src; // ios safari will convert the image automatically // with its orientation once append it into dom (#279) if (!is_safari) { sizingimage.style.csstext = 'left:0;' + 'max-height:none!important;' + 'max-width:none!important;' + 'min-height:0!important;' + 'min-width:0!important;' + 'opacity:0;' + 'position:absolute;' + 'top:0;' + 'z-index:-1;'; body.appendchild(sizingimage); } } }, { key: "stop", value: function stop() { var image = this.image; image.onload = null; image.onerror = null; image.parentnode.removechild(image); this.image = null; } }, { key: "build", value: function build() { if (!this.sized || this.ready) { return; } var element = this.element, options = this.options, image = this.image; // create cropper elements var container = element.parentnode; var template = document.createelement('div'); template.innerhtml = template; var cropper = template.queryselector(".".concat(namespace, "-container")); var canvas = cropper.queryselector(".".concat(namespace, "-canvas")); var dragbox = cropper.queryselector(".".concat(namespace, "-drag-box")); var cropbox = cropper.queryselector(".".concat(namespace, "-crop-box")); var face = cropbox.queryselector(".".concat(namespace, "-face")); this.container = container; this.cropper = cropper; this.canvas = canvas; this.dragbox = dragbox; this.cropbox = cropbox; this.viewbox = cropper.queryselector(".".concat(namespace, "-view-box")); this.face = face; canvas.appendchild(image); // hide the original image addclass(element, class_hidden); // inserts the cropper after to the current image container.insertbefore(cropper, element.nextsibling); // show the image if is hidden if (!this.isimg) { removeclass(image, class_hide); } this.initpreview(); this.bind(); options.initialaspectratio = math.max(0, options.initialaspectratio) || nan; options.aspectratio = math.max(0, options.aspectratio) || nan; options.viewmode = math.max(0, math.min(3, math.round(options.viewmode))) || 0; addclass(cropbox, class_hidden); if (!options.guides) { addclass(cropbox.getelementsbyclassname("".concat(namespace, "-dashed")), class_hidden); } if (!options.center) { addclass(cropbox.getelementsbyclassname("".concat(namespace, "-center")), class_hidden); } if (options.background) { addclass(cropper, "".concat(namespace, "-bg")); } if (!options.highlight) { addclass(face, class_invisible); } if (options.cropboxmovable) { addclass(face, class_move); setdata(face, data_action, action_all); } if (!options.cropboxresizable) { addclass(cropbox.getelementsbyclassname("".concat(namespace, "-line")), class_hidden); addclass(cropbox.getelementsbyclassname("".concat(namespace, "-point")), class_hidden); } this.render(); this.ready = true; this.setdragmode(options.dragmode); if (options.autocrop) { this.crop(); } this.setdata(options.data); if (isfunction(options.ready)) { addlistener(element, event_ready, options.ready, { once: true }); } dispatchevent(element, event_ready); } }, { key: "unbuild", value: function unbuild() { if (!this.ready) { return; } this.ready = false; this.unbind(); this.resetpreview(); this.cropper.parentnode.removechild(this.cropper); removeclass(this.element, class_hidden); } }, { key: "uncreate", value: function uncreate() { if (this.ready) { this.unbuild(); this.ready = false; this.cropped = false; } else if (this.sizing) { this.sizingimage.onload = null; this.sizing = false; this.sized = false; } else if (this.reloading) { this.xhr.onabort = null; this.xhr.abort(); } else if (this.image) { this.stop(); } } /** * get the no conflict cropper class. * @returns {cropper} the cropper class. */ }], [{ key: "noconflict", value: function noconflict() { window.cropper = anothercropper; return cropper; } /** * change the default options. * @param {object} options - the new default options. */ }, { key: "setdefaults", value: function setdefaults(options) { assign(defaults, isplainobject(options) && options); } }]); return cropper; }(); assign(cropper.prototype, render, preview, events, handlers, change, methods); return cropper;})));cropper-js
cropper-jquery-js
/*! * jquery cropper v1.0.0 * https://github.com/fengyuanchen/jquery-cropper * * copyright (c) 2018 chen fengyuan * released under the mit license * * date: 2018-04-01t06:20:13.168z */(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('jquery'), require('cropperjs')) : typeof define === 'function' && define.amd ? define(['jquery', 'cropperjs'], factory) : global.layui && layui.define ? layui.define(['jquery', 'cropper'], function (exports) { exports('jqcropper', factory(layui.jquery, layui.cropper)) }) : (factory(global.jquery, global.cropper));}(this, (function ($, cropper) { 'use strict'; $ = $ && $.hasownproperty('default') ? $['default'] : $; cropper = cropper && cropper.hasownproperty('default') ? cropper['default'] : cropper; if ($.fn) { var anothercropper = $.fn.cropper; var namespace = 'cropper'; $.fn.cropper = function jquerycropper(option) { for (var _len = arguments.length, args = array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } var result = void 0; this.each(function (i, element) { var $element = $(element); var isdestroy = option === 'destroy'; var cropper = $element.data(namespace); if (!cropper) { if (isdestroy) { return; } var options = $.extend({}, $element.data(), $.isplainobject(option) && option); cropper = new cropper(element, options); $element.data(namespace, cropper); } if (typeof option === 'string') { var fn = cropper[option]; if ($.isfunction(fn)) { result = fn.apply(cropper, args); if (result === cropper) { result = undefined; } if (isdestroy) { $element.removedata(namespace); } } } }); return result !== undefined ? result : this; }; $.fn.cropper.constructor = cropper; $.fn.cropper.setdefaults = cropper.setdefaults; $.fn.cropper.noconflict = function noconflict() { $.fn.cropper = anothercropper; return this; }; }})));cropper-jquery
实现图片裁剪的代码
裁剪html
<!doctype html><html><head> <meta charset="utf-8"> <title></title> <meta name="renderer" content="webkit"> <meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> <link rel="stylesheet" href="{{static_url('layui/css/layui.css')}}" media="all"> <link rel="stylesheet" href="{{static_url('cropper/cropper.css')}}"> <style> #choice-btn { display: inline-block; height: 38px; line-height: 38px; padding: 0 18px; background-color: #009688; color: #fff; white-space: nowrap; text-align: center; font-size: 14px; border: none; border-radius: 2px; cursor: pointer; } </style></head><body><div style="margin: 20px;"> <div style="margin-bottom:30px;"> <label for="cropper_imgupload"> <span id="choice-btn"><i>&#xe67c;</i>选择图片</span> </label> <input type="file" id="cropper_imgupload" name="file" style="display:none" accept="image/*"> </div> <div class="layui-row layui-col-space15"> <div> <div id="readyimg" style="height: 360px;width: 100%;border: 1px dashed black;background-color: rgb(247, 247, 247);"> <img id="img" src="" alt=""> </div> </div> <div> <div>预览:</div> <div id="img-preview" style="width: 180px;height: 120px;overflow: hidden;border: 1px dashed black;"> </div> </div> </div> <div class="layui-row layui-hide oper-btn" style="margin-top: 20px;"> <div> <button type="button" class="layui-btn layui-icon layui-icon-left" cropper-event="rotate" data-option="-15" title="rotate -90 degrees"> 向左旋转 </button> <button type="button" class="layui-btn layui-icon layui-icon-right" cropper-event="rotate" data-option="15" title="rotate 90 degrees"> 向右旋转 </button> <button type="button" class="layui-btn layui-icon layui-icon-refresh" cropper-event="reset" title="reset-image">重置图片 </button> </div> <div class="layui-col-xs2 layui-col-xs-offset1"> <button type="button" class="layui-btn layui-btn-fluid" id="keep-save" cropper-event="confirmsave">保存修改 </button> </div> </div></div><script src="{{static_url('layui/layui.js')}}" charset="utf-8"></script><script> layui.config({ base: '/static/cropper/' }).extend({ cropper: 'cropper' }).use(['element', 'layer', 'cropper', 'jquery', 'jqcropper'], function () { var element = layui.element; var $ = layui.jquery; var layer = layui.layer; var cropper = layui.cropper; var imageele = $("#img") , preview = '#img-preview' , file = $("input[name='file']") , uploadimagemaxsize = 2048 //文件上传大小限制 , options = { aspectratio: 3 / 2, viewmode: 1, preview: preview, dragmode: 'none', responsive: false, restore: false // cropboxmovable:false, // cropboxresizable:false, }; // 找到上传图片的input标签绑定change事件 $("#cropper_imgupload").change(function () { $(".oper-btn").removeclass("layui-hide"); // 1. 创建一个读取文件的对象 var filereader = new filereader(); filereader.readasdataurl(this.files[0]); filereader.onload = function () { // 2. 等上一步读完文件之后才 把图片加载到img标签中 imageele.attr("src", filereader.result); //图片链接(base64) imageele.cropper(options); }; }); $(".layui-btn").on('click', function () { var event = $(this).attr("cropper-event"); //监听确认保存图像 if (event === 'confirmsave') { imageele.cropper("getcroppedcanvas").toblob(function (blob) { var filesize = (blob["size"] / 1024).tofixed(2); if (filesize < uploadimagemaxsize) { var formdata = new formdata(); formdata.append('file', blob); $.ajax({ method: "post", url: '/web/api/upload/upload?option={{option}}', //用于文件上传的服务器端请求地址 data: formdata, processdata: false, contenttype: false, success: function (response) { if (response.code === 0) { // 代开裁剪页面的回调函数 parent.croppercallback(response.data, '{{index}}'); layer.msg("上传成功", { icon: 1, time: 2500 // 默认三秒 }, function () { // 回调关闭弹出层 var index = parent.layer.getframeindex(window.name); parent.layer.close(index); }); $(".canvanslog").attr("src", response.data[0].fp_show); } else { layer.alert(response.msg, {icon: 2}); } }, error: function (response) { layer.alert("网络异常", {icon: 2}); } }); } else { layer.alert("上传图片大小不超过" + uploadimagemaxsize + "kb", {icon: 2}) } }); } else if (event === 'rotate') { //监听旋转 var option = $(this).attr('data-option'); imageele.cropper('rotate', option); } else if (event === 'reset') { //重设图片 imageele.cropper('reset'); } }); });</script></body></html>cropper-html
父页面的回调
// 图片上传成功回调函数 function croppercallback(response, index) { if (response) { var inputele = document.getelementbyid("imginput"); if (index === "none") { // 添加图片的回调 var trele = document.createelement("tr"); var addimageindex = totalarray.length; trele.innerhtml = '<td><img src="' + response[0].fp_show + '" alt=""></td><td><span style="color: #5fb878;">添加成功</span></td><td>' + '<button type="button" class="layui-btn layui-btn-sm edit-btn" id="edit_' + addimageindex + '">修改</button>' + '<button type="button" class="layui-btn layui-btn-sm delete-btn" id="delete_' + addimageindex + '">删除</button></td>'; document.getelementbyid("imglist").appendchild(trele); totalarray.push(response[0].fp_relative); } else { // 修改图片的回调 var thistrchild = document.getelementbyid("edit_" + index).parentelement.parentelement.children; thistrchild[0].innerhtml = '<img src="' + response[0].fp_show + '" alt="">'; thistrchild[1].innerhtml = '<span style="color: #5fb878;">修改成功</span>'; totalarray[parseint(index)] = response[0].fp_relative; } inputele.value = totalarray.join(","); } }
更多layui知识请关注layui使用教程栏目。
出处:https://www.cnblogs.com/zhaopanpan/
以上就是基于layui+cropper.js实现上传图片的裁剪功能的方法的详细内容。
其它类似信息

推荐信息