公司发布微信H5前端阵子刚刚弄好的H5端的图片上传插件,现在有需要裁剪图片。前端找了一个插件---cropper

本人对这插件不怎么熟悉,这个案例最好用在一个页面只有一个上传图片的功能上而且只适合单个图片上传:

本案例的主要思路是:使用H5的canvas对象,通过canvas对象调用方法把图片转换成base64上传图片

引入CSS以及js:

1 <link rel="stylesheet" href="css/cropper.css"/>
2 <link rel="stylesheet" href="css/cropper-main.css"/>
3 <script src="js/cropper.js"></script>
4 <script src="js/upload-main.js"></script>

html:

1 <div class="head-img">>
2   <input type="file" class="hidden" id="inputImage" accept="image/*" />
3 </div>

cropper.js:

   1 /*!
   2  * Cropper v0.9.2
   3  * https://github.com/fengyuanchen/cropper
   4  *
   5  * Copyright (c) 2014-2015 Fengyuan Chen and contributors
   6  * Released under the MIT license
   7  *
   8  * Date: 2015-04-18T04:35:01.500Z
   9  */
  10 
  11 (function (factory) {
  12   if (typeof define === 'function' && define.amd) {
  13     // AMD. Register as anonymous module.
  14     define(['jquery'], factory);
  15   } else if (typeof exports === 'object') {
  16     // Node / CommonJS
  17     factory(require('jquery'));
  18   } else {
  19     // Browser globals.
  20     factory(jQuery);
  21   }
  22 })(function ($) {
  23 
  24   'use strict';
  25 
  26   var $window = $(window),
  27       $document = $(document),
  28       location = window.location,
  29 
  30       // Constants
  31       CROPPER_NAMESPACE = '.cropper',
  32       CROPPER_PREVIEW = 'preview' + CROPPER_NAMESPACE,
  33 
  34       // RegExps
  35       REGEXP_DRAG_TYPES = /^(e|n|w|s|ne|nw|sw|se|all|crop|move|zoom)$/,
  36 
  37       // Classes
  38       CLASS_MODAL = 'cropper-modal',
  39       CLASS_HIDE = 'cropper-hide',
  40       CLASS_HIDDEN = 'cropper-hidden',
  41       CLASS_INVISIBLE = 'cropper-invisible',
  42       CLASS_MOVE = 'cropper-move',
  43       CLASS_CROP = 'cropper-crop',
  44       CLASS_DISABLED = 'cropper-disabled',
  45       CLASS_BG = 'cropper-bg',
  46 
  47       // Events
  48       EVENT_MOUSE_DOWN = 'mousedown touchstart',
  49       EVENT_MOUSE_MOVE = 'mousemove touchmove',
  50       EVENT_MOUSE_UP = 'mouseup mouseleave touchend touchleave touchcancel',
  51       EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll',
  52       EVENT_DBLCLICK = 'dblclick',
  53       EVENT_RESIZE = 'resize' + CROPPER_NAMESPACE, // Bind to window with namespace
  54       EVENT_BUILD = 'build' + CROPPER_NAMESPACE,
  55       EVENT_BUILT = 'built' + CROPPER_NAMESPACE,
  56       EVENT_DRAG_START = 'dragstart' + CROPPER_NAMESPACE,
  57       EVENT_DRAG_MOVE = 'dragmove' + CROPPER_NAMESPACE,
  58       EVENT_DRAG_END = 'dragend' + CROPPER_NAMESPACE,
  59       EVENT_ZOOM_IN = 'zoomin' + CROPPER_NAMESPACE,
  60       EVENT_ZOOM_OUT = 'zoomout' + CROPPER_NAMESPACE,
  61 
  62       // Supports
  63       SUPPORT_CANVAS = $.isFunction($('<canvas>')[0].getContext),
  64 
  65       // Others
  66       sqrt = Math.sqrt,
  67       min = Math.min,
  68       max = Math.max,
  69       abs = Math.abs,
  70       sin = Math.sin,
  71       cos = Math.cos,
  72       num = parseFloat,
  73 
  74       // Prototype
  75       prototype = {};
  76 
  77   function isNumber(n) {
  78     return typeof n === 'number';
  79   }
  80 
  81   function isUndefined(n) {
  82     return typeof n === 'undefined';
  83   }
  84 
  85   function toArray(obj, offset) {
  86     var args = [];
  87 
  88     if (isNumber(offset)) { // It's necessary for IE8
  89       args.push(offset);
  90     }
  91 
  92     return args.slice.apply(obj, args);
  93   }
  94 
  95   // Custom proxy to avoid jQuery's guid
  96   function proxy(fn, context) {
  97     var args = toArray(arguments, 2);
  98 
  99     return function () {
 100       return fn.apply(context, args.concat(toArray(arguments)));
 101     };
 102   }
 103 
 104   function isCrossOriginURL(url) {
 105     var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i);
 106 
 107     return parts && (parts[1] !== location.protocol || parts[2] !== location.hostname || parts[3] !== location.port);
 108   }
 109 
 110   function addTimestamp(url) {
 111     var timestamp = 'timestamp=' + (new Date()).getTime();
 112 
 113     return (url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp);
 114   }
 115 
 116   function inRange(source, target) {
 117     return target.left < 0 && source.width < (target.left + target.width) && target.top < 0 && source.height < (target.top + target.height);
 118   }
 119 
 120   function getRotateValue(degree) {
 121     return degree ? 'rotate(' + degree + 'deg)' : 'none';
 122   }
 123 
 124   function getRotatedSizes(data, reverse) {
 125     var deg = abs(data.degree) % 180,
 126         arc = (deg > 90 ? (180 - deg) : deg) * Math.PI / 180,
 127         sinArc = sin(arc),
 128         cosArc = cos(arc),
 129         width = data.width,
 130         height = data.height,
 131         aspectRatio = data.aspectRatio,
 132         newWidth,
 133         newHeight;
 134 
 135     if (!reverse) {
 136       newWidth = width * cosArc + height * sinArc;
 137       newHeight = width * sinArc + height * cosArc;
 138     } else {
 139       newWidth = width / (cosArc + sinArc / aspectRatio);
 140       newHeight = newWidth / aspectRatio;
 141     }
 142 
 143     return {
 144       width: newWidth,
 145       height: newHeight
 146     };
 147   }
 148 
 149   function getSourceCanvas(image, data) {
 150     var canvas = $('<canvas>')[0],
 151         context = canvas.getContext('2d'),
 152         width = data.naturalWidth,
 153         height = data.naturalHeight,
 154         rotate = data.rotate,
 155         rotated = getRotatedSizes({
 156           width: width,
 157           height: height,
 158           degree: rotate
 159         });
 160 
 161     if (rotate) {
 162       canvas.width = rotated.width;
 163       canvas.height = rotated.height;
 164       context.save();
 165       context.translate(rotated.width / 2, rotated.height / 2);
 166       context.rotate(rotate * Math.PI / 180);
 167       context.drawImage(image, -width / 2, -height / 2, width, height);
 168       context.restore();
 169     } else {
 170       canvas.width = width;
 171       canvas.height = height;
 172       context.drawImage(image, 0, 0, width, height);
 173     }
 174 
 175     return canvas;
 176   }
 177 
 178   function Cropper(element, options) {
 179     this.$element = $(element);
 180     this.options = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) && options);
 181 
 182     this.ready = false;
 183     this.built = false;
 184     this.rotated = false;
 185     this.cropped = false;
 186     this.disabled = false;
 187     this.canvas = null;
 188     this.cropBox = null;
 189 
 190     this.load();
 191   }
 192 
 193   prototype.load = function (url) {
 194     var options = this.options,
 195         $this = this.$element,
 196         crossOrigin,
 197         bustCacheUrl,
 198         buildEvent,
 199         $clone;
 200 
 201     if (!url) {
 202       if ($this.is('img')) {
 203         if (!$this.attr('src')) {
 204           return;
 205         }
 206 
 207         url = $this.prop('src');
 208       } else if ($this.is('canvas') && SUPPORT_CANVAS) {
 209         url = $this[0].toDataURL();
 210       }
 211     }
 212 
 213     if (!url) {
 214       return;
 215     }
 216 
 217     buildEvent = $.Event(EVENT_BUILD);
 218     $this.one(EVENT_BUILD, options.build).trigger(buildEvent); // Only trigger once
 219 
 220     if (buildEvent.isDefaultPrevented()) {
 221       return;
 222     }
 223 
 224     if (options.checkImageOrigin && isCrossOriginURL(url)) {
 225       crossOrigin = 'anonymous';
 226 
 227       if (!$this.prop('crossOrigin')) { // Only when there was not a "crossOrigin" property
 228         bustCacheUrl = addTimestamp(url); // Bust cache (#148)
 229       }
 230     }
 231 
 232     this.$clone = $clone = $('<img>');
 233 
 234     $clone.one('load', $.proxy(function () {
 235       var naturalWidth = $clone.prop('naturalWidth') || $clone.width(),
 236           naturalHeight = $clone.prop('naturalHeight') || $clone.height();
 237 
 238       this.image = {
 239         naturalWidth: naturalWidth,
 240         naturalHeight: naturalHeight,
 241         aspectRatio: naturalWidth / naturalHeight,
 242         rotate: 0
 243       };
 244 
 245       this.url = url;
 246       this.ready = true;
 247       this.build();
 248     }, this)).one('error', function () {
 249       $clone.remove();
 250     }).attr({
 251       src: bustCacheUrl || url,
 252       crossOrigin: crossOrigin
 253     });
 254 
 255     // Hide and insert into the document
 256     $clone.addClass(CLASS_HIDE).insertAfter($this);
 257   };
 258 
 259   prototype.build = function () {
 260     var $this = this.$element,
 261         $clone = this.$clone,
 262         options = this.options,
 263         $cropper,
 264         $cropBox;
 265 
 266     if (!this.ready) {
 267       return;
 268     }
 269 
 270     if (this.built) {
 271       this.unbuild();
 272     }
 273 
 274     // Create cropper elements
 275     this.$cropper = $cropper = $(Cropper.TEMPLATE);
 276 
 277     // Hide the original image
 278     $this.addClass(CLASS_HIDDEN);
 279 
 280     // Show the clone iamge
 281     $clone.removeClass(CLASS_HIDE);
 282 
 283     this.$container = $this.parent().append($cropper);
 284     this.$canvas = $cropper.find('.cropper-canvas').append($clone);
 285     this.$dragBox = $cropper.find('.cropper-drag-box');
 286     this.$cropBox = $cropBox = $cropper.find('.cropper-crop-box');
 287     this.$viewBox = $cropper.find('.cropper-view-box');
 288 
 289     this.addListeners();
 290     this.initPreview();
 291 
 292     // Format aspect ratio
 293     options.aspectRatio = num(options.aspectRatio) || NaN; // 0 -> NaN
 294 
 295     if (options.autoCrop) {
 296       this.cropped = true;
 297 
 298       if (options.modal) {
 299         this.$dragBox.addClass(CLASS_MODAL);
 300       }
 301     } else {
 302       $cropBox.addClass(CLASS_HIDDEN);
 303     }
 304 
 305     if (options.background) {
 306       $cropper.addClass(CLASS_BG);
 307     }
 308 
 309     if (!options.highlight) {
 310       $cropBox.find('.cropper-face').addClass(CLASS_INVISIBLE);
 311     }
 312 
 313     if (!options.guides) {
 314       $cropBox.find('.cropper-dashed').addClass(CLASS_HIDDEN);
 315     }
 316 
 317     if (!options.movable) {
 318       $cropBox.find('.cropper-face').data('drag', 'move');
 319     }
 320 
 321     if (!options.resizable) {
 322       $cropBox.find('.cropper-line, .cropper-point').addClass(CLASS_HIDDEN);
 323     }
 324 
 325     this.setDragMode(options.dragCrop ? 'crop' : 'move');
 326 
 327     this.built = true;
 328     this.render();
 329     $this.one(EVENT_BUILT, options.built).trigger(EVENT_BUILT); // Only trigger once
 330   };
 331 
 332   prototype.unbuild = function () {
 333     if (!this.built) {
 334       return;
 335     }
 336 
 337     this.built = false;
 338     this.container = null;
 339     this.canvas = null;
 340     this.cropBox = null; // This is necessary when replace
 341     this.removeListeners();
 342 
 343     this.resetPreview();
 344     this.$preview = null;
 345 
 346     this.$viewBox = null;
 347     this.$cropBox = null;
 348     this.$dragBox = null;
 349     this.$canvas = null;
 350     this.$container = null;
 351 
 352     this.$cropper.remove();
 353     this.$cropper = null;
 354   };
 355 
 356   $.extend(prototype, {
 357     render: function () {
 358       this.initContainer();
 359       this.initCanvas();
 360       this.initCropBox();
 361 
 362       this.renderCanvas();
 363 
 364       if (this.cropped) {
 365         this.renderCropBox();
 366       }
 367     },
 368 
 369     initContainer: function () {
 370       var $this = this.$element,
 371           $container = this.$container,
 372           $cropper = this.$cropper,
 373           options = this.options;
 374 
 375       $cropper.addClass(CLASS_HIDDEN);
 376       $this.removeClass(CLASS_HIDDEN);
 377 
 378       $cropper.css((this.container = {
 379         width: max($container.width(), num(options.minContainerWidth) || 200),
 380         height: max($container.height(), num(options.minContainerHeight) || 100)
 381       }));
 382 
 383       $this.addClass(CLASS_HIDDEN);
 384       $cropper.removeClass(CLASS_HIDDEN);
 385     },
 386 
 387     // image box (wrapper)
 388     initCanvas: function () {
 389       var container = this.container,
 390           containerWidth = container.width,
 391           containerHeight = container.height,
 392           image = this.image,
 393           aspectRatio = image.aspectRatio,
 394           canvas = {
 395             aspectRatio: aspectRatio,
 396             width: containerWidth,
 397             height: containerHeight
 398           };
 399 
 400       if (containerHeight * aspectRatio > containerWidth) {
 401         canvas.height = containerWidth / aspectRatio;
 402       } else {
 403         canvas.width = containerHeight * aspectRatio;
 404       }
 405 
 406       canvas.oldLeft = canvas.left = (containerWidth - canvas.width) / 2;
 407       canvas.oldTop = canvas.top = (containerHeight - canvas.height) / 2;
 408 
 409       this.canvas = canvas;
 410       this.limitCanvas(true, true);
 411       this.initialImage = $.extend({}, image);
 412       this.initialCanvas = $.extend({}, canvas);
 413     },
 414 
 415     limitCanvas: function (size, position) {
 416       var options = this.options,
 417           strict = options.strict,
 418           container = this.container,
 419           containerWidth = container.width,
 420           containerHeight = container.height,
 421           canvas = this.canvas,
 422           aspectRatio = canvas.aspectRatio,
 423           cropBox = this.cropBox,
 424           cropped = this.cropped && cropBox,
 425           minCanvasWidth,
 426           minCanvasHeight;
 427 
 428       if (size) {
 429         minCanvasWidth = num(options.minCanvasWidth) || 0;
 430         minCanvasHeight = num(options.minCanvasHeight) || 0;
 431 
 432         if (minCanvasWidth) {
 433           if (strict) {
 434             minCanvasWidth = max(cropped ? cropBox.width : containerWidth, minCanvasWidth);
 435           }
 436 
 437           minCanvasHeight = minCanvasWidth / aspectRatio;
 438         } else if (minCanvasHeight) {
 439 
 440           if (strict) {
 441             minCanvasHeight = max(cropped ? cropBox.height : containerHeight, minCanvasHeight);
 442           }
 443 
 444           minCanvasWidth = minCanvasHeight * aspectRatio;
 445         } else if (strict) {
 446           if (cropped) {
 447             minCanvasWidth = cropBox.width;
 448             minCanvasHeight = cropBox.height;
 449 
 450             if (minCanvasHeight * aspectRatio > minCanvasWidth) {
 451               minCanvasWidth = minCanvasHeight * aspectRatio;
 452             } else {
 453               minCanvasHeight = minCanvasWidth / aspectRatio;
 454             }
 455           } else {
 456             minCanvasWidth = containerWidth;
 457             minCanvasHeight = containerHeight;
 458 
 459             if (minCanvasHeight * aspectRatio > minCanvasWidth) {
 460               minCanvasHeight = minCanvasWidth / aspectRatio;
 461             } else {
 462               minCanvasWidth = minCanvasHeight * aspectRatio;
 463             }
 464           }
 465         }
 466 
 467         $.extend(canvas, {
 468           minWidth: minCanvasWidth,
 469           minHeight: minCanvasHeight,
 470           maxWidth: Infinity,
 471           maxHeight: Infinity
 472         });
 473       }
 474 
 475       if (position) {
 476         if (strict) {
 477           if (cropped) {
 478             canvas.minLeft = min(cropBox.left, (cropBox.left + cropBox.width) - canvas.width);
 479             canvas.minTop = min(cropBox.top, (cropBox.top + cropBox.height) - canvas.height);
 480             canvas.maxLeft = cropBox.left;
 481             canvas.maxTop = cropBox.top;
 482           } else {
 483             canvas.minLeft = min(0, containerWidth - canvas.width);
 484             canvas.minTop = min(0, containerHeight - canvas.height);
 485             canvas.maxLeft = max(0, containerWidth - canvas.width);
 486             canvas.maxTop = max(0, containerHeight - canvas.height);
 487           }
 488         } else {
 489           canvas.minLeft = -canvas.width;
 490           canvas.minTop = -canvas.height;
 491           canvas.maxLeft = containerWidth;
 492           canvas.maxTop = containerHeight;
 493         }
 494       }
 495     },
 496 
 497     renderCanvas: function (changed) {
 498       var options = this.options,
 499           canvas = this.canvas,
 500           image = this.image,
 501           aspectRatio,
 502           rotated;
 503 
 504       if (this.rotated) {
 505         this.rotated = false;
 506 
 507         // Computes rotatation sizes with image sizes
 508         rotated = getRotatedSizes({
 509           width: image.width,
 510           height: image.height,
 511           degree: image.rotate
 512         });
 513 
 514         aspectRatio = rotated.width / rotated.height;
 515 
 516         if (aspectRatio !== canvas.aspectRatio) {
 517           canvas.left -= (rotated.width - canvas.width) / 2;
 518           canvas.top -= (rotated.height - canvas.height) / 2;
 519           canvas.width = rotated.width;
 520           canvas.height = rotated.height;
 521           canvas.aspectRatio = aspectRatio;
 522           this.limitCanvas(true, false);
 523         }
 524       }
 525 
 526       if (canvas.width > canvas.maxWidth || canvas.width < canvas.minWidth) {
 527         canvas.left = canvas.oldLeft;
 528       }
 529 
 530       if (canvas.height > canvas.maxHeight || canvas.height < canvas.minHeight) {
 531         canvas.top = canvas.oldTop;
 532       }
 533 
 534       canvas.width = min(max(canvas.width, canvas.minWidth), canvas.maxWidth);
 535       canvas.height = min(max(canvas.height, canvas.minHeight), canvas.maxHeight);
 536 
 537       this.limitCanvas(false, true);
 538 
 539       canvas.oldLeft = canvas.left = min(max(canvas.left, canvas.minLeft), canvas.maxLeft);
 540       canvas.oldTop = canvas.top = min(max(canvas.top, canvas.minTop), canvas.maxTop);
 541 
 542       this.$canvas.css({
 543         width: canvas.width,
 544         height: canvas.height,
 545         left: canvas.left,
 546         top: canvas.top
 547       });
 548 
 549       this.renderImage();
 550 
 551       if (this.cropped && options.strict && !inRange(this.container, canvas)) {
 552         this.limitCropBox(true, true);
 553       }
 554 
 555       if (changed) {
 556         this.output();
 557       }
 558     },
 559 
 560     renderImage: function () {
 561       var canvas = this.canvas,
 562           image = this.image,
 563           reversed;
 564 
 565       if (image.rotate) {
 566         reversed = getRotatedSizes({
 567           width: canvas.width,
 568           height: canvas.height,
 569           degree: image.rotate,
 570           aspectRatio: image.aspectRatio
 571         }, true);
 572       }
 573 
 574       $.extend(image, reversed ? {
 575         width: reversed.width,
 576         height: reversed.height,
 577         left: (canvas.width - reversed.width) / 2,
 578         top: (canvas.height - reversed.height) / 2
 579       } : {
 580         width: canvas.width,
 581         height: canvas.height,
 582         left: 0,
 583         top: 0
 584       });
 585 
 586       this.$clone.css({
 587         width: image.width,
 588         height: image.height,
 589         marginLeft: image.left,
 590         marginTop: image.top,
 591         transform: getRotateValue(image.rotate)
 592       });
 593     },
 594 
 595     initCropBox: function () {
 596       var options = this.options,
 597           canvas = this.canvas,
 598           aspectRatio = options.aspectRatio,
 599           autoCropArea = num(options.autoCropArea) || 0.8,
 600           cropBox = {
 601             width: canvas.width,
 602             height: canvas.height
 603           };
 604 
 605       if (aspectRatio) {
 606         if (canvas.height * aspectRatio > canvas.width) {
 607           cropBox.height = cropBox.width / aspectRatio;
 608         } else {
 609           cropBox.width = cropBox.height * aspectRatio;
 610         }
 611       }
 612 
 613       this.cropBox = cropBox;
 614       this.limitCropBox(true, true);
 615 
 616       // Initialize auto crop area
 617       cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
 618       cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);
 619 
 620       // The width of auto crop area must large than "minWidth", and the height too. (#164)
 621       cropBox.width = max(cropBox.minWidth, cropBox.width * autoCropArea);
 622       cropBox.height = max(cropBox.minHeight, cropBox.height * autoCropArea);
 623       cropBox.oldLeft = cropBox.left = canvas.left + (canvas.width - cropBox.width) / 2;
 624       cropBox.oldTop = cropBox.top = canvas.top + (canvas.height - cropBox.height) / 2;
 625 
 626       this.initialCropBox = $.extend({}, cropBox);
 627     },
 628 
 629     limitCropBox: function (size, position) {
 630       var options = this.options,
 631           strict = options.strict,
 632           container = this.container,
 633           containerWidth = container.width,
 634           containerHeight = container.height,
 635           canvas = this.canvas,
 636           cropBox = this.cropBox,
 637           aspectRatio = options.aspectRatio,
 638           minCropBoxWidth,
 639           minCropBoxHeight;
 640 
 641       if (size) {
 642         minCropBoxWidth = num(options.minCropBoxWidth) || 0;
 643         minCropBoxHeight = num(options.minCropBoxHeight) || 0;
 644 
 645         // min/maxCropBoxWidth/Height must less than conatiner width/height
 646         cropBox.minWidth = min(containerWidth, minCropBoxWidth);
 647         cropBox.minHeight = min(containerHeight, minCropBoxHeight);
 648         cropBox.maxWidth = min(containerWidth, strict ? canvas.width : containerWidth);
 649         cropBox.maxHeight = min(containerHeight, strict ? canvas.height : containerHeight);
 650 
 651         if (aspectRatio) {
 652           // compare crop box size with container first
 653           if (cropBox.maxHeight * aspectRatio > cropBox.maxWidth) {
 654             cropBox.minHeight = cropBox.minWidth / aspectRatio;
 655             cropBox.maxHeight = cropBox.maxWidth / aspectRatio;
 656           } else {
 657             cropBox.minWidth = cropBox.minHeight * aspectRatio;
 658             cropBox.maxWidth = cropBox.maxHeight * aspectRatio;
 659           }
 660         }
 661 
 662         // The "minWidth" must be less than "maxWidth", and the "minHeight" too.
 663         cropBox.minWidth = min(cropBox.maxWidth, cropBox.minWidth);
 664         cropBox.minHeight = min(cropBox.maxHeight, cropBox.minHeight);
 665       }
 666 
 667       if (position) {
 668         if (strict) {
 669           cropBox.minLeft = max(0, canvas.left);
 670           cropBox.minTop = max(0, canvas.top);
 671           cropBox.maxLeft = min(containerWidth, canvas.left + canvas.width) - cropBox.width;
 672           cropBox.maxTop = min(containerHeight, canvas.top + canvas.height) - cropBox.height;
 673         } else {
 674           cropBox.minLeft = 0;
 675           cropBox.minTop = 0;
 676           cropBox.maxLeft = containerWidth - cropBox.width;
 677           cropBox.maxTop = containerHeight - cropBox.height;
 678         }
 679       }
 680     },
 681 
 682     renderCropBox: function () {
 683       var options = this.options,
 684           container = this.container,
 685           containerWidth = container.width,
 686           containerHeight = container.height,
 687           $cropBox = this.$cropBox,
 688           cropBox = this.cropBox;
 689 
 690       if (cropBox.width > cropBox.maxWidth || cropBox.width < cropBox.minWidth) {
 691         cropBox.left = cropBox.oldLeft;
 692       }
 693 
 694       if (cropBox.height > cropBox.maxHeight || cropBox.height < cropBox.minHeight) {
 695         cropBox.top = cropBox.oldTop;
 696       }
 697 
 698       cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
 699       cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);
 700 
 701       this.limitCropBox(false, true);
 702 
 703       cropBox.oldLeft = cropBox.left = min(max(cropBox.left, cropBox.minLeft), cropBox.maxLeft);
 704       cropBox.oldTop = cropBox.top = min(max(cropBox.top, cropBox.minTop), cropBox.maxTop);
 705 
 706       if (options.movable) {
 707         $cropBox.find('.cropper-face').data('drag', (cropBox.width === containerWidth && cropBox.height === containerHeight) ? 'move' : 'all');
 708       }
 709 
 710       $cropBox.css({
 711         width: cropBox.width,
 712         height: cropBox.height,
 713         left: cropBox.left,
 714         top: cropBox.top
 715       });
 716 
 717       if (this.cropped && options.strict && !inRange(container, this.canvas)) {
 718         this.limitCanvas(true, true);
 719       }
 720 
 721       if (!this.disabled) {
 722         this.output();
 723       }
 724     },
 725 
 726     output: function () {
 727       var options = this.options;
 728 
 729       this.preview();
 730 
 731       if (options.crop) {
 732         options.crop.call(this.$element, this.getData());
 733       }
 734     }
 735   });
 736 
 737   prototype.initPreview = function () {
 738     var url = this.url;
 739 
 740     this.$preview = $(this.options.preview);
 741     this.$viewBox.html('<img src="' + url + '">');
 742 
 743     // Override img element styles
 744     // Add `display:block` to avoid margin top issue (Occur only when margin-top <= -height)
 745     this.$preview.each(function () {
 746       var $this = $(this);
 747 
 748       $this.data(CROPPER_PREVIEW, {
 749         width: $this.width(),
 750         height: $this.height(),
 751         original: $this.html()
 752       }).html('<img src="' + url + '" style="display:block;width:100%;min-width:0!important;min-height:0!important;max-width:none!important;max-height:none!important;image-orientation: 0deg!important">');
 753     });
 754   };
 755 
 756   prototype.resetPreview = function () {
 757     this.$preview.each(function () {
 758       var $this = $(this);
 759 
 760       $this.html($this.data(CROPPER_PREVIEW).original).removeData(CROPPER_PREVIEW);
 761     });
 762   };
 763 
 764   prototype.preview = function () {
 765     var image = this.image,
 766         canvas = this.canvas,
 767         cropBox = this.cropBox,
 768         width = image.width,
 769         height = image.height,
 770         left = cropBox.left - canvas.left - image.left,
 771         top = cropBox.top - canvas.top - image.top,
 772         rotate = image.rotate;
 773 
 774     if (!this.cropped || this.disabled) {
 775       return;
 776     }
 777 
 778     this.$viewBox.find('img').css({
 779       width: width,
 780       height: height,
 781       marginLeft: -left,
 782       marginTop: -top,
 783       transform: getRotateValue(rotate)
 784     });
 785 
 786     this.$preview.each(function () {
 787       var $this = $(this),
 788           data = $this.data(CROPPER_PREVIEW),
 789           ratio = data.width / cropBox.width,
 790           newWidth = data.width,
 791           newHeight = cropBox.height * ratio;
 792 
 793       if (newHeight > data.height) {
 794         ratio = data.height / cropBox.height;
 795         newWidth = cropBox.width * ratio;
 796         newHeight = data.height;
 797       }
 798 
 799       $this.width(newWidth).height(newHeight).find('img').css({
 800         width: width * ratio,
 801         height: height * ratio,
 802         marginLeft: -left * ratio,
 803         marginTop: -top * ratio,
 804         transform: getRotateValue(rotate)
 805       });
 806     });
 807   };
 808 
 809   prototype.addListeners = function () {
 810     var options = this.options;
 811 
 812     this.$element.on(EVENT_DRAG_START, options.dragstart).on(EVENT_DRAG_MOVE, options.dragmove).on(EVENT_DRAG_END, options.dragend).on(EVENT_ZOOM_IN, options.zoomin).on(EVENT_ZOOM_OUT, options.zoomout);
 813     this.$cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.dragstart, this)).on(EVENT_DBLCLICK, $.proxy(this.dblclick, this));
 814 
 815     if (options.zoomable && options.mouseWheelZoom) {
 816       this.$cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this));
 817     }
 818 
 819     $document.on(EVENT_MOUSE_MOVE, (this._dragmove = proxy(this.dragmove, this))).on(EVENT_MOUSE_UP, (this._dragend = proxy(this.dragend, this)));
 820 
 821     if (options.responsive) {
 822       $window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this)));
 823     }
 824   };
 825 
 826   prototype.removeListeners = function () {
 827     var options = this.options;
 828 
 829     this.$element.off(EVENT_DRAG_START, options.dragstart).off(EVENT_DRAG_MOVE, options.dragmove).off(EVENT_DRAG_END, options.dragend).off(EVENT_ZOOM_IN, options.zoomin).off(EVENT_ZOOM_OUT, options.zoomout);
 830     this.$cropper.off(EVENT_MOUSE_DOWN, this.dragstart).off(EVENT_DBLCLICK, this.dblclick);
 831 
 832     if (options.zoomable && options.mouseWheelZoom) {
 833       this.$cropper.off(EVENT_WHEEL, this.wheel);
 834     }
 835 
 836     $document.off(EVENT_MOUSE_MOVE, this._dragmove).off(EVENT_MOUSE_UP, this._dragend);
 837 
 838     if (options.responsive) {
 839       $window.off(EVENT_RESIZE, this._resize);
 840     }
 841   };
 842 
 843   $.extend(prototype, {
 844     resize: function () {
 845       var $container = this.$container,
 846           container = this.container,
 847           canvasData,
 848           cropBoxData,
 849           ratio;
 850 
 851       if (this.disabled) {
 852         return;
 853       }
 854 
 855       ratio = $container.width() / container.width;
 856 
 857       if (ratio !== 1 || $container.height() !== container.height) {
 858         canvasData = this.getCanvasData();
 859         cropBoxData = this.getCropBoxData();
 860 
 861         this.render();
 862         this.setCanvasData($.each(canvasData, function (i, n) {
 863           canvasData[i] = n * ratio;
 864         }));
 865         this.setCropBoxData($.each(cropBoxData, function (i, n) {
 866           cropBoxData[i] = n * ratio;
 867         }));
 868       }
 869     },
 870 
 871     dblclick: function () {
 872       if (this.disabled) {
 873         return;
 874       }
 875 
 876       if (this.$dragBox.hasClass(CLASS_CROP)) {
 877         this.setDragMode('move');
 878       } else {
 879         this.setDragMode('crop');
 880       }
 881     },
 882 
 883     wheel: function (event) {
 884       var e = event.originalEvent,
 885           delta = 1;
 886 
 887       if (this.disabled) {
 888         return;
 889       }
 890 
 891       event.preventDefault();
 892 
 893       if (e.deltaY) {
 894         delta = e.deltaY > 0 ? 1 : -1;
 895       } else if (e.wheelDelta) {
 896         delta = -e.wheelDelta / 120;
 897       } else if (e.detail) {
 898         delta = e.detail > 0 ? 1 : -1;
 899       }
 900 
 901       this.zoom(-delta * 0.1);
 902     },
 903 
 904     dragstart: function (event) {
 905       var options = this.options,
 906           originalEvent = event.originalEvent,
 907           touches = originalEvent && originalEvent.touches,
 908           e = event,
 909           dragType,
 910           dragStartEvent,
 911           touchesLength;
 912 
 913       if (this.disabled) {
 914         return;
 915       }
 916 
 917       if (touches) {
 918         touchesLength = touches.length;
 919 
 920         if (touchesLength > 1) {
 921           if (options.zoomable && options.touchDragZoom && touchesLength === 2) {
 922             e = touches[1];
 923             this.startX2 = e.pageX;
 924             this.startY2 = e.pageY;
 925             dragType = 'zoom';
 926           } else {
 927             return;
 928           }
 929         }
 930 
 931         e = touches[0];
 932       }
 933 
 934       dragType = dragType || $(e.target).data('drag');
 935 
 936       if (REGEXP_DRAG_TYPES.test(dragType)) {
 937         event.preventDefault();
 938 
 939         dragStartEvent = $.Event(EVENT_DRAG_START, {
 940           originalEvent: originalEvent,
 941           dragType: dragType
 942         });
 943 
 944         this.$element.trigger(dragStartEvent);
 945 
 946         if (dragStartEvent.isDefaultPrevented()) {
 947           return;
 948         }
 949 
 950         this.dragType = dragType;
 951         this.cropping = false;
 952         this.startX = e.pageX;
 953         this.startY = e.pageY;
 954 
 955         if (dragType === 'crop') {
 956           this.cropping = true;
 957           this.$dragBox.addClass(CLASS_MODAL);
 958         }
 959       }
 960     },
 961 
 962     dragmove: function (event) {
 963       var options = this.options,
 964           originalEvent = event.originalEvent,
 965           touches = originalEvent && originalEvent.touches,
 966           e = event,
 967           dragType = this.dragType,
 968           dragMoveEvent,
 969           touchesLength;
 970 
 971       if (this.disabled) {
 972         return;
 973       }
 974 
 975       if (touches) {
 976         touchesLength = touches.length;
 977 
 978         if (touchesLength > 1) {
 979           if (options.zoomable && options.touchDragZoom && touchesLength === 2) {
 980             e = touches[1];
 981             this.endX2 = e.pageX;
 982             this.endY2 = e.pageY;
 983           } else {
 984             return;
 985           }
 986         }
 987 
 988         e = touches[0];
 989       }
 990 
 991       if (dragType) {
 992         event.preventDefault();
 993 
 994         dragMoveEvent = $.Event(EVENT_DRAG_MOVE, {
 995           originalEvent: originalEvent,
 996           dragType: dragType
 997         });
 998 
 999         this.$element.trigger(dragMoveEvent);
1000 
1001         if (dragMoveEvent.isDefaultPrevented()) {
1002           return;
1003         }
1004 
1005         this.endX = e.pageX;
1006         this.endY = e.pageY;
1007 
1008         this.change();
1009       }
1010     },
1011 
1012     dragend: function (event) {
1013       var dragType = this.dragType,
1014           dragEndEvent;
1015 
1016       if (this.disabled) {
1017         return;
1018       }
1019 
1020       if (dragType) {
1021         event.preventDefault();
1022 
1023         dragEndEvent = $.Event(EVENT_DRAG_END, {
1024           originalEvent: event.originalEvent,
1025           dragType: dragType
1026         });
1027 
1028         this.$element.trigger(dragEndEvent);
1029 
1030         if (dragEndEvent.isDefaultPrevented()) {
1031           return;
1032         }
1033 
1034         if (this.cropping) {
1035           this.cropping = false;
1036           this.$dragBox.toggleClass(CLASS_MODAL, this.cropped && this.options.modal);
1037         }
1038 
1039         this.dragType = '';
1040       }
1041     }
1042   });
1043 
1044   $.extend(prototype, {
1045     reset: function () {
1046       if (!this.built || this.disabled) {
1047         return;
1048       }
1049 
1050       this.image = $.extend({}, this.initialImage);
1051       this.canvas = $.extend({}, this.initialCanvas);
1052       this.renderCanvas();
1053 
1054       if (this.cropped) {
1055         this.cropBox = $.extend({}, this.initialCropBox);
1056         this.renderCropBox();
1057       }
1058     },
1059 
1060     clear: function () {
1061       if (!this.cropped || this.disabled) {
1062         return;
1063       }
1064 
1065       $.extend(this.cropBox, {
1066         left: 0,
1067         top: 0,
1068         width: 0,
1069         height: 0
1070       });
1071 
1072       this.cropped = false;
1073       this.renderCropBox();
1074 
1075       this.limitCanvas();
1076       this.renderCanvas(); // Render canvas after render crop box
1077 
1078       this.$dragBox.removeClass(CLASS_MODAL);
1079       this.$cropBox.addClass(CLASS_HIDDEN);
1080     },
1081 
1082     destroy: function () {
1083       var $this = this.$element;
1084 
1085       if (this.ready) {
1086         this.unbuild();
1087         $this.removeClass(CLASS_HIDDEN);
1088       } else {
1089         this.$clone.off('load').remove();
1090       }
1091 
1092       $this.removeData('cropper');
1093     },
1094 
1095     replace: function (url) {
1096       if (!this.disabled && url) {
1097         this.load(url);
1098       }
1099     },
1100 
1101     enable: function () {
1102       if (this.built) {
1103         this.disabled = false;
1104         this.$cropper.removeClass(CLASS_DISABLED);
1105       }
1106     },
1107 
1108     disable: function () {
1109       if (this.built) {
1110         this.disabled = true;
1111         this.$cropper.addClass(CLASS_DISABLED);
1112       }
1113     },
1114 
1115     move: function (offsetX, offsetY) {
1116       var canvas = this.canvas;
1117 
1118       if (this.built && !this.disabled && isNumber(offsetX) && isNumber(offsetY)) {
1119         canvas.left += offsetX;
1120         canvas.top += offsetY;
1121         this.renderCanvas(true);
1122       }
1123     },
1124 
1125     zoom: function (delta) {
1126       var canvas = this.canvas,
1127           zoomEvent,
1128           width,
1129           height;
1130 
1131       delta = num(delta);
1132 
1133       if (delta && this.built && !this.disabled && this.options.zoomable) {
1134         zoomEvent = delta > 0 ? $.Event(EVENT_ZOOM_IN) : $.Event(EVENT_ZOOM_OUT);
1135         this.$element.trigger(zoomEvent);
1136 
1137         if (zoomEvent.isDefaultPrevented()) {
1138           return;
1139         }
1140 
1141         delta = delta <= -1 ? 1 / (1 - delta) : delta <= 1 ? (1 + delta) : delta;
1142         width = canvas.width * delta;
1143         height = canvas.height * delta;
1144         canvas.left -= (width - canvas.width) / 2;
1145         canvas.top -= (height - canvas.height) / 2;
1146         canvas.width = width;
1147         canvas.height = height;
1148         this.renderCanvas(true);
1149         this.setDragMode('move');
1150       }
1151     },
1152 
1153     rotate: function (degree) {
1154       var image = this.image;
1155 
1156       degree = num(degree);
1157 
1158       if (degree && this.built && !this.disabled && this.options.rotatable) {
1159         image.rotate = (image.rotate + degree) % 360;
1160         this.rotated = true;
1161         this.renderCanvas(true);
1162       }
1163     },
1164 
1165     getData: function () {
1166       var cropBox = this.cropBox,
1167           canvas = this.canvas,
1168           image = this.image,
1169           ratio,
1170           data;
1171 
1172       if (this.built && this.cropped) {
1173         data = {
1174           x: cropBox.left - canvas.left,
1175           y: cropBox.top - canvas.top,
1176           width: cropBox.width,
1177           height: cropBox.height
1178         };
1179 
1180         ratio = image.width / image.naturalWidth;
1181 
1182         $.each(data, function (i, n) {
1183           n = n / ratio;
1184           data[i] = n;
1185         });
1186 
1187       } else {
1188         data = {
1189           x: 0,
1190           y: 0,
1191           width: 0,
1192           height: 0
1193         };
1194       }
1195 
1196       data.rotate = image.rotate;
1197 
1198       return data;
1199     },
1200 
1201     getContainerData: function () {
1202       return this.built ? this.container : {};
1203     },
1204 
1205     getImageData: function () {
1206       return this.ready ? this.image : {};
1207     },
1208 
1209     getCanvasData: function () {
1210       var canvas = this.canvas,
1211           data;
1212 
1213       if (this.built) {
1214         data = {
1215           left: canvas.left,
1216           top: canvas.top,
1217           width: canvas.width,
1218           height: canvas.height
1219         };
1220       }
1221 
1222       return data || {};
1223     },
1224 
1225     setCanvasData: function (data) {
1226       var canvas = this.canvas,
1227           aspectRatio = canvas.aspectRatio;
1228 
1229       if (this.built && !this.disabled && $.isPlainObject(data)) {
1230         if (isNumber(data.left)) {
1231           canvas.left = data.left;
1232         }
1233 
1234         if (isNumber(data.top)) {
1235           canvas.top = data.top;
1236         }
1237 
1238         if (isNumber(data.width)) {
1239           canvas.width = data.width;
1240           canvas.height = data.width / aspectRatio;
1241         } else if (isNumber(data.height)) {
1242           canvas.height = data.height;
1243           canvas.width = data.height * aspectRatio;
1244         }
1245 
1246         this.renderCanvas(true);
1247       }
1248     },
1249 
1250     getCropBoxData: function () {
1251       var cropBox = this.cropBox,
1252           data;
1253 
1254       if (this.built && this.cropped) {
1255         data = {
1256           left: cropBox.left,
1257           top: cropBox.top,
1258           width: cropBox.width,
1259           height: cropBox.height
1260         };
1261       }
1262 
1263       return data || {};
1264     },
1265 
1266     setCropBoxData: function (data) {
1267       var cropBox = this.cropBox,
1268           aspectRatio = this.options.aspectRatio;
1269 
1270       if (this.built && this.cropped && !this.disabled && $.isPlainObject(data)) {
1271 
1272         if (isNumber(data.left)) {
1273           cropBox.left = data.left;
1274         }
1275 
1276         if (isNumber(data.top)) {
1277           cropBox.top = data.top;
1278         }
1279 
1280         if (aspectRatio) {
1281           if (isNumber(data.width)) {
1282             cropBox.width = data.width;
1283             cropBox.height = cropBox.width / aspectRatio;
1284           } else if (isNumber(data.height)) {
1285             cropBox.height = data.height;
1286             cropBox.width = cropBox.height * aspectRatio;
1287           }
1288         } else {
1289           if (isNumber(data.width)) {
1290             cropBox.width = data.width;
1291           }
1292 
1293           if (isNumber(data.height)) {
1294             cropBox.height = data.height;
1295           }
1296         }
1297 
1298         this.renderCropBox();
1299       }
1300     },
1301 
1302     getCroppedCanvas: function (options) {
1303       var originalWidth,
1304           originalHeight,
1305           canvasWidth,
1306           canvasHeight,
1307           scaledWidth,
1308           scaledHeight,
1309           scaledRatio,
1310           aspectRatio,
1311           canvas,
1312           context,
1313           data;
1314 
1315       if (!this.built || !this.cropped || !SUPPORT_CANVAS) {
1316         return;
1317       }
1318 
1319       if (!$.isPlainObject(options)) {
1320         options = {};
1321       }
1322 
1323       data = this.getData();
1324       originalWidth = data.width;
1325       originalHeight = data.height;
1326       aspectRatio = originalWidth / originalHeight;
1327 
1328       if ($.isPlainObject(options)) {
1329         scaledWidth = options.width;
1330         scaledHeight = options.height;
1331 
1332         if (scaledWidth) {
1333           scaledHeight = scaledWidth / aspectRatio;
1334           scaledRatio = scaledWidth / originalWidth;
1335         } else if (scaledHeight) {
1336           scaledWidth = scaledHeight * aspectRatio;
1337           scaledRatio = scaledHeight / originalHeight;
1338         }
1339       }
1340 
1341       canvasWidth = scaledWidth || originalWidth;
1342       canvasHeight = scaledHeight || originalHeight;
1343 
1344       canvas = $('<canvas>')[0];
1345       canvas.width = canvasWidth;
1346       canvas.height = canvasHeight;
1347       context = canvas.getContext('2d');
1348 
1349       if (options.fillColor) {
1350         context.fillStyle = options.fillColor;
1351         context.fillRect(0, 0, canvasWidth, canvasHeight);
1352       }
1353 
1354       // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
1355       context.drawImage.apply(context, (function () {
1356         var source = getSourceCanvas(this.$clone[0], this.image),
1357             sourceWidth = source.width,
1358             sourceHeight = source.height,
1359             args = [source],
1360             srcX = data.x, // source canvas
1361             srcY = data.y,
1362             srcWidth,
1363             srcHeight,
1364             dstX, // destination canvas
1365             dstY,
1366             dstWidth,
1367             dstHeight;
1368 
1369         if (srcX <= -originalWidth || srcX > sourceWidth) {
1370           srcX = srcWidth = dstX = dstWidth = 0;
1371         } else if (srcX <= 0) {
1372           dstX = -srcX;
1373           srcX = 0;
1374           srcWidth = dstWidth = min(sourceWidth, originalWidth + srcX);
1375         } else if (srcX <= sourceWidth) {
1376           dstX = 0;
1377           srcWidth = dstWidth = min(originalWidth, sourceWidth - srcX);
1378         }
1379 
1380         if (srcWidth <= 0 || srcY <= -originalHeight || srcY > sourceHeight) {
1381           srcY = srcHeight = dstY = dstHeight = 0;
1382         } else if (srcY <= 0) {
1383           dstY = -srcY;
1384           srcY = 0;
1385           srcHeight = dstHeight = min(sourceHeight, originalHeight + srcY);
1386         } else if (srcY <= sourceHeight) {
1387           dstY = 0;
1388           srcHeight = dstHeight = min(originalHeight, sourceHeight - srcY);
1389         }
1390 
1391         args.push(srcX, srcY, srcWidth, srcHeight);
1392 
1393         // Scale destination sizes
1394         if (scaledRatio) {
1395           dstX *= scaledRatio;
1396           dstY *= scaledRatio;
1397           dstWidth *= scaledRatio;
1398           dstHeight *= scaledRatio;
1399         }
1400 
1401         // Avoid "IndexSizeError" in IE and Firefox
1402         if (dstWidth > 0 && dstHeight > 0) {
1403           args.push(dstX, dstY, dstWidth, dstHeight);
1404         }
1405 
1406         return args;
1407       }).call(this));
1408 
1409       return canvas;
1410     },
1411 
1412     setAspectRatio: function (aspectRatio) {
1413       var options = this.options;
1414 
1415       if (!this.disabled && !isUndefined(aspectRatio)) {
1416         options.aspectRatio = num(aspectRatio) || NaN; // 0 -> NaN
1417 
1418         if (this.built) {
1419           this.initCropBox();
1420 
1421           if (this.cropped) {
1422             this.renderCropBox();
1423           }
1424         }
1425       }
1426     },
1427 
1428     setDragMode: function (mode) {
1429       var $dragBox = this.$dragBox,
1430           cropable = false,
1431           movable = false;
1432 
1433       if (!this.ready || this.disabled) {
1434         return;
1435       }
1436 
1437       switch (mode) {
1438         case 'crop':
1439           if (this.options.dragCrop) {
1440             cropable = true;
1441             $dragBox.data('drag', mode);
1442           } else {
1443             movable = true;
1444           }
1445 
1446           break;
1447 
1448         case 'move':
1449           movable = true;
1450           $dragBox.data('drag', mode);
1451 
1452           break;
1453 
1454         default:
1455           $dragBox.removeData('drag');
1456       }
1457 
1458       $dragBox.toggleClass(CLASS_CROP, cropable).toggleClass(CLASS_MOVE, movable);
1459     }
1460   });
1461 
1462   prototype.change = function () {
1463     var dragType = this.dragType,
1464         options = this.options,
1465         canvas = this.canvas,
1466         container = this.container,
1467         cropBox = this.cropBox,
1468         width = cropBox.width,
1469         height = cropBox.height,
1470         left = cropBox.left,
1471         top = cropBox.top,
1472         right = left + width,
1473         bottom = top + height,
1474         minLeft = 0,
1475         minTop = 0,
1476         maxWidth = container.width,
1477         maxHeight = container.height,
1478         renderable = true,
1479         aspectRatio = options.aspectRatio,
1480         range = {
1481           x: this.endX - this.startX,
1482           y: this.endY - this.startY
1483         },
1484         offset;
1485 
1486     if (options.strict) {
1487       minLeft = cropBox.minLeft;
1488       minTop = cropBox.minTop;
1489       maxWidth = minLeft + min(container.width, canvas.width);
1490       maxHeight = minTop + min(container.height, canvas.height);
1491     }
1492 
1493     if (aspectRatio) {
1494       range.X = range.y * aspectRatio;
1495       range.Y = range.x / aspectRatio;
1496     }
1497 
1498     switch (dragType) {
1499       // Move cropBox
1500       case 'all':
1501         left += range.x;
1502         top += range.y;
1503         break;
1504 
1505       // Resize cropBox
1506       case 'e':
1507         if (range.x >= 0 && (right >= maxWidth || aspectRatio && (top <= minTop || bottom >= maxHeight))) {
1508           renderable = false;
1509           break;
1510         }
1511 
1512         width += range.x;
1513 
1514         if (aspectRatio) {
1515           height = width / aspectRatio;
1516           top -= range.Y / 2;
1517         }
1518 
1519         if (width < 0) {
1520           dragType = 'w';
1521           width = 0;
1522         }
1523 
1524         break;
1525 
1526       case 'n':
1527         if (range.y <= 0 && (top <= minTop || aspectRatio && (left <= minLeft || right >= maxWidth))) {
1528           renderable = false;
1529           break;
1530         }
1531 
1532         height -= range.y;
1533         top += range.y;
1534 
1535         if (aspectRatio) {
1536           width = height * aspectRatio;
1537           left += range.X / 2;
1538         }
1539 
1540         if (height < 0) {
1541           dragType = 's';
1542           height = 0;
1543         }
1544 
1545         break;
1546 
1547       case 'w':
1548         if (range.x <= 0 && (left <= minLeft || aspectRatio && (top <= minTop || bottom >= maxHeight))) {
1549           renderable = false;
1550           break;
1551         }
1552 
1553         width -= range.x;
1554         left += range.x;
1555 
1556         if (aspectRatio) {
1557           height = width / aspectRatio;
1558           top += range.Y / 2;
1559         }
1560 
1561         if (width < 0) {
1562           dragType = 'e';
1563           width = 0;
1564         }
1565 
1566         break;
1567 
1568       case 's':
1569         if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && (left <= minLeft || right >= maxWidth))) {
1570           renderable = false;
1571           break;
1572         }
1573 
1574         height += range.y;
1575 
1576         if (aspectRatio) {
1577           width = height * aspectRatio;
1578           left -= range.X / 2;
1579         }
1580 
1581         if (height < 0) {
1582           dragType = 'n';
1583           height = 0;
1584         }
1585 
1586         break;
1587 
1588       case 'ne':
1589         if (aspectRatio) {
1590           if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {
1591             renderable = false;
1592             break;
1593           }
1594 
1595           height -= range.y;
1596           top += range.y;
1597           width = height * aspectRatio;
1598         } else {
1599           if (range.x >= 0) {
1600             if (right < maxWidth) {
1601               width += range.x;
1602             } else if (range.y <= 0 && top <= minTop) {
1603               renderable = false;
1604             }
1605           } else {
1606             width += range.x;
1607           }
1608 
1609           if (range.y <= 0) {
1610             if (top > 0) {
1611               height -= range.y;
1612               top += range.y;
1613             }
1614           } else {
1615             height -= range.y;
1616             top += range.y;
1617           }
1618         }
1619 
1620         if (width < 0 && height < 0) {
1621           dragType = 'sw';
1622           height = 0;
1623           width = 0;
1624         } else if (width < 0) {
1625           dragType = 'nw';
1626           width = 0;
1627         } else if (height < 0) {
1628           dragType = 'se';
1629           height = 0;
1630         }
1631 
1632         break;
1633 
1634       case 'nw':
1635         if (aspectRatio) {
1636           if (range.y <= 0 && (top <= minTop || left <= minLeft)) {
1637             renderable = false;
1638             break;
1639           }
1640 
1641           height -= range.y;
1642           top += range.y;
1643           width = height * aspectRatio;
1644           left += range.X;
1645         } else {
1646           if (range.x <= 0) {
1647             if (left > 0) {
1648               width -= range.x;
1649               left += range.x;
1650             } else if (range.y <= 0 && top <= minTop) {
1651               renderable = false;
1652             }
1653           } else {
1654             width -= range.x;
1655             left += range.x;
1656           }
1657 
1658           if (range.y <= 0) {
1659             if (top > 0) {
1660               height -= range.y;
1661               top += range.y;
1662             }
1663           } else {
1664             height -= range.y;
1665             top += range.y;
1666           }
1667         }
1668 
1669         if (width < 0 && height < 0) {
1670           dragType = 'se';
1671           height = 0;
1672           width = 0;
1673         } else if (width < 0) {
1674           dragType = 'ne';
1675           width = 0;
1676         } else if (height < 0) {
1677           dragType = 'sw';
1678           height = 0;
1679         }
1680 
1681         break;
1682 
1683       case 'sw':
1684         if (aspectRatio) {
1685           if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {
1686             renderable = false;
1687             break;
1688           }
1689 
1690           width -= range.x;
1691           left += range.x;
1692           height = width / aspectRatio;
1693         } else {
1694           if (range.x <= 0) {
1695             if (left > 0) {
1696               width -= range.x;
1697               left += range.x;
1698             } else if (range.y >= 0 && bottom >= maxHeight) {
1699               renderable = false;
1700             }
1701           } else {
1702             width -= range.x;
1703             left += range.x;
1704           }
1705 
1706           if (range.y >= 0) {
1707             if (bottom < maxHeight) {
1708               height += range.y;
1709             }
1710           } else {
1711             height += range.y;
1712           }
1713         }
1714 
1715         if (width < 0 && height < 0) {
1716           dragType = 'ne';
1717           height = 0;
1718           width = 0;
1719         } else if (width < 0) {
1720           dragType = 'se';
1721           width = 0;
1722         } else if (height < 0) {
1723           dragType = 'nw';
1724           height = 0;
1725         }
1726 
1727         break;
1728 
1729       case 'se':
1730         if (aspectRatio) {
1731           if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
1732             renderable = false;
1733             break;
1734           }
1735 
1736           width += range.x;
1737           height = width / aspectRatio;
1738         } else {
1739           if (range.x >= 0) {
1740             if (right < maxWidth) {
1741               width += range.x;
1742             } else if (range.y >= 0 && bottom >= maxHeight) {
1743               renderable = false;
1744             }
1745           } else {
1746             width += range.x;
1747           }
1748 
1749           if (range.y >= 0) {
1750             if (bottom < maxHeight) {
1751               height += range.y;
1752             }
1753           } else {
1754             height += range.y;
1755           }
1756         }
1757 
1758         if (width < 0 && height < 0) {
1759           dragType = 'nw';
1760           height = 0;
1761           width = 0;
1762         } else if (width < 0) {
1763           dragType = 'sw';
1764           width = 0;
1765         } else if (height < 0) {
1766           dragType = 'ne';
1767           height = 0;
1768         }
1769 
1770         break;
1771 
1772       // Move image
1773       case 'move':
1774         canvas.left += range.x;
1775         canvas.top += range.y;
1776         this.renderCanvas(true);
1777         renderable = false;
1778         break;
1779 
1780       // Scale image
1781       case 'zoom':
1782         this.zoom(function (x1, y1, x2, y2) {
1783           var z1 = sqrt(x1 * x1 + y1 * y1),
1784               z2 = sqrt(x2 * x2 + y2 * y2);
1785 
1786           return (z2 - z1) / z1;
1787         }(
1788           abs(this.startX - this.startX2),
1789           abs(this.startY - this.startY2),
1790           abs(this.endX - this.endX2),
1791           abs(this.endY - this.endY2)
1792         ));
1793 
1794         this.startX2 = this.endX2;
1795         this.startY2 = this.endY2;
1796         renderable = false;
1797         break;
1798 
1799       // Crop image
1800       case 'crop':
1801         if (range.x && range.y) {
1802           offset = this.$cropper.offset();
1803           left = this.startX - offset.left;
1804           top = this.startY - offset.top;
1805           width = cropBox.minWidth;
1806           height = cropBox.minHeight;
1807 
1808           if (range.x > 0) {
1809             if (range.y > 0) {
1810               dragType = 'se';
1811             } else {
1812               dragType = 'ne';
1813               top -= height;
1814             }
1815           } else {
1816             if (range.y > 0) {
1817               dragType = 'sw';
1818               left -= width;
1819             } else {
1820               dragType = 'nw';
1821               left -= width;
1822               top -= height;
1823             }
1824           }
1825 
1826           // Show the cropBox if is hidden
1827           if (!this.cropped) {
1828             this.cropped = true;
1829             this.$cropBox.removeClass(CLASS_HIDDEN);
1830           }
1831         }
1832 
1833         break;
1834 
1835       // No default
1836     }
1837 
1838     if (renderable) {
1839       cropBox.width = width;
1840       cropBox.height = height;
1841       cropBox.left = left;
1842       cropBox.top = top;
1843       this.dragType = dragType;
1844 
1845       this.renderCropBox();
1846     }
1847 
1848     // Override
1849     this.startX = this.endX;
1850     this.startY = this.endY;
1851   };
1852 
1853   $.extend(Cropper.prototype, prototype);
1854 
1855   Cropper.DEFAULTS = {
1856     // Defines the aspect ratio of the crop box
1857     // Type: Number
1858     aspectRatio: NaN,
1859 
1860     // Defines the percentage of automatic cropping area when initializes
1861     // Type: Number (Must large than 0 and less than 1)
1862     autoCropArea: 0.8, // 80%
1863 
1864     // Outputs the cropping results.
1865     // Type: Function
1866     crop: null,
1867 
1868     // Add extra containers for previewing
1869     // Type: String (jQuery selector)
1870     preview: '',
1871 
1872     // Toggles
1873     strict: true, // strict mode, the image cannot zoom out less than the container
1874     responsive: true, // Rebuild when resize the window
1875     checkImageOrigin: true, // Check if the target image is cross origin
1876 
1877     modal: true, // Show the black modal
1878     guides: true, // Show the dashed lines for guiding
1879     highlight: true, // Show the white modal to highlight the crop box
1880     background: true, // Show the grid background
1881 
1882     autoCrop: true, // Enable to crop the image automatically when initialize
1883     dragCrop: true, // Enable to create new crop box by dragging over the image
1884     movable: true, // Enable to move the crop box
1885     resizable: true, // Enable to resize the crop box
1886     rotatable: true, // Enable to rotate the image
1887     zoomable: true, // Enable to zoom the image
1888     touchDragZoom: true, // Enable to zoom the image by wheeling mouse
1889     mouseWheelZoom: true, // Enable to zoom the image by dragging touch
1890 
1891     // Dimensions
1892     minCanvasWidth: 0,
1893     minCanvasHeight: 0,
1894     minCropBoxWidth: 0,
1895     minCropBoxHeight: 0,
1896     minContainerWidth: 200,
1897     minContainerHeight: 100,
1898 
1899     // Events
1900     build: null, // Function
1901     built: null, // Function
1902     dragstart: null, // Function
1903     dragmove: null, // Function
1904     dragend: null, // Function
1905     zoomin: null, // Function
1906     zoomout: null // Function
1907   };
1908 
1909   Cropper.setDefaults = function (options) {
1910     $.extend(Cropper.DEFAULTS, options);
1911   };
1912 
1913   // Use the string compressor: Strmin (https://github.com/fengyuanchen/strmin)
1914   Cropper.TEMPLATE = (function (source, words) {
1915     words = words.split(',');
1916     return source.replace(/\d+/g, function (i) {
1917       return words[i];
1918     });
1919   })('<0 6="5-container"><0 6="5-canvas"></0><0 6="5-2-9" 3-2="move"></0><0 6="5-crop-9"><1 6="5-view-9"></1><1 6="5-8 8-h"></1><1 6="5-8 8-v"></1><1 6="5-face" 3-2="all"></1><1 6="5-7 7-e" 3-2="e"></1><1 6="5-7 7-n" 3-2="n"></1><1 6="5-7 7-w" 3-2="w"></1><1 6="5-7 7-s" 3-2="s"></1><1 6="5-4 4-e" 3-2="e"></1><1 6="5-4 4-n" 3-2="n"></1><1 6="5-4 4-w" 3-2="w"></1><1 6="5-4 4-s" 3-2="s"></1><1 6="5-4 4-ne" 3-2="ne"></1><1 6="5-4 4-nw" 3-2="nw"></1><1 6="5-4 4-sw" 3-2="sw"></1><1 6="5-4 4-se" 3-2="se"></1></0></0>', 'div,span,drag,data,point,cropper,class,line,dashed,box');
1920 
1921   /* Template source:
1922   <div class="cropper-container">
1923     <div class="cropper-canvas"></div>
1924     <div class="cropper-drag-box" data-drag="move"></div>
1925     <div class="cropper-crop-box">
1926       <span class="cropper-view-box"></span>
1927       <span class="cropper-dashed dashed-h"></span>
1928       <span class="cropper-dashed dashed-v"></span>
1929       <span class="cropper-face" data-drag="all"></span>
1930       <span class="cropper-line line-e" data-drag="e"></span>
1931       <span class="cropper-line line-n" data-drag="n"></span>
1932       <span class="cropper-line line-w" data-drag="w"></span>
1933       <span class="cropper-line line-s" data-drag="s"></span>
1934       <span class="cropper-point point-e" data-drag="e"></span>
1935       <span class="cropper-point point-n" data-drag="n"></span>
1936       <span class="cropper-point point-w" data-drag="w"></span>
1937       <span class="cropper-point point-s" data-drag="s"></span>
1938       <span class="cropper-point point-ne" data-drag="ne"></span>
1939       <span class="cropper-point point-nw" data-drag="nw"></span>
1940       <span class="cropper-point point-sw" data-drag="sw"></span>
1941       <span class="cropper-point point-se" data-drag="se"></span>
1942     </div>
1943   </div>
1944   */
1945 
1946   // Save the other cropper
1947   Cropper.other = $.fn.cropper;
1948 
1949   // Register as jQuery plugin
1950   $.fn.cropper = function (options) {
1951     var args = toArray(arguments, 1),
1952         result;
1953 
1954     this.each(function () {
1955       var $this = $(this),
1956           data = $this.data('cropper'),
1957           fn;
1958 
1959       if (!data) {
1960         $this.data('cropper', (data = new Cropper(this, options)));
1961       }
1962 
1963       if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
1964         result = fn.apply(data, args);
1965       }
1966     });
1967 
1968     return isUndefined(result) ? this : result;
1969   };
1970 
1971   $.fn.cropper.Constructor = Cropper;
1972   $.fn.cropper.setDefaults = Cropper.setDefaults;
1973 
1974   // No conflict
1975   $.fn.cropper.noConflict = function () {
1976     $.fn.cropper = Cropper.other;
1977     return this;
1978   };
1979 
1980 });

 

