公司发布微信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 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, 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 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 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 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 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 canvas.width, 568 height: canvas.height, 569 degree: image.rotate, 570 aspectRatio: image.aspectRatio 571 }, true); 572 } 573 574 $.extend(image, reversed ? { 575 reversed.width, 576 height: reversed.height, 577 left: (canvas.width - reversed.width) / 2, 578 top: (canvas.height - reversed.height) / 2 579 } : { 580 canvas.width, 581 height: canvas.height, 582 left: 0, 583 top: 0 584 }); 585 586 this.$clone.css({ 587 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 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 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 $this.width(), 750 height: $this.height(), 751 original: $this.html() 752 }).html('<img src="' + url + '" style="display:block;100%;min-0!important;min-height:0!important;max-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, 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 * 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 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 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 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 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 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 }
好了,这个玩意我也是第一次玩,前端也是第一次玩,搞了一天的时间,顿时感觉各种丢脸,有好的方法希望大神推荐,欢迎各种搬砖!谢谢~~~