核心的js:(js里面,最主要的一个方法)upload-main.js

  1 $(function () {
  2 
  3   'use strict';
  4 
  5   var console = window.console || { log: function () {} },
  6       $alert = $('.docs-alert'),
  7       $message = $alert.find('.message'),
  8       showMessage = function (message, type) {
  9         $message.text(message);
 10 
 11         if (type) {
 12           $message.addClass(type);
 13         }
 14 
 15         $alert.fadeIn();
 16 
 17         setTimeout(function () {
 18           $alert.fadeOut();
 19         }, 3000);
 20       };
 21 
 22   // Demo
 23   // -------------------------------------------------------------------------
 24 
 25   (function () {
 26     var $image = $('.img-container > img'),
 27         $dataX = $('#dataX'),
 28         $dataY = $('#dataY'),
 29         $dataHeight = $('#dataHeight'),
 30         $dataWidth = $('#dataWidth'),
 31         $dataRotate = $('#dataRotate'),
 32         options = {
 33           
 34 
 35           aspectRatio: 1 / 1,
 36           preview: '.img-preview',
 37           crop: function (data) {
 38             $dataX.val(Math.round(data.x));
 39             $dataY.val(Math.round(data.y));
 40             $dataHeight.val(Math.round(data.height));
 41             $dataWidth.val(Math.round(data.width));
 42             $dataRotate.val(Math.round(data.rotate));
 43           }
 44         };
 45 
 46     $image.on({
 47       'build.cropper': function (e) {
 48         console.log(e.type);
 49       },
 50       'built.cropper': function (e) {
 51         console.log(e.type);
 52       },
 53       'dragstart.cropper': function (e) {
 54         console.log(e.type, e.dragType);
 55       },
 56       'dragmove.cropper': function (e) {
 57         console.log(e.type, e.dragType);
 58       },
 59       'dragend.cropper': function (e) {
 60         console.log(e.type, e.dragType);
 61       },
 62       'zoomin.cropper': function (e) {
 63         console.log(e.type);
 64       },
 65       'zoomout.cropper': function (e) {
 66         console.log(e.type);
 67       }
 68     }).cropper(options);
 69 
 70 
 71     // Methods
 72     $(document.body).on('click', '[data-method]', function () {//可以在这里处理对选择的文件进行判断,
 73       var data = $(this).data(),
 74           $target,
 75           result;
 76 
 77       if (data.method) {
 78         data = $.extend({}, data); // Clone a new one
 79 
 80         if (typeof data.target !== 'undefined') {
 81           $target = $(data.target);
 82 
 83           if (typeof data.option === 'undefined') {
 84             try {
 85               data.option = JSON.parse($target.val());
 86             } catch (e) {
 87               console.log(e.message);
 88             }
 89           }
 90         }
 91 
 92         result = $image.cropper(data.method, data.option);
 93         $("#base64ImgData").val(result.toDataURL('image/jpeg'));//这里可以保存你将上传的base64格式的图片(核心的方法toDataURL,还有,这个方法静态页面调用会保存,需要放到服务器上面运行)
 94         if (data.method === 'getCroppedCanvas') {
 95           $('.img-preview img').attr('src', result.toDataURL('image/jpeg'));
 96         }
 97 
 98         if ($.isPlainObject(result) && $target) {
 99           try {
100             $target.val(JSON.stringify(result));
101           } catch (e) {
102             console.log(e.message);
103           }
104         }
105 
106       }//这后面是我处理的逻辑
107       $('.step2').hide();
108       $('.modal').show();
109       $.DialogByZ.Loading('../images/wechat/loading.png');
110       $.ajax({
111         cache:false,
112         type:"post",
113         data:"base64="+result.toDataURL('image/jpeg')+"&filePath=user",
114         url:"wechatPersonalCenterAction_updateHeadPortraitByBase64.do",
115         success:function(data, textStatus){
116             debugger;
117             if(data != '0'){
118                 $('.modal').hide();
119                 $('.zbox-uploading').hide();
120                 $.DialogByZ.Autofade({Content: "上传成功!"});
121                 $(".head-img").attr("style", "background-image:url('http://m.uecun.com/uecun/attachfiles/user/"+$("#universalid").val()+"/"+data+"');");
122                 
123             }
124         },
125         error:function(XMLHttpRequest, textStatus, errorThrown){}
126     });
127     }).on('keydown', function (e) {
128 
129       switch (e.which) {
130         case 37:
131           e.preventDefault();
132           $image.cropper('move', -1, 0);
133           break;
134 
135         case 38:
136           e.preventDefault();
137           $image.cropper('move', 0, -1);
138           break;
139 
140         case 39:
141           e.preventDefault();
142           $image.cropper('move', 1, 0);
143           break;
144 
145         case 40:
146           e.preventDefault();
147           $image.cropper('move', 0, 1);
148           break;
149       }
150 
151     });
152 
153 
154     // Import image
155     var $inputImage = $('#inputImage'),
156         URL = window.URL || window.webkitURL,
157         blobURL;
158 
159 
160 
161     if (URL) {
162       $inputImage.change(function () {//或者这里可以对图片进行验证
163 
164         var docObj = document.getElementById("inputImage");
165 //            return;
166         if (typeof(docObj.files[0]) == "undefined") {
167             $.DialogByZ.Autofade({Content: "请选择要上传的图片"});
168             return false;
169         } else {
170             if (docObj.files[0].size>2560000) {
171                 $.DialogByZ.Autofade({Content: "上传文件大小不能大于2M"});
172                 return false;
173             }
174             var name = docObj.value;
175             var fileName = name.substring(name.lastIndexOf(".")+1).toLowerCase();
176             if (fileName!="jpg" && fileName!="png" && fileName!="gif") {
177                 $.DialogByZ.Autofade({Content: "上传文件格式不对"});
178                 return false;
179             }
180         }
181         var files = this.files,
182             file;
183         console.info(URL,files);
184 
185         if (files && files.length) {
186           file = files[0];
187           $('.step2').show();
188 //          $('.step2').fadeIn(500);
189           if (/^image\/\w+$/.test(file.type)) {
190             blobURL = URL.createObjectURL(file);
191             $image.one('built.cropper', function () {
192               URL.revokeObjectURL(blobURL); // Revoke when load complete
193             }).cropper('reset', true).cropper('replace', blobURL);
194             $inputImage.val('');
195           } else {
196             showMessage('Please choose an image file.');
197           }
198 
199         }
200       });
201     } else {
202       $inputImage.parent().remove();
203     }
204 
205 
206     // Options
207     $('.docs-options :checkbox').on('change', function () {
208       var $this = $(this);
209 
210       options[$this.val()] = $this.prop('checked');
211       $image.cropper('destroy').cropper(options);
212     });
213 
214 
215     // Tooltips
216     //$('[data-toggle="tooltip"]').tooltip();
217 
218   }());
219 
220 });

java后台代码:

 1 /**
 2      * 用户h5头像上传
 3      */
 4     public void updateHeadPortraitByBase64(){
 5         try {
 6             response.setCharacterEncoding("utf-8");
 7             String iconBase64 = request.getParameter("base64");//需要把 data:image/jpeg(png具体看图片的类型);base64, 去掉,需要截取后面的图片数据(不截取这段字符串会导致图片无法生成)
 8             if(StringUtils.isBlank(iconBase64)){
 9                 response.getWriter().print("0");
10             }
11             iconBase64 = iconBase64.split(",")[1].replaceAll(" ", "+");//由于请求的过程中,会把+好替换成空字符串,如果不替换+好,生成的图片会出现乱码
12             User user = SessionUtil.getSysUserFormSession(request);
13             File file = new File(IMG_PATH+"/"+model.getFilePath()+"/"+user.getUniversalid());
14             if (!file.exists() && !file.isDirectory()) {
15                 file.mkdirs();
16             }
17             String l = System.currentTimeMillis()+".jpg";
18             System.out.println("修改头像:               "+l);
19             byte[] buffer = new BASE64Decoder().decodeBuffer(iconBase64);//把base64的图片以二进制的形式读取到字节数组,然后在写入某个文件或者文件夹
20             FileOutputStream out = new FileOutputStream(IMG_PATH+"/"+model.getFilePath()+"/"+user.getUniversalid()+"/"+l);
21             out.write(buffer);
22             out.close();
23             int update = userService.updateUserHeadPortrait(l+"",user.getUniversalid());
24             if(update <= 0){
25                 response.getWriter().print("0");
26             } else {
27                 user = userService.findUserDetailById(user.getUniversalid().toString());
28                 SessionUtil.setSysUserToSession(request, user);
29                 response.getWriter().print(l);
30             }
31 //            uploadPhotoService.saveUploadPhoto(l, model.getFileDataId(), model.getInputPicFileFileName(), "1", model.getTableName());
32         } catch (Exception e) {
33             e.printStackTrace();
34         }
35     
36     }

好了,这个玩意我也是第一次玩,前端也是第一次玩,搞了一天的时间,顿时感觉各种丢脸,有好的方法希望大神推荐,欢迎各种搬砖!谢谢~~~

 

 posted on 2017-05-25 17:46  _萨瓦迪卡  阅读(2742)  评论(0编辑  收藏  举报