http://www.htmleaf.com/ziliaoku/qianduanjiaocheng/201502151385.html

jQuery 缩放 旋转 裁剪图片 Image Cropper

A simple jQuery image cropping plugin.

https://fengyuanchen.github.io/cropper

涂鸦效果

https://github.com/Leimi/drawingboard.js#drawingboardjs

全局定义,从而实现涂鸦的undo,redo,reset

  1. imageBoard = window.imageBoard;
  2. $(".picLabel").click(function() {
  3. $("#firstStep").hide();
  4. $("#zbeubeu").show();
  5. $("#drawing-board").css("width",$(".cropper-canvas img").width());
  6. $("#drawing-board").css("height",$(".cropper-canvas img").height());
  7.  
  8. imageBoard = new DrawingBoard.Board('drawing-board', {
  9. background: "http://test.file.tiplus.cn/task/16443/1452588926.598544.jpg?timestamp=1481096992278",
  10. color: "#d00",
  11. size: 3,
  12. controls: false,
  13. //controls: [
  14. //'Navigation',
  15. //],
  16. webStorage:false,
  17. //webStorage: 'session',
  18. enlargeYourContainer: true,
  19. stretchImg: true,
  20. droppable: true,
  21. //controlsPosition:"bottom center"
  22. });
  23. $(".cropper-container").hide();
  24. $(".drawing-board-canvas-wrapper").css("width", $(".cropper-canvas").css("witdh"));
  25. $(".drawing-board-canvas-wrapper").css("height", $(".cropper-canvas").css("height"));
  26. $(".drawing-board-canvas-wrapper").css("left", $(".cropper-canvas").css("left"));
  27. $(".drawing-board-canvas-wrapper").css("top", $(".cropper-canvas").css("top"));
  28.  
  29. $("#secondStep").show();
  30.  
  31. })
  32. $("#undo").click(function(){
  33. //imageBoard.downloadImg();
  34. imageBoard.goBackInHistory();
  35. })
  36. $("#redo").click(function(){
  37. //imageBoard.downloadImg();
  38. imageBoard.goForthInHistory();
  39. })
  40. $(".destroy").click(function(){
  41. //imageBoard.downloadImg();
  42. imageBoard.reset({ background: true });
  43. })

可以实现图片放大缩小,可以在途中增加文字:

https://github.com/lamida/canvas-draw-undo

http://jsfiddle.net/epistemex/95r85zh4/

http://phrogz.net/tmp/canvas_zoom_to_cursor.html

图片放大缩小拖放

通过cropper完成canvas的旋转、放大或者缩小,修改cropper使得drawingboardjs也同步修改

  1. var $image = $('#image');
  2. $image.cropper({
  3. aspectRatio: 16 / 9,
  4. movable: true,
  5. dragMode:'move',
  6. crop: function(e) {
  7. // Output the result data for cropping image.
  8. },
  9. built: function () {
  10. croppable = true;
  11. }
  12. });
  13. setTimeout(function(){
  14. clearCanvas();
  15. }, 1000);
  16. $(".rotate").click(function(){
  17. var croppedCanvas;
  18. if (!croppable) {
  19. return;
  20. }
  21. croppedCanvas = $image.cropper('rotate', -90);
  22. $(".cropper-canvas img").css("display","none");
  23. });
  24. $(".enlarge_pic").click(function(){
  25. var croppedCanvas;
  26. if (!croppable) {
  27. return;
  28. }
  29. croppedCanvas = $image.cropper('zoom', 0.1);
  30. $(".cropper-canvas img").css("display","none");
  31. });
  32. $(".shrink_pic").click(function(){
  33. var croppedCanvas;
  34. if (!croppable) {
  35. return;
  36. }
  37. croppedCanvas = $image.cropper('zoom', -0.1);
  38. $(".cropper-canvas img").css("display","none");
  39. });

修改后的cropper文件:

  1. /*!
  2. * Cropper v2.3.4
  3. * https://github.com/fengyuanchen/cropper
  4. *
  5. * Copyright (c) 2014-2016 Fengyuan Chen and contributors
  6. * Released under the MIT license
  7. *
  8. * Date: 2016-09-03T05:50:45.412Z
  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. // Globals
  27. var $window = $(window);
  28. var $document = $(document);
  29. var location = window.location;
  30. var navigator = window.navigator;
  31. var ArrayBuffer = window.ArrayBuffer;
  32. var Uint8Array = window.Uint8Array;
  33. var DataView = window.DataView;
  34. var btoa = window.btoa;
  35.  
  36. // Constants
  37. var NAMESPACE = 'cropper';
  38.  
  39. // Classes
  40. var CLASS_MODAL = 'cropper-modal';
  41. var CLASS_HIDE = 'cropper-hide';
  42. var CLASS_HIDDEN = 'cropper-hidden';
  43. var CLASS_INVISIBLE = 'cropper-invisible';
  44. var CLASS_MOVE = 'cropper-move';
  45. var CLASS_CROP = 'cropper-crop';
  46. var CLASS_DISABLED = 'cropper-disabled';
  47. var CLASS_BG = 'cropper-bg';
  48.  
  49. // Events
  50. var EVENT_MOUSE_DOWN = 'mousedown touchstart pointerdown MSPointerDown';
  51. var EVENT_MOUSE_MOVE = 'mousemove touchmove pointermove MSPointerMove';
  52. var EVENT_MOUSE_UP = 'mouseup touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel';
  53. var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll';
  54. var EVENT_DBLCLICK = 'dblclick';
  55. var EVENT_LOAD = 'load.' + NAMESPACE;
  56. var EVENT_ERROR = 'error.' + NAMESPACE;
  57. var EVENT_RESIZE = 'resize.' + NAMESPACE; // Bind to window with namespace
  58. var EVENT_BUILD = 'build.' + NAMESPACE;
  59. var EVENT_BUILT = 'built.' + NAMESPACE;
  60. var EVENT_CROP_START = 'cropstart.' + NAMESPACE;
  61. var EVENT_CROP_MOVE = 'cropmove.' + NAMESPACE;
  62. var EVENT_CROP_END = 'cropend.' + NAMESPACE;
  63. var EVENT_CROP = 'crop.' + NAMESPACE;
  64. var EVENT_ZOOM = 'zoom.' + NAMESPACE;
  65.  
  66. // RegExps
  67. var REGEXP_ACTIONS = /^(e|w|s|n|se|sw|ne|nw|all|crop|move|zoom)$/;
  68. var REGEXP_DATA_URL = /^data:/;
  69. var REGEXP_DATA_URL_HEAD = /^data:([^;]+);base64,/;
  70. var REGEXP_DATA_URL_JPEG = /^data:image\/jpeg.*;base64,/;
  71.  
  72. // Data keys
  73. var DATA_PREVIEW = 'preview';
  74. var DATA_ACTION = 'action';
  75.  
  76. // Actions
  77. var ACTION_EAST = 'e';
  78. var ACTION_WEST = 'w';
  79. var ACTION_SOUTH = 's';
  80. var ACTION_NORTH = 'n';
  81. var ACTION_SOUTH_EAST = 'se';
  82. var ACTION_SOUTH_WEST = 'sw';
  83. var ACTION_NORTH_EAST = 'ne';
  84. var ACTION_NORTH_WEST = 'nw';
  85. var ACTION_ALL = 'all';
  86. var ACTION_CROP = 'crop';
  87. var ACTION_MOVE = 'move';
  88. var ACTION_ZOOM = 'zoom';
  89. var ACTION_NONE = 'none';
  90.  
  91. // Supports
  92. var SUPPORT_CANVAS = $.isFunction($('<canvas>')[0].getContext);
  93. var IS_SAFARI_OR_UIWEBVIEW = navigator && /(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i.test(navigator.userAgent);
  94.  
  95. // Maths
  96. var num = Number;
  97. var min = Math.min;
  98. var max = Math.max;
  99. var abs = Math.abs;
  100. var sin = Math.sin;
  101. var cos = Math.cos;
  102. var sqrt = Math.sqrt;
  103. var round = Math.round;
  104. var floor = Math.floor;
  105.  
  106. // Utilities
  107. var fromCharCode = String.fromCharCode;
  108.  
  109. function isNumber(n) {
  110. return typeof n === 'number' && !isNaN(n);
  111. }
  112.  
  113. function isUndefined(n) {
  114. return typeof n === 'undefined';
  115. }
  116.  
  117. function toArray(obj, offset) {
  118. var args = [];
  119.  
  120. // This is necessary for IE8
  121. if (isNumber(offset)) {
  122. args.push(offset);
  123. }
  124.  
  125. return args.slice.apply(obj, args);
  126. }
  127.  
  128. // Custom proxy to avoid jQuery's guid
  129. function proxy(fn, context) {
  130. var args = toArray(arguments, 2);
  131.  
  132. return function () {
  133. return fn.apply(context, args.concat(toArray(arguments)));
  134. };
  135. }
  136.  
  137. function isCrossOriginURL(url) {
  138. var parts = url.match(/^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i);
  139.  
  140. return parts && (
  141. parts[1] !== location.protocol ||
  142. parts[2] !== location.hostname ||
  143. parts[3] !== location.port
  144. );
  145. }
  146.  
  147. function addTimestamp(url) {
  148. var timestamp = 'timestamp=' + (new Date()).getTime();
  149.  
  150. return (url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp);
  151. }
  152.  
  153. function getCrossOrigin(crossOrigin) {
  154. return crossOrigin ? ' crossOrigin="' + crossOrigin + '"' : '';
  155. }
  156.  
  157. function getImageSize(image, callback) {
  158. var newImage;
  159.  
  160. // Modern browsers (ignore Safari, #120 & #509)
  161. if (image.naturalWidth && !IS_SAFARI_OR_UIWEBVIEW) {
  162. return callback(image.naturalWidth, image.naturalHeight);
  163. }
  164.  
  165. // IE8: Don't use `new Image()` here (#319)
  166. newImage = document.createElement('img');
  167.  
  168. newImage.onload = function () {
  169. callback(this.width, this.height);
  170. };
  171.  
  172. newImage.src = image.src;
  173. }
  174.  
  175. function getTransform(options) {
  176. var transforms = [];
  177. var rotate = options.rotate;
  178. var scaleX = options.scaleX;
  179. var scaleY = options.scaleY;
  180.  
  181. // Rotate should come first before scale to match orientation transform
  182. if (isNumber(rotate) && rotate !== 0) {
  183. transforms.push('rotate(' + rotate + 'deg)');
  184. }
  185.  
  186. if (isNumber(scaleX) && scaleX !== 1) {
  187. transforms.push('scaleX(' + scaleX + ')');
  188. }
  189.  
  190. if (isNumber(scaleY) && scaleY !== 1) {
  191. transforms.push('scaleY(' + scaleY + ')');
  192. }
  193.  
  194. return transforms.length ? transforms.join(' ') : 'none';
  195. }
  196.  
  197. function getRotatedSizes(data, isReversed) {
  198. var deg = abs(data.degree) % 180;
  199. var arc = (deg > 90 ? (180 - deg) : deg) * Math.PI / 180;
  200. var sinArc = sin(arc);
  201. var cosArc = cos(arc);
  202. var width = data.width;
  203. var height = data.height;
  204. var aspectRatio = data.aspectRatio;
  205. var newWidth;
  206. var newHeight;
  207.  
  208. if (!isReversed) {
  209. newWidth = width * cosArc + height * sinArc;
  210. newHeight = width * sinArc + height * cosArc;
  211. } else {
  212. newWidth = width / (cosArc + sinArc / aspectRatio);
  213. newHeight = newWidth / aspectRatio;
  214. }
  215.  
  216. return {
  217. width: newWidth,
  218. height: newHeight
  219. };
  220. }
  221.  
  222. function getSourceCanvas(image, data) {
  223. var canvas = $('<canvas>')[0];
  224. var context = canvas.getContext('2d');
  225. var dstX = 0;
  226. var dstY = 0;
  227. var dstWidth = data.naturalWidth;
  228. var dstHeight = data.naturalHeight;
  229. var rotate = data.rotate;
  230. var scaleX = data.scaleX;
  231. var scaleY = data.scaleY;
  232. var scalable = isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1);
  233. var rotatable = isNumber(rotate) && rotate !== 0;
  234. var advanced = rotatable || scalable;
  235. var canvasWidth = dstWidth * abs(scaleX || 1);
  236. var canvasHeight = dstHeight * abs(scaleY || 1);
  237. var translateX;
  238. var translateY;
  239. var rotated;
  240.  
  241. if (scalable) {
  242. translateX = canvasWidth / 2;
  243. translateY = canvasHeight / 2;
  244. }
  245.  
  246. if (rotatable) {
  247. rotated = getRotatedSizes({
  248. width: canvasWidth,
  249. height: canvasHeight,
  250. degree: rotate
  251. });
  252.  
  253. canvasWidth = rotated.width;
  254. canvasHeight = rotated.height;
  255. translateX = canvasWidth / 2;
  256. translateY = canvasHeight / 2;
  257. }
  258.  
  259. canvas.width = canvasWidth;
  260. canvas.height = canvasHeight;
  261.  
  262. if (advanced) {
  263. dstX = -dstWidth / 2;
  264. dstY = -dstHeight / 2;
  265.  
  266. context.save();
  267. context.translate(translateX, translateY);
  268. }
  269.  
  270. // Rotate should come first before scale as in the "getTransform" function
  271. if (rotatable) {
  272. context.rotate(rotate * Math.PI / 180);
  273. }
  274.  
  275. if (scalable) {
  276. context.scale(scaleX, scaleY);
  277. }
  278.  
  279. context.drawImage(image, floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight));
  280.  
  281. if (advanced) {
  282. context.restore();
  283. }
  284.  
  285. return canvas;
  286. }
  287.  
  288. function getTouchesCenter(touches) {
  289. var length = touches.length;
  290. var pageX = 0;
  291. var pageY = 0;
  292.  
  293. if (length) {
  294. $.each(touches, function (i, touch) {
  295. pageX += touch.pageX;
  296. pageY += touch.pageY;
  297. });
  298.  
  299. pageX /= length;
  300. pageY /= length;
  301. }
  302.  
  303. return {
  304. pageX: pageX,
  305. pageY: pageY
  306. };
  307. }
  308.  
  309. function getStringFromCharCode(dataView, start, length) {
  310. var str = '';
  311. var i;
  312.  
  313. for (i = start, length += start; i < length; i++) {
  314. str += fromCharCode(dataView.getUint8(i));
  315. }
  316.  
  317. return str;
  318. }
  319.  
  320. function getOrientation(arrayBuffer) {
  321. var dataView = new DataView(arrayBuffer);
  322. var length = dataView.byteLength;
  323. var orientation;
  324. var exifIDCode;
  325. var tiffOffset;
  326. var firstIFDOffset;
  327. var littleEndian;
  328. var endianness;
  329. var app1Start;
  330. var ifdStart;
  331. var offset;
  332. var i;
  333.  
  334. // Only handle JPEG image (start by 0xFFD8)
  335. if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) {
  336. offset = 2;
  337.  
  338. while (offset < length) {
  339. if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) {
  340. app1Start = offset;
  341. break;
  342. }
  343.  
  344. offset++;
  345. }
  346. }
  347.  
  348. if (app1Start) {
  349. exifIDCode = app1Start + 4;
  350. tiffOffset = app1Start + 10;
  351.  
  352. if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') {
  353. endianness = dataView.getUint16(tiffOffset);
  354. littleEndian = endianness === 0x4949;
  355.  
  356. if (littleEndian || endianness === 0x4D4D /* bigEndian */) {
  357. if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) {
  358. firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
  359.  
  360. if (firstIFDOffset >= 0x00000008) {
  361. ifdStart = tiffOffset + firstIFDOffset;
  362. }
  363. }
  364. }
  365. }
  366. }
  367.  
  368. if (ifdStart) {
  369. length = dataView.getUint16(ifdStart, littleEndian);
  370.  
  371. for (i = 0; i < length; i++) {
  372. offset = ifdStart + i * 12 + 2;
  373.  
  374. if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */) {
  375.  
  376. // 8 is the offset of the current tag's value
  377. offset += 8;
  378.  
  379. // Get the original orientation value
  380. orientation = dataView.getUint16(offset, littleEndian);
  381.  
  382. // Override the orientation with its default value for Safari (#120)
  383. if (IS_SAFARI_OR_UIWEBVIEW) {
  384. dataView.setUint16(offset, 1, littleEndian);
  385. }
  386.  
  387. break;
  388. }
  389. }
  390. }
  391.  
  392. return orientation;
  393. }
  394.  
  395. function dataURLToArrayBuffer(dataURL) {
  396. var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, '');
  397. var binary = atob(base64);
  398. var length = binary.length;
  399. var arrayBuffer = new ArrayBuffer(length);
  400. var dataView = new Uint8Array(arrayBuffer);
  401. var i;
  402.  
  403. for (i = 0; i < length; i++) {
  404. dataView[i] = binary.charCodeAt(i);
  405. }
  406.  
  407. return arrayBuffer;
  408. }
  409.  
  410. // Only available for JPEG image
  411. function arrayBufferToDataURL(arrayBuffer) {
  412. var dataView = new Uint8Array(arrayBuffer);
  413. var length = dataView.length;
  414. var base64 = '';
  415. var i;
  416.  
  417. for (i = 0; i < length; i++) {
  418. base64 += fromCharCode(dataView[i]);
  419. }
  420.  
  421. return 'data:image/jpeg;base64,' + btoa(base64);
  422. }
  423.  
  424. function Cropper(element, options) {
  425. this.$element = $(element);
  426. this.options = $.extend({}, Cropper.DEFAULTS, $.isPlainObject(options) && options);
  427. this.isLoaded = false;
  428. this.isBuilt = false;
  429. this.isCompleted = false;
  430. this.isRotated = false;
  431. this.isCropped = false;
  432. this.isDisabled = false;
  433. this.isReplaced = false;
  434. this.isLimited = false;
  435. this.wheeling = false;
  436. this.isImg = false;
  437. this.originalUrl = '';
  438. this.canvas = null;
  439. this.cropBox = null;
  440. this.init();
  441. }
  442.  
  443. Cropper.prototype = {
  444. constructor: Cropper,
  445.  
  446. init: function () {
  447. var $this = this.$element;
  448. var url;
  449.  
  450. if ($this.is('img')) {
  451. this.isImg = true;
  452.  
  453. // Should use `$.fn.attr` here. e.g.: "img/picture.jpg"
  454. this.originalUrl = url = $this.attr('src');
  455.  
  456. // Stop when it's a blank image
  457. if (!url) {
  458. return;
  459. }
  460.  
  461. // Should use `$.fn.prop` here. e.g.: "http://example.com/img/picture.jpg"
  462. url = $this.prop('src');
  463. } else if ($this.is('canvas') && SUPPORT_CANVAS) {
  464. url = $this[0].toDataURL();
  465. }
  466.  
  467. this.load(url);
  468. },
  469.  
  470. // A shortcut for triggering custom events
  471. trigger: function (type, data) {
  472. var e = $.Event(type, data);
  473.  
  474. this.$element.trigger(e);
  475.  
  476. return e;
  477. },
  478.  
  479. load: function (url) {
  480. var options = this.options;
  481. var $this = this.$element;
  482. var read;
  483. var xhr;
  484.  
  485. if (!url) {
  486. return;
  487. }
  488.  
  489. // Trigger build event first
  490. $this.one(EVENT_BUILD, options.build);
  491.  
  492. if (this.trigger(EVENT_BUILD).isDefaultPrevented()) {
  493. return;
  494. }
  495.  
  496. this.url = url;
  497. this.image = {};
  498.  
  499. if (!options.checkOrientation || !ArrayBuffer) {
  500. return this.clone();
  501. }
  502.  
  503. read = $.proxy(this.read, this);
  504.  
  505. // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari
  506. if (REGEXP_DATA_URL.test(url)) {
  507. return REGEXP_DATA_URL_JPEG.test(url) ?
  508. read(dataURLToArrayBuffer(url)) :
  509. this.clone();
  510. }
  511.  
  512. xhr = new XMLHttpRequest();
  513.  
  514. xhr.onerror = xhr.onabort = $.proxy(function () {
  515. this.clone();
  516. }, this);
  517.  
  518. xhr.onload = function () {
  519. read(this.response);
  520. };
  521.  
  522. if (options.checkCrossOrigin && isCrossOriginURL(url) && $this.prop('crossOrigin')) {
  523. url = addTimestamp(url);
  524. }
  525.  
  526. xhr.open('get', url);
  527. xhr.responseType = 'arraybuffer';
  528. xhr.send();
  529. },
  530.  
  531. read: function (arrayBuffer) {
  532. var options = this.options;
  533. var orientation = getOrientation(arrayBuffer);
  534. var image = this.image;
  535. var rotate = 0;
  536. var scaleX = 1;
  537. var scaleY = 1;
  538.  
  539. if (orientation > 1) {
  540. this.url = arrayBufferToDataURL(arrayBuffer);
  541.  
  542. switch (orientation) {
  543.  
  544. // flip horizontal
  545. case 2:
  546. scaleX = -1;
  547. break;
  548.  
  549. // rotate left 180°
  550. case 3:
  551. rotate = -180;
  552. break;
  553.  
  554. // flip vertical
  555. case 4:
  556. scaleY = -1;
  557. break;
  558.  
  559. // flip vertical + rotate right 90°
  560. case 5:
  561. rotate = 90;
  562. scaleY = -1;
  563. break;
  564.  
  565. // rotate right 90°
  566. case 6:
  567. rotate = 90;
  568. break;
  569.  
  570. // flip horizontal + rotate right 90°
  571. case 7:
  572. rotate = 90;
  573. scaleX = -1;
  574. break;
  575.  
  576. // rotate left 90°
  577. case 8:
  578. rotate = -90;
  579. break;
  580. }
  581. }
  582.  
  583. if (options.rotatable) {
  584. image.rotate = rotate;
  585. }
  586.  
  587. if (options.scalable) {
  588. image.scaleX = scaleX;
  589. image.scaleY = scaleY;
  590. }
  591.  
  592. this.clone();
  593. },
  594.  
  595. clone: function () {
  596. var options = this.options;
  597. var $this = this.$element;
  598. var url = this.url;
  599. var crossOrigin = '';
  600. var crossOriginUrl;
  601. var $clone;
  602.  
  603. if (options.checkCrossOrigin && isCrossOriginURL(url)) {
  604. crossOrigin = $this.prop('crossOrigin');
  605.  
  606. if (crossOrigin) {
  607. crossOriginUrl = url;
  608. } else {
  609. crossOrigin = 'anonymous';
  610.  
  611. // Bust cache (#148) when there is not a "crossOrigin" property
  612. crossOriginUrl = addTimestamp(url);
  613. }
  614. }
  615.  
  616. this.crossOrigin = crossOrigin;
  617. this.crossOriginUrl = crossOriginUrl;
  618. this.$clone = $clone = $('<img' + getCrossOrigin(crossOrigin) + ' src="' + (crossOriginUrl || url) + '">');
  619.  
  620. if (this.isImg) {
  621. if ($this[0].complete) {
  622. this.start();
  623. } else {
  624. $this.one(EVENT_LOAD, $.proxy(this.start, this));
  625. }
  626. } else {
  627. $clone.
  628. one(EVENT_LOAD, $.proxy(this.start, this)).
  629. one(EVENT_ERROR, $.proxy(this.stop, this)).
  630. addClass(CLASS_HIDE).
  631. insertAfter($this);
  632. }
  633. },
  634.  
  635. start: function () {
  636. var $image = this.$element;
  637. var $clone = this.$clone;
  638.  
  639. if (!this.isImg) {
  640. $clone.off(EVENT_ERROR, this.stop);
  641. $image = $clone;
  642. }
  643.  
  644. getImageSize($image[0], $.proxy(function (naturalWidth, naturalHeight) {
  645. $.extend(this.image, {
  646. naturalWidth: naturalWidth,
  647. naturalHeight: naturalHeight,
  648. aspectRatio: naturalWidth / naturalHeight
  649. });
  650.  
  651. this.isLoaded = true;
  652. this.build();
  653. }, this));
  654. },
  655.  
  656. stop: function () {
  657. this.$clone.remove();
  658. this.$clone = null;
  659. },
  660.  
  661. build: function () {
  662. var options = this.options;
  663. var $this = this.$element;
  664. var $clone = this.$clone;
  665. var $cropper;
  666. var $cropBox;
  667. var $face;
  668.  
  669. if (!this.isLoaded) {
  670. return;
  671. }
  672.  
  673. // Unbuild first when replace
  674. if (this.isBuilt) {
  675. this.unbuild();
  676. }
  677.  
  678. // Create cropper elements
  679. this.$container = $this.parent();
  680. this.$cropper = $cropper = $(Cropper.TEMPLATE);
  681. this.$canvas = $cropper.find('.cropper-canvas').append($clone);
  682. this.$dragBox = $cropper.find('.cropper-drag-box');
  683. this.$cropBox = $cropBox = $cropper.find('.cropper-crop-box');
  684. this.$viewBox = $cropper.find('.cropper-view-box');
  685. this.$face = $face = $cropBox.find('.cropper-face');
  686.  
  687. // Hide the original image
  688. $this.addClass(CLASS_HIDDEN).after($cropper);
  689.  
  690. // Show the clone image if is hidden
  691. if (!this.isImg) {
  692. $clone.removeClass(CLASS_HIDE);
  693. }
  694.  
  695. this.initPreview();
  696. this.bind();
  697.  
  698. options.aspectRatio = max(0, options.aspectRatio) || NaN;
  699. options.viewMode = max(0, min(3, round(options.viewMode))) || 0;
  700.  
  701. if (options.autoCrop) {
  702. this.isCropped = true;
  703.  
  704. if (options.modal) {
  705. this.$dragBox.addClass(CLASS_MODAL);
  706. }
  707. } else {
  708. $cropBox.addClass(CLASS_HIDDEN);
  709. }
  710.  
  711. if (!options.guides) {
  712. $cropBox.find('.cropper-dashed').addClass(CLASS_HIDDEN);
  713. }
  714.  
  715. if (!options.center) {
  716. $cropBox.find('.cropper-center').addClass(CLASS_HIDDEN);
  717. }
  718.  
  719. if (options.cropBoxMovable) {
  720. $face.addClass(CLASS_MOVE).data(DATA_ACTION, ACTION_ALL);
  721. }
  722.  
  723. if (!options.highlight) {
  724. $face.addClass(CLASS_INVISIBLE);
  725. }
  726.  
  727. if (options.background) {
  728. $cropper.addClass(CLASS_BG);
  729. }
  730.  
  731. if (!options.cropBoxResizable) {
  732. $cropBox.find('.cropper-line, .cropper-point').addClass(CLASS_HIDDEN);
  733. }
  734.  
  735. this.setDragMode(options.dragMode);
  736. this.render();
  737. this.isBuilt = true;
  738. this.setData(options.data);
  739. $this.one(EVENT_BUILT, options.built);
  740.  
  741. // Trigger the built event asynchronously to keep `data('cropper')` is defined
  742. this.completing = setTimeout($.proxy(function () {
  743. this.trigger(EVENT_BUILT);
  744. this.trigger(EVENT_CROP, this.getData());
  745. this.isCompleted = true;
  746. }, this), 0);
  747. },
  748.  
  749. unbuild: function () {
  750. if (!this.isBuilt) {
  751. return;
  752. }
  753.  
  754. if (!this.isCompleted) {
  755. clearTimeout(this.completing);
  756. }
  757.  
  758. this.isBuilt = false;
  759. this.isCompleted = false;
  760. this.initialImage = null;
  761.  
  762. // Clear `initialCanvas` is necessary when replace
  763. this.initialCanvas = null;
  764. this.initialCropBox = null;
  765. this.container = null;
  766. this.canvas = null;
  767.  
  768. // Clear `cropBox` is necessary when replace
  769. this.cropBox = null;
  770. this.unbind();
  771.  
  772. this.resetPreview();
  773. this.$preview = null;
  774.  
  775. this.$viewBox = null;
  776. this.$cropBox = null;
  777. this.$dragBox = null;
  778. this.$canvas = null;
  779. this.$container = null;
  780.  
  781. this.$cropper.remove();
  782. this.$cropper = null;
  783. },
  784.  
  785. render: function () {
  786. this.initContainer();
  787. this.initCanvas();
  788. this.initCropBox();
  789.  
  790. this.renderCanvas();
  791.  
  792. if (this.isCropped) {
  793. this.renderCropBox();
  794. }
  795. },
  796.  
  797. initContainer: function () {
  798. var options = this.options;
  799. var $this = this.$element;
  800. var $container = this.$container;
  801. var $cropper = this.$cropper;
  802.  
  803. $cropper.addClass(CLASS_HIDDEN);
  804. $this.removeClass(CLASS_HIDDEN);
  805.  
  806. $cropper.css((this.container = {
  807. width: max($container.width(), num(options.minContainerWidth) || 200),
  808. height: max($container.height(), num(options.minContainerHeight) || 100)
  809. }));
  810.  
  811. $this.addClass(CLASS_HIDDEN);
  812. $cropper.removeClass(CLASS_HIDDEN);
  813. },
  814.  
  815. // Canvas (image wrapper)
  816. initCanvas: function () {
  817. var viewMode = this.options.viewMode;
  818. var container = this.container;
  819. var containerWidth = container.width;
  820. var containerHeight = container.height;
  821. var image = this.image;
  822. var imageNaturalWidth = image.naturalWidth;
  823. var imageNaturalHeight = image.naturalHeight;
  824. var is90Degree = abs(image.rotate) === 90;
  825. var naturalWidth = is90Degree ? imageNaturalHeight : imageNaturalWidth;
  826. var naturalHeight = is90Degree ? imageNaturalWidth : imageNaturalHeight;
  827. var aspectRatio = naturalWidth / naturalHeight;
  828. var canvasWidth = containerWidth;
  829. var canvasHeight = containerHeight;
  830. var canvas;
  831.  
  832. if (containerHeight * aspectRatio > containerWidth) {
  833. if (viewMode === 3) {
  834. canvasWidth = containerHeight * aspectRatio;
  835. } else {
  836. canvasHeight = containerWidth / aspectRatio;
  837. }
  838. } else {
  839. if (viewMode === 3) {
  840. canvasHeight = containerWidth / aspectRatio;
  841. } else {
  842. canvasWidth = containerHeight * aspectRatio;
  843. }
  844. }
  845. canvasWidth = canvasWidth * 0.8;
  846. canvasHeight = canvasHeight * 0.8;
  847.  
  848. canvas = {
  849. naturalWidth: naturalWidth,
  850. naturalHeight: naturalHeight,
  851. aspectRatio: aspectRatio,
  852. width: canvasWidth,
  853. height: canvasHeight
  854. };
  855.  
  856. canvas.oldLeft = canvas.left = (containerWidth - canvasWidth) / 2;
  857. canvas.oldTop = canvas.top = (containerHeight - canvasHeight) / 2;
  858.  
  859. this.canvas = canvas;
  860. this.isLimited = (viewMode === 1 || viewMode === 2);
  861. this.limitCanvas(true, true);
  862. this.initialImage = $.extend({}, image);
  863. this.initialCanvas = $.extend({}, canvas);
  864. },
  865.  
  866. limitCanvas: function (isSizeLimited, isPositionLimited) {
  867. var options = this.options;
  868. var viewMode = options.viewMode;
  869. var container = this.container;
  870. var containerWidth = container.width;
  871. var containerHeight = container.height;
  872. var canvas = this.canvas;
  873. var aspectRatio = canvas.aspectRatio;
  874. var cropBox = this.cropBox;
  875. var isCropped = this.isCropped && cropBox;
  876. var minCanvasWidth;
  877. var minCanvasHeight;
  878. var newCanvasLeft;
  879. var newCanvasTop;
  880.  
  881. if (isSizeLimited) {
  882. minCanvasWidth = num(options.minCanvasWidth) || 0;
  883. minCanvasHeight = num(options.minCanvasHeight) || 0;
  884.  
  885. if (viewMode) {
  886. if (viewMode > 1) {
  887. minCanvasWidth = max(minCanvasWidth, containerWidth);
  888. minCanvasHeight = max(minCanvasHeight, containerHeight);
  889.  
  890. if (viewMode === 3) {
  891. if (minCanvasHeight * aspectRatio > minCanvasWidth) {
  892. minCanvasWidth = minCanvasHeight * aspectRatio;
  893. } else {
  894. minCanvasHeight = minCanvasWidth / aspectRatio;
  895. }
  896. }
  897. } else {
  898. if (minCanvasWidth) {
  899. minCanvasWidth = max(minCanvasWidth, isCropped ? cropBox.width : 0);
  900. } else if (minCanvasHeight) {
  901. minCanvasHeight = max(minCanvasHeight, isCropped ? cropBox.height : 0);
  902. } else if (isCropped) {
  903. minCanvasWidth = cropBox.width;
  904. minCanvasHeight = cropBox.height;
  905.  
  906. if (minCanvasHeight * aspectRatio > minCanvasWidth) {
  907. minCanvasWidth = minCanvasHeight * aspectRatio;
  908. } else {
  909. minCanvasHeight = minCanvasWidth / aspectRatio;
  910. }
  911. }
  912. }
  913. }
  914.  
  915. if (minCanvasWidth && minCanvasHeight) {
  916. if (minCanvasHeight * aspectRatio > minCanvasWidth) {
  917. minCanvasHeight = minCanvasWidth / aspectRatio;
  918. } else {
  919. minCanvasWidth = minCanvasHeight * aspectRatio;
  920. }
  921. } else if (minCanvasWidth) {
  922. minCanvasHeight = minCanvasWidth / aspectRatio;
  923. } else if (minCanvasHeight) {
  924. minCanvasWidth = minCanvasHeight * aspectRatio;
  925. }
  926.  
  927. canvas.minWidth = minCanvasWidth;
  928. canvas.minHeight = minCanvasHeight;
  929. canvas.maxWidth = Infinity;
  930. canvas.maxHeight = Infinity;
  931. }
  932.  
  933. if (isPositionLimited) {
  934. if (viewMode) {
  935. newCanvasLeft = containerWidth - canvas.width;
  936. newCanvasTop = containerHeight - canvas.height;
  937.  
  938. canvas.minLeft = min(0, newCanvasLeft);
  939. canvas.minTop = min(0, newCanvasTop);
  940. canvas.maxLeft = max(0, newCanvasLeft);
  941. canvas.maxTop = max(0, newCanvasTop);
  942.  
  943. if (isCropped && this.isLimited) {
  944. canvas.minLeft = min(
  945. cropBox.left,
  946. cropBox.left + cropBox.width - canvas.width
  947. );
  948. canvas.minTop = min(
  949. cropBox.top,
  950. cropBox.top + cropBox.height - canvas.height
  951. );
  952. canvas.maxLeft = cropBox.left;
  953. canvas.maxTop = cropBox.top;
  954.  
  955. if (viewMode === 2) {
  956. if (canvas.width >= containerWidth) {
  957. canvas.minLeft = min(0, newCanvasLeft);
  958. canvas.maxLeft = max(0, newCanvasLeft);
  959. }
  960.  
  961. if (canvas.height >= containerHeight) {
  962. canvas.minTop = min(0, newCanvasTop);
  963. canvas.maxTop = max(0, newCanvasTop);
  964. }
  965. }
  966. }
  967. } else {
  968. canvas.minLeft = -canvas.width;
  969. canvas.minTop = -canvas.height;
  970. canvas.maxLeft = containerWidth;
  971. canvas.maxTop = containerHeight;
  972. }
  973. }
  974. },
  975.  
  976. renderCanvas: function (isChanged) {
  977. var canvas = this.canvas;
  978. var image = this.image;
  979. var rotate = image.rotate;
  980. var naturalWidth = image.naturalWidth;
  981. var naturalHeight = image.naturalHeight;
  982. var aspectRatio;
  983. var rotated;
  984.  
  985. if (this.isRotated) {
  986. this.isRotated = false;
  987.  
  988. // Computes rotated sizes with image sizes
  989. rotated = getRotatedSizes({
  990. width: image.width,
  991. height: image.height,
  992. degree: rotate
  993. });
  994.  
  995. aspectRatio = rotated.width / rotated.height;
  996.  
  997. if (aspectRatio !== canvas.aspectRatio) {
  998. canvas.left -= (rotated.width - canvas.width) / 2;
  999. canvas.top -= (rotated.height - canvas.height) / 2;
  1000. canvas.width = rotated.width;
  1001. canvas.height = rotated.height;
  1002. canvas.aspectRatio = aspectRatio;
  1003. canvas.naturalWidth = naturalWidth;
  1004. canvas.naturalHeight = naturalHeight;
  1005.  
  1006. // Computes rotated sizes with natural image sizes
  1007. if (rotate % 180) {
  1008. rotated = getRotatedSizes({
  1009. width: naturalWidth,
  1010. height: naturalHeight,
  1011. degree: rotate
  1012. });
  1013.  
  1014. canvas.naturalWidth = rotated.width;
  1015. canvas.naturalHeight = rotated.height;
  1016. }
  1017.  
  1018. this.limitCanvas(true, false);
  1019. }
  1020. }
  1021.  
  1022. if (canvas.width > canvas.maxWidth || canvas.width < canvas.minWidth) {
  1023. canvas.left = canvas.oldLeft;
  1024. }
  1025.  
  1026. if (canvas.height > canvas.maxHeight || canvas.height < canvas.minHeight) {
  1027. canvas.top = canvas.oldTop;
  1028. }
  1029.  
  1030. canvas.width = min(max(canvas.width, canvas.minWidth), canvas.maxWidth);
  1031. canvas.height = min(max(canvas.height, canvas.minHeight), canvas.maxHeight);
  1032.  
  1033. this.limitCanvas(false, true);
  1034.  
  1035. canvas.oldLeft = canvas.left = min(max(canvas.left, canvas.minLeft), canvas.maxLeft);
  1036. canvas.oldTop = canvas.top = min(max(canvas.top, canvas.minTop), canvas.maxTop);
  1037.  
  1038. this.$canvas.css({
  1039. width: canvas.width,
  1040. height: canvas.height,
  1041. left: canvas.left,
  1042. top: canvas.top
  1043. });
  1044. //add by zhaoliang 20161224
  1045. $("#drawing-board").css({
  1046. width: canvas.width,
  1047. height: canvas.height,
  1048. left: canvas.left,
  1049. top: canvas.top
  1050. });
  1051. $(".drawing-board-canvas").css({
  1052. width: canvas.width,
  1053. height: canvas.height
  1054. });
  1055. //add end
  1056.  
  1057. this.renderImage();
  1058.  
  1059. if (this.isCropped && this.isLimited) {
  1060. this.limitCropBox(true, true);
  1061. }
  1062.  
  1063. if (isChanged) {
  1064. this.output();
  1065. }
  1066. },
  1067.  
  1068. renderImage: function (isChanged) {
  1069. var canvas = this.canvas;
  1070. var image = this.image;
  1071. var reversed;
  1072.  
  1073. if (image.rotate) {
  1074. reversed = getRotatedSizes({
  1075. width: canvas.width,
  1076. height: canvas.height,
  1077. degree: image.rotate,
  1078. aspectRatio: image.aspectRatio
  1079. }, true);
  1080. }
  1081.  
  1082. $.extend(image, reversed ? {
  1083. width: reversed.width,
  1084. height: reversed.height,
  1085. left: (canvas.width - reversed.width) / 2,
  1086. top: (canvas.height - reversed.height) / 2
  1087. } : {
  1088. width: canvas.width,
  1089. height: canvas.height,
  1090. left: 0,
  1091. top: 0
  1092. });
  1093.  
  1094. this.$clone.css({
  1095. width: image.width,
  1096. height: image.height,
  1097. marginLeft: image.left,
  1098. marginTop: image.top,
  1099. transform: getTransform(image)
  1100. });
  1101.  
  1102. //图片旋转的时候,canvas也旋转
  1103. $(".drawing-board-canvas").css({
  1104. width: image.width,
  1105. height: image.height,
  1106. marginLeft: image.left,
  1107. marginTop: image.top,
  1108. transform: getTransform(image)
  1109. });
  1110. //end
  1111.  
  1112. if (isChanged) {
  1113. this.output();
  1114. }
  1115. },
  1116.  
  1117. initCropBox: function () {
  1118. var options = this.options;
  1119. var canvas = this.canvas;
  1120. var aspectRatio = options.aspectRatio;
  1121. var autoCropArea = num(options.autoCropArea) || 0.8;
  1122. var cropBox = {
  1123. width: canvas.width,
  1124. height: canvas.height
  1125. };
  1126.  
  1127. if (aspectRatio) {
  1128. if (canvas.height * aspectRatio > canvas.width) {
  1129. cropBox.height = cropBox.width / aspectRatio;
  1130. } else {
  1131. cropBox.width = cropBox.height * aspectRatio;
  1132. }
  1133. }
  1134.  
  1135. //add by zhaoliang 20161224
  1136. canvas.top = 10;
  1137. $("#drawing-board").css({
  1138. width: canvas.width,
  1139. height: canvas.height,
  1140. left: canvas.left,
  1141. top: canvas.top
  1142. });
  1143. $(".drawing-board-canvas").css({
  1144. width: canvas.width,
  1145. height: canvas.height
  1146. });
  1147. //$(".drawing-board-canvas").attr({
  1148. // width: canvas.width,
  1149. // height: canvas.height
  1150. //});
  1151. //add end
  1152. this.cropBox = cropBox;
  1153. this.limitCropBox(true, true);
  1154.  
  1155. // Initialize auto crop area
  1156. cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
  1157. cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);
  1158.  
  1159. // The width of auto crop area must large than "minWidth", and the height too. (#164)
  1160. cropBox.width = max(cropBox.minWidth, cropBox.width * autoCropArea);
  1161. cropBox.height = max(cropBox.minHeight, cropBox.height * autoCropArea);
  1162. cropBox.oldLeft = cropBox.left = canvas.left + (canvas.width - cropBox.width) / 2;
  1163. cropBox.oldTop = cropBox.top = canvas.top + (canvas.height - cropBox.height) / 2;
  1164.  
  1165. this.initialCropBox = $.extend({}, cropBox);
  1166. },
  1167.  
  1168. limitCropBox: function (isSizeLimited, isPositionLimited) {
  1169. var options = this.options;
  1170. var aspectRatio = options.aspectRatio;
  1171. var container = this.container;
  1172. var containerWidth = container.width;
  1173. var containerHeight = container.height;
  1174. var canvas = this.canvas;
  1175. var cropBox = this.cropBox;
  1176. var isLimited = this.isLimited;
  1177. var minCropBoxWidth;
  1178. var minCropBoxHeight;
  1179. var maxCropBoxWidth;
  1180. var maxCropBoxHeight;
  1181.  
  1182. if (isSizeLimited) {
  1183. minCropBoxWidth = num(options.minCropBoxWidth) || 0;
  1184. minCropBoxHeight = num(options.minCropBoxHeight) || 0;
  1185.  
  1186. // The min/maxCropBoxWidth/Height must be less than containerWidth/Height
  1187. minCropBoxWidth = min(minCropBoxWidth, containerWidth);
  1188. minCropBoxHeight = min(minCropBoxHeight, containerHeight);
  1189. maxCropBoxWidth = min(containerWidth, isLimited ? canvas.width : containerWidth);
  1190. maxCropBoxHeight = min(containerHeight, isLimited ? canvas.height : containerHeight);
  1191.  
  1192. if (aspectRatio) {
  1193. if (minCropBoxWidth && minCropBoxHeight) {
  1194. if (minCropBoxHeight * aspectRatio > minCropBoxWidth) {
  1195. minCropBoxHeight = minCropBoxWidth / aspectRatio;
  1196. } else {
  1197. minCropBoxWidth = minCropBoxHeight * aspectRatio;
  1198. }
  1199. } else if (minCropBoxWidth) {
  1200. minCropBoxHeight = minCropBoxWidth / aspectRatio;
  1201. } else if (minCropBoxHeight) {
  1202. minCropBoxWidth = minCropBoxHeight * aspectRatio;
  1203. }
  1204.  
  1205. if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) {
  1206. maxCropBoxHeight = maxCropBoxWidth / aspectRatio;
  1207. } else {
  1208. maxCropBoxWidth = maxCropBoxHeight * aspectRatio;
  1209. }
  1210. }
  1211.  
  1212. // The minWidth/Height must be less than maxWidth/Height
  1213. cropBox.minWidth = min(minCropBoxWidth, maxCropBoxWidth);
  1214. cropBox.minHeight = min(minCropBoxHeight, maxCropBoxHeight);
  1215. cropBox.maxWidth = maxCropBoxWidth;
  1216. cropBox.maxHeight = maxCropBoxHeight;
  1217. }
  1218.  
  1219. if (isPositionLimited) {
  1220. if (isLimited) {
  1221. cropBox.minLeft = max(0, canvas.left);
  1222. cropBox.minTop = max(0, canvas.top);
  1223. cropBox.maxLeft = min(containerWidth, canvas.left + canvas.width) - cropBox.width;
  1224. cropBox.maxTop = min(containerHeight, canvas.top + canvas.height) - cropBox.height;
  1225. } else {
  1226. cropBox.minLeft = 0;
  1227. cropBox.minTop = 0;
  1228. cropBox.maxLeft = containerWidth - cropBox.width;
  1229. cropBox.maxTop = containerHeight - cropBox.height;
  1230. }
  1231. }
  1232. },
  1233.  
  1234. renderCropBox: function () {
  1235. var options = this.options;
  1236. var container = this.container;
  1237. var containerWidth = container.width;
  1238. var containerHeight = container.height;
  1239. var cropBox = this.cropBox;
  1240.  
  1241. if (cropBox.width > cropBox.maxWidth || cropBox.width < cropBox.minWidth) {
  1242. cropBox.left = cropBox.oldLeft;
  1243. }
  1244.  
  1245. if (cropBox.height > cropBox.maxHeight || cropBox.height < cropBox.minHeight) {
  1246. cropBox.top = cropBox.oldTop;
  1247. }
  1248.  
  1249. cropBox.width = min(max(cropBox.width, cropBox.minWidth), cropBox.maxWidth);
  1250. cropBox.height = min(max(cropBox.height, cropBox.minHeight), cropBox.maxHeight);
  1251.  
  1252. this.limitCropBox(false, true);
  1253.  
  1254. cropBox.oldLeft = cropBox.left = min(max(cropBox.left, cropBox.minLeft), cropBox.maxLeft);
  1255. cropBox.oldTop = cropBox.top = min(max(cropBox.top, cropBox.minTop), cropBox.maxTop);
  1256.  
  1257. if (options.movable && options.cropBoxMovable) {
  1258.  
  1259. // Turn to move the canvas when the crop box is equal to the container
  1260. this.$face.data(DATA_ACTION, (cropBox.width === containerWidth && cropBox.height === containerHeight) ? ACTION_MOVE : ACTION_ALL);
  1261. }
  1262.  
  1263. this.$cropBox.css({
  1264. width: cropBox.width,
  1265. height: cropBox.height,
  1266. left: cropBox.left,
  1267. top: cropBox.top
  1268. });
  1269.  
  1270. if (this.isCropped && this.isLimited) {
  1271. this.limitCanvas(true, true);
  1272. }
  1273.  
  1274. if (!this.isDisabled) {
  1275. this.output();
  1276. }
  1277. },
  1278.  
  1279. output: function () {
  1280. this.preview();
  1281.  
  1282. if (this.isCompleted) {
  1283. this.trigger(EVENT_CROP, this.getData());
  1284. }
  1285. },
  1286.  
  1287. initPreview: function () {
  1288. var crossOrigin = getCrossOrigin(this.crossOrigin);
  1289. var url = crossOrigin ? this.crossOriginUrl : this.url;
  1290. var $clone2;
  1291.  
  1292. this.$preview = $(this.options.preview);
  1293. this.$clone2 = $clone2 = $('<img' + crossOrigin + ' src="' + url + '">');
  1294. this.$viewBox.html($clone2);
  1295. this.$preview.each(function () {
  1296. var $this = $(this);
  1297.  
  1298. // Save the original size for recover
  1299. $this.data(DATA_PREVIEW, {
  1300. width: $this.width(),
  1301. height: $this.height(),
  1302. html: $this.html()
  1303. });
  1304.  
  1305. /**
  1306. * Override img element styles
  1307. * Add `display:block` to avoid margin top issue
  1308. * (Occur only when margin-top <= -height)
  1309. */
  1310. $this.html(
  1311. '<img' + crossOrigin + ' src="' + url + '" style="' +
  1312. 'display:block;width:100%;height:auto;' +
  1313. 'min-width:0!important;min-height:0!important;' +
  1314. 'max-width:none!important;max-height:none!important;' +
  1315. 'image-orientation:0deg!important;">'
  1316. );
  1317. });
  1318. },
  1319.  
  1320. resetPreview: function () {
  1321. this.$preview.each(function () {
  1322. var $this = $(this);
  1323. var data = $this.data(DATA_PREVIEW);
  1324.  
  1325. $this.css({
  1326. width: data.width,
  1327. height: data.height
  1328. }).html(data.html).removeData(DATA_PREVIEW);
  1329. });
  1330. },
  1331.  
  1332. preview: function () {
  1333. var image = this.image;
  1334. var canvas = this.canvas;
  1335. var cropBox = this.cropBox;
  1336. var cropBoxWidth = cropBox.width;
  1337. var cropBoxHeight = cropBox.height;
  1338. var width = image.width;
  1339. var height = image.height;
  1340. var left = cropBox.left - canvas.left - image.left;
  1341. var top = cropBox.top - canvas.top - image.top;
  1342.  
  1343. if (!this.isCropped || this.isDisabled) {
  1344. return;
  1345. }
  1346.  
  1347. this.$clone2.css({
  1348. width: width,
  1349. height: height,
  1350. marginLeft: -left,
  1351. marginTop: -top,
  1352. transform: getTransform(image)
  1353. });
  1354.  
  1355. this.$preview.each(function () {
  1356. var $this = $(this);
  1357. var data = $this.data(DATA_PREVIEW);
  1358. var originalWidth = data.width;
  1359. var originalHeight = data.height;
  1360. var newWidth = originalWidth;
  1361. var newHeight = originalHeight;
  1362. var ratio = 1;
  1363.  
  1364. if (cropBoxWidth) {
  1365. ratio = originalWidth / cropBoxWidth;
  1366. newHeight = cropBoxHeight * ratio;
  1367. }
  1368.  
  1369. if (cropBoxHeight && newHeight > originalHeight) {
  1370. ratio = originalHeight / cropBoxHeight;
  1371. newWidth = cropBoxWidth * ratio;
  1372. newHeight = originalHeight;
  1373. }
  1374.  
  1375. $this.css({
  1376. width: newWidth,
  1377. height: newHeight
  1378. }).find('img').css({
  1379. width: width * ratio,
  1380. height: height * ratio,
  1381. marginLeft: -left * ratio,
  1382. marginTop: -top * ratio,
  1383. transform: getTransform(image)
  1384. });
  1385. });
  1386. },
  1387.  
  1388. bind: function () {
  1389. var options = this.options;
  1390. var $this = this.$element;
  1391. var $cropper = this.$cropper;
  1392.  
  1393. if ($.isFunction(options.cropstart)) {
  1394. $this.on(EVENT_CROP_START, options.cropstart);
  1395. }
  1396.  
  1397. if ($.isFunction(options.cropmove)) {
  1398. $this.on(EVENT_CROP_MOVE, options.cropmove);
  1399. }
  1400.  
  1401. if ($.isFunction(options.cropend)) {
  1402. $this.on(EVENT_CROP_END, options.cropend);
  1403. }
  1404.  
  1405. if ($.isFunction(options.crop)) {
  1406. $this.on(EVENT_CROP, options.crop);
  1407. }
  1408.  
  1409. if ($.isFunction(options.zoom)) {
  1410. $this.on(EVENT_ZOOM, options.zoom);
  1411. }
  1412.  
  1413. $cropper.on(EVENT_MOUSE_DOWN, $.proxy(this.cropStart, this));
  1414.  
  1415. if (options.zoomable && options.zoomOnWheel) {
  1416. $cropper.on(EVENT_WHEEL, $.proxy(this.wheel, this));
  1417. }
  1418.  
  1419. if (options.toggleDragModeOnDblclick) {
  1420. $cropper.on(EVENT_DBLCLICK, $.proxy(this.dblclick, this));
  1421. }
  1422.  
  1423. $document.
  1424. on(EVENT_MOUSE_MOVE, (this._cropMove = proxy(this.cropMove, this))).
  1425. on(EVENT_MOUSE_UP, (this._cropEnd = proxy(this.cropEnd, this)));
  1426.  
  1427. if (options.responsive) {
  1428. $window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this)));
  1429. }
  1430. },
  1431.  
  1432. unbind: function () {
  1433. var options = this.options;
  1434. var $this = this.$element;
  1435. var $cropper = this.$cropper;
  1436.  
  1437. if ($.isFunction(options.cropstart)) {
  1438. $this.off(EVENT_CROP_START, options.cropstart);
  1439. }
  1440.  
  1441. if ($.isFunction(options.cropmove)) {
  1442. $this.off(EVENT_CROP_MOVE, options.cropmove);
  1443. }
  1444.  
  1445. if ($.isFunction(options.cropend)) {
  1446. $this.off(EVENT_CROP_END, options.cropend);
  1447. }
  1448.  
  1449. if ($.isFunction(options.crop)) {
  1450. $this.off(EVENT_CROP, options.crop);
  1451. }
  1452.  
  1453. if ($.isFunction(options.zoom)) {
  1454. $this.off(EVENT_ZOOM, options.zoom);
  1455. }
  1456.  
  1457. $cropper.off(EVENT_MOUSE_DOWN, this.cropStart);
  1458.  
  1459. if (options.zoomable && options.zoomOnWheel) {
  1460. $cropper.off(EVENT_WHEEL, this.wheel);
  1461. }
  1462.  
  1463. if (options.toggleDragModeOnDblclick) {
  1464. $cropper.off(EVENT_DBLCLICK, this.dblclick);
  1465. }
  1466.  
  1467. $document.
  1468. off(EVENT_MOUSE_MOVE, this._cropMove).
  1469. off(EVENT_MOUSE_UP, this._cropEnd);
  1470.  
  1471. if (options.responsive) {
  1472. $window.off(EVENT_RESIZE, this._resize);
  1473. }
  1474. },
  1475.  
  1476. resize: function () {
  1477. var restore = this.options.restore;
  1478. var $container = this.$container;
  1479. var container = this.container;
  1480. var canvasData;
  1481. var cropBoxData;
  1482. var ratio;
  1483.  
  1484. // Check `container` is necessary for IE8
  1485. if (this.isDisabled || !container) {
  1486. return;
  1487. }
  1488.  
  1489. ratio = $container.width() / container.width;
  1490.  
  1491. // Resize when width changed or height changed
  1492. if (ratio !== 1 || $container.height() !== container.height) {
  1493. if (restore) {
  1494. canvasData = this.getCanvasData();
  1495. cropBoxData = this.getCropBoxData();
  1496. }
  1497.  
  1498. this.render();
  1499.  
  1500. if (restore) {
  1501. this.setCanvasData($.each(canvasData, function (i, n) {
  1502. canvasData[i] = n * ratio;
  1503. }));
  1504. this.setCropBoxData($.each(cropBoxData, function (i, n) {
  1505. cropBoxData[i] = n * ratio;
  1506. }));
  1507. }
  1508. }
  1509. },
  1510.  
  1511. dblclick: function () {
  1512. if (this.isDisabled) {
  1513. return;
  1514. }
  1515.  
  1516. if (this.$dragBox.hasClass(CLASS_CROP)) {
  1517. this.setDragMode(ACTION_MOVE);
  1518. } else {
  1519. this.setDragMode(ACTION_CROP);
  1520. }
  1521. },
  1522.  
  1523. wheel: function (event) {
  1524. var e = event.originalEvent || event;
  1525. var ratio = num(this.options.wheelZoomRatio) || 0.1;
  1526. var delta = 1;
  1527.  
  1528. if (this.isDisabled) {
  1529. return;
  1530. }
  1531.  
  1532. event.preventDefault();
  1533.  
  1534. // Limit wheel speed to prevent zoom too fast
  1535. if (this.wheeling) {
  1536. return;
  1537. }
  1538.  
  1539. this.wheeling = true;
  1540.  
  1541. setTimeout($.proxy(function () {
  1542. this.wheeling = false;
  1543. }, this), 50);
  1544.  
  1545. if (e.deltaY) {
  1546. delta = e.deltaY > 0 ? 1 : -1;
  1547. } else if (e.wheelDelta) {
  1548. delta = -e.wheelDelta / 120;
  1549. } else if (e.detail) {
  1550. delta = e.detail > 0 ? 1 : -1;
  1551. }
  1552.  
  1553. this.zoom(-delta * ratio, event);
  1554. },
  1555.  
  1556. cropStart: function (event) {
  1557. var options = this.options;
  1558. var originalEvent = event.originalEvent;
  1559. var touches = originalEvent && originalEvent.touches;
  1560. var e = event;
  1561. var touchesLength;
  1562. var action;
  1563.  
  1564. if (this.isDisabled) {
  1565. return;
  1566. }
  1567.  
  1568. if (touches) {
  1569. touchesLength = touches.length;
  1570.  
  1571. if (touchesLength > 1) {
  1572. if (options.zoomable && options.zoomOnTouch && touchesLength === 2) {
  1573. e = touches[1];
  1574. this.startX2 = e.pageX;
  1575. this.startY2 = e.pageY;
  1576. action = ACTION_ZOOM;
  1577. } else {
  1578. return;
  1579. }
  1580. }
  1581.  
  1582. e = touches[0];
  1583. }
  1584.  
  1585. action = action || $(e.target).data(DATA_ACTION);
  1586.  
  1587. if (REGEXP_ACTIONS.test(action)) {
  1588. if (this.trigger(EVENT_CROP_START, {
  1589. originalEvent: originalEvent,
  1590. action: action
  1591. }).isDefaultPrevented()) {
  1592. return;
  1593. }
  1594.  
  1595. event.preventDefault();
  1596.  
  1597. this.action = action;
  1598. this.cropping = false;
  1599.  
  1600. // IE8 has `event.pageX/Y`, but not `event.originalEvent.pageX/Y`
  1601. // IE10 has `event.originalEvent.pageX/Y`, but not `event.pageX/Y`
  1602. this.startX = e.pageX || originalEvent && originalEvent.pageX;
  1603. this.startY = e.pageY || originalEvent && originalEvent.pageY;
  1604.  
  1605. if (action === ACTION_CROP) {
  1606. this.cropping = true;
  1607. this.$dragBox.addClass(CLASS_MODAL);
  1608. }
  1609. }
  1610. },
  1611.  
  1612. cropMove: function (event) {
  1613. var options = this.options;
  1614. var originalEvent = event.originalEvent;
  1615. var touches = originalEvent && originalEvent.touches;
  1616. var e = event;
  1617. var action = this.action;
  1618. var touchesLength;
  1619.  
  1620. if (this.isDisabled) {
  1621. return;
  1622. }
  1623.  
  1624. if (touches) {
  1625. touchesLength = touches.length;
  1626.  
  1627. if (touchesLength > 1) {
  1628. if (options.zoomable && options.zoomOnTouch && touchesLength === 2) {
  1629. e = touches[1];
  1630. this.endX2 = e.pageX;
  1631. this.endY2 = e.pageY;
  1632. } else {
  1633. return;
  1634. }
  1635. }
  1636.  
  1637. e = touches[0];
  1638. }
  1639.  
  1640. if (action) {
  1641. if (this.trigger(EVENT_CROP_MOVE, {
  1642. originalEvent: originalEvent,
  1643. action: action
  1644. }).isDefaultPrevented()) {
  1645. return;
  1646. }
  1647.  
  1648. event.preventDefault();
  1649.  
  1650. this.endX = e.pageX || originalEvent && originalEvent.pageX;
  1651. this.endY = e.pageY || originalEvent && originalEvent.pageY;
  1652.  
  1653. this.change(e.shiftKey, action === ACTION_ZOOM ? event : null);
  1654. }
  1655. },
  1656.  
  1657. cropEnd: function (event) {
  1658. var originalEvent = event.originalEvent;
  1659. var action = this.action;
  1660.  
  1661. if (this.isDisabled) {
  1662. return;
  1663. }
  1664.  
  1665. if (action) {
  1666. event.preventDefault();
  1667.  
  1668. if (this.cropping) {
  1669. this.cropping = false;
  1670. this.$dragBox.toggleClass(CLASS_MODAL, this.isCropped && this.options.modal);
  1671. }
  1672.  
  1673. this.action = '';
  1674.  
  1675. this.trigger(EVENT_CROP_END, {
  1676. originalEvent: originalEvent,
  1677. action: action
  1678. });
  1679. }
  1680. },
  1681.  
  1682. change: function (shiftKey, event) {
  1683. var options = this.options;
  1684. var aspectRatio = options.aspectRatio;
  1685. var action = this.action;
  1686. var container = this.container;
  1687. var canvas = this.canvas;
  1688. var cropBox = this.cropBox;
  1689. var width = cropBox.width;
  1690. var height = cropBox.height;
  1691. var left = cropBox.left;
  1692. var top = cropBox.top;
  1693. var right = left + width;
  1694. var bottom = top + height;
  1695. var minLeft = 0;
  1696. var minTop = 0;
  1697. var maxWidth = container.width;
  1698. var maxHeight = container.height;
  1699. var renderable = true;
  1700. var offset;
  1701. var range;
  1702.  
  1703. // Locking aspect ratio in "free mode" by holding shift key (#259)
  1704. if (!aspectRatio && shiftKey) {
  1705. aspectRatio = width && height ? width / height : 1;
  1706. }
  1707.  
  1708. if (this.isLimited) {
  1709. minLeft = cropBox.minLeft;
  1710. minTop = cropBox.minTop;
  1711. maxWidth = minLeft + min(container.width, canvas.width, canvas.left + canvas.width);
  1712. maxHeight = minTop + min(container.height, canvas.height, canvas.top + canvas.height);
  1713. }
  1714.  
  1715. range = {
  1716. x: this.endX - this.startX,
  1717. y: this.endY - this.startY
  1718. };
  1719.  
  1720. if (aspectRatio) {
  1721. range.X = range.y * aspectRatio;
  1722. range.Y = range.x / aspectRatio;
  1723. }
  1724.  
  1725. switch (action) {
  1726. // Move crop box
  1727. case ACTION_ALL:
  1728. left += range.x;
  1729. top += range.y;
  1730. break;
  1731.  
  1732. // Resize crop box
  1733. case ACTION_EAST:
  1734. if (range.x >= 0 && (right >= maxWidth || aspectRatio &&
  1735. (top <= minTop || bottom >= maxHeight))) {
  1736.  
  1737. renderable = false;
  1738. break;
  1739. }
  1740.  
  1741. width += range.x;
  1742.  
  1743. if (aspectRatio) {
  1744. height = width / aspectRatio;
  1745. top -= range.Y / 2;
  1746. }
  1747.  
  1748. if (width < 0) {
  1749. action = ACTION_WEST;
  1750. width = 0;
  1751. }
  1752.  
  1753. break;
  1754.  
  1755. case ACTION_NORTH:
  1756. if (range.y <= 0 && (top <= minTop || aspectRatio &&
  1757. (left <= minLeft || right >= maxWidth))) {
  1758.  
  1759. renderable = false;
  1760. break;
  1761. }
  1762.  
  1763. height -= range.y;
  1764. top += range.y;
  1765.  
  1766. if (aspectRatio) {
  1767. width = height * aspectRatio;
  1768. left += range.X / 2;
  1769. }
  1770.  
  1771. if (height < 0) {
  1772. action = ACTION_SOUTH;
  1773. height = 0;
  1774. }
  1775.  
  1776. break;
  1777.  
  1778. case ACTION_WEST:
  1779. if (range.x <= 0 && (left <= minLeft || aspectRatio &&
  1780. (top <= minTop || bottom >= maxHeight))) {
  1781.  
  1782. renderable = false;
  1783. break;
  1784. }
  1785.  
  1786. width -= range.x;
  1787. left += range.x;
  1788.  
  1789. if (aspectRatio) {
  1790. height = width / aspectRatio;
  1791. top += range.Y / 2;
  1792. }
  1793.  
  1794. if (width < 0) {
  1795. action = ACTION_EAST;
  1796. width = 0;
  1797. }
  1798.  
  1799. break;
  1800.  
  1801. case ACTION_SOUTH:
  1802. if (range.y >= 0 && (bottom >= maxHeight || aspectRatio &&
  1803. (left <= minLeft || right >= maxWidth))) {
  1804.  
  1805. renderable = false;
  1806. break;
  1807. }
  1808.  
  1809. height += range.y;
  1810.  
  1811. if (aspectRatio) {
  1812. width = height * aspectRatio;
  1813. left -= range.X / 2;
  1814. }
  1815.  
  1816. if (height < 0) {
  1817. action = ACTION_NORTH;
  1818. height = 0;
  1819. }
  1820.  
  1821. break;
  1822.  
  1823. case ACTION_NORTH_EAST:
  1824. if (aspectRatio) {
  1825. if (range.y <= 0 && (top <= minTop || right >= maxWidth)) {
  1826. renderable = false;
  1827. break;
  1828. }
  1829.  
  1830. height -= range.y;
  1831. top += range.y;
  1832. width = height * aspectRatio;
  1833. } else {
  1834. if (range.x >= 0) {
  1835. if (right < maxWidth) {
  1836. width += range.x;
  1837. } else if (range.y <= 0 && top <= minTop) {
  1838. renderable = false;
  1839. }
  1840. } else {
  1841. width += range.x;
  1842. }
  1843.  
  1844. if (range.y <= 0) {
  1845. if (top > minTop) {
  1846. height -= range.y;
  1847. top += range.y;
  1848. }
  1849. } else {
  1850. height -= range.y;
  1851. top += range.y;
  1852. }
  1853. }
  1854.  
  1855. if (width < 0 && height < 0) {
  1856. action = ACTION_SOUTH_WEST;
  1857. height = 0;
  1858. width = 0;
  1859. } else if (width < 0) {
  1860. action = ACTION_NORTH_WEST;
  1861. width = 0;
  1862. } else if (height < 0) {
  1863. action = ACTION_SOUTH_EAST;
  1864. height = 0;
  1865. }
  1866.  
  1867. break;
  1868.  
  1869. case ACTION_NORTH_WEST:
  1870. if (aspectRatio) {
  1871. if (range.y <= 0 && (top <= minTop || left <= minLeft)) {
  1872. renderable = false;
  1873. break;
  1874. }
  1875.  
  1876. height -= range.y;
  1877. top += range.y;
  1878. width = height * aspectRatio;
  1879. left += range.X;
  1880. } else {
  1881. if (range.x <= 0) {
  1882. if (left > minLeft) {
  1883. width -= range.x;
  1884. left += range.x;
  1885. } else if (range.y <= 0 && top <= minTop) {
  1886. renderable = false;
  1887. }
  1888. } else {
  1889. width -= range.x;
  1890. left += range.x;
  1891. }
  1892.  
  1893. if (range.y <= 0) {
  1894. if (top > minTop) {
  1895. height -= range.y;
  1896. top += range.y;
  1897. }
  1898. } else {
  1899. height -= range.y;
  1900. top += range.y;
  1901. }
  1902. }
  1903.  
  1904. if (width < 0 && height < 0) {
  1905. action = ACTION_SOUTH_EAST;
  1906. height = 0;
  1907. width = 0;
  1908. } else if (width < 0) {
  1909. action = ACTION_NORTH_EAST;
  1910. width = 0;
  1911. } else if (height < 0) {
  1912. action = ACTION_SOUTH_WEST;
  1913. height = 0;
  1914. }
  1915.  
  1916. break;
  1917.  
  1918. case ACTION_SOUTH_WEST:
  1919. if (aspectRatio) {
  1920. if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) {
  1921. renderable = false;
  1922. break;
  1923. }
  1924.  
  1925. width -= range.x;
  1926. left += range.x;
  1927. height = width / aspectRatio;
  1928. } else {
  1929. if (range.x <= 0) {
  1930. if (left > minLeft) {
  1931. width -= range.x;
  1932. left += range.x;
  1933. } else if (range.y >= 0 && bottom >= maxHeight) {
  1934. renderable = false;
  1935. }
  1936. } else {
  1937. width -= range.x;
  1938. left += range.x;
  1939. }
  1940.  
  1941. if (range.y >= 0) {
  1942. if (bottom < maxHeight) {
  1943. height += range.y;
  1944. }
  1945. } else {
  1946. height += range.y;
  1947. }
  1948. }
  1949.  
  1950. if (width < 0 && height < 0) {
  1951. action = ACTION_NORTH_EAST;
  1952. height = 0;
  1953. width = 0;
  1954. } else if (width < 0) {
  1955. action = ACTION_SOUTH_EAST;
  1956. width = 0;
  1957. } else if (height < 0) {
  1958. action = ACTION_NORTH_WEST;
  1959. height = 0;
  1960. }
  1961.  
  1962. break;
  1963.  
  1964. case ACTION_SOUTH_EAST:
  1965. if (aspectRatio) {
  1966. if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) {
  1967. renderable = false;
  1968. break;
  1969. }
  1970.  
  1971. width += range.x;
  1972. height = width / aspectRatio;
  1973. } else {
  1974. if (range.x >= 0) {
  1975. if (right < maxWidth) {
  1976. width += range.x;
  1977. } else if (range.y >= 0 && bottom >= maxHeight) {
  1978. renderable = false;
  1979. }
  1980. } else {
  1981. width += range.x;
  1982. }
  1983.  
  1984. if (range.y >= 0) {
  1985. if (bottom < maxHeight) {
  1986. height += range.y;
  1987. }
  1988. } else {
  1989. height += range.y;
  1990. }
  1991. }
  1992.  
  1993. if (width < 0 && height < 0) {
  1994. action = ACTION_NORTH_WEST;
  1995. height = 0;
  1996. width = 0;
  1997. } else if (width < 0) {
  1998. action = ACTION_SOUTH_WEST;
  1999. width = 0;
  2000. } else if (height < 0) {
  2001. action = ACTION_NORTH_EAST;
  2002. height = 0;
  2003. }
  2004.  
  2005. break;
  2006.  
  2007. // Move canvas
  2008. case ACTION_MOVE:
  2009. this.move(range.x, range.y);
  2010. renderable = false;
  2011. break;
  2012.  
  2013. // Zoom canvas
  2014. case ACTION_ZOOM:
  2015. this.zoom((function (x1, y1, x2, y2) {
  2016. var z1 = sqrt(x1 * x1 + y1 * y1);
  2017. var z2 = sqrt(x2 * x2 + y2 * y2);
  2018.  
  2019. return (z2 - z1) / z1;
  2020. })(
  2021. abs(this.startX - this.startX2),
  2022. abs(this.startY - this.startY2),
  2023. abs(this.endX - this.endX2),
  2024. abs(this.endY - this.endY2)
  2025. ), event);
  2026. this.startX2 = this.endX2;
  2027. this.startY2 = this.endY2;
  2028. renderable = false;
  2029. break;
  2030.  
  2031. // Create crop box
  2032. case ACTION_CROP:
  2033. if (!range.x || !range.y) {
  2034. renderable = false;
  2035. break;
  2036. }
  2037.  
  2038. offset = this.$cropper.offset();
  2039. left = this.startX - offset.left;
  2040. top = this.startY - offset.top;
  2041. width = cropBox.minWidth;
  2042. height = cropBox.minHeight;
  2043.  
  2044. if (range.x > 0) {
  2045. action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST;
  2046. } else if (range.x < 0) {
  2047. left -= width;
  2048. action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST;
  2049. }
  2050.  
  2051. if (range.y < 0) {
  2052. top -= height;
  2053. }
  2054.  
  2055. // Show the crop box if is hidden
  2056. if (!this.isCropped) {
  2057. this.$cropBox.removeClass(CLASS_HIDDEN);
  2058. this.isCropped = true;
  2059.  
  2060. if (this.isLimited) {
  2061. this.limitCropBox(true, true);
  2062. }
  2063. }
  2064.  
  2065. break;
  2066.  
  2067. // No default
  2068. }
  2069.  
  2070. if (renderable) {
  2071. cropBox.width = width;
  2072. cropBox.height = height;
  2073. cropBox.left = left;
  2074. cropBox.top = top;
  2075. this.action = action;
  2076.  
  2077. this.renderCropBox();
  2078. }
  2079.  
  2080. // Override
  2081. this.startX = this.endX;
  2082. this.startY = this.endY;
  2083. },
  2084.  
  2085. // Show the crop box manually
  2086. crop: function () {
  2087. if (!this.isBuilt || this.isDisabled) {
  2088. return;
  2089. }
  2090.  
  2091. if (!this.isCropped) {
  2092. this.isCropped = true;
  2093. this.limitCropBox(true, true);
  2094.  
  2095. if (this.options.modal) {
  2096. this.$dragBox.addClass(CLASS_MODAL);
  2097. }
  2098.  
  2099. this.$cropBox.removeClass(CLASS_HIDDEN);
  2100. }
  2101.  
  2102. this.setCropBoxData(this.initialCropBox);
  2103. },
  2104.  
  2105. // Reset the image and crop box to their initial states
  2106. reset: function () {
  2107. if (!this.isBuilt || this.isDisabled) {
  2108. return;
  2109. }
  2110.  
  2111. this.image = $.extend({}, this.initialImage);
  2112. this.canvas = $.extend({}, this.initialCanvas);
  2113. this.cropBox = $.extend({}, this.initialCropBox);
  2114.  
  2115. this.renderCanvas();
  2116.  
  2117. if (this.isCropped) {
  2118. this.renderCropBox();
  2119. }
  2120. },
  2121.  
  2122. // Clear the crop box
  2123. clear: function () {
  2124. if (!this.isCropped || this.isDisabled) {
  2125. return;
  2126. }
  2127.  
  2128. $.extend(this.cropBox, {
  2129. left: 0,
  2130. top: 0,
  2131. width: 0,
  2132. height: 0
  2133. });
  2134.  
  2135. this.isCropped = false;
  2136. this.renderCropBox();
  2137.  
  2138. this.limitCanvas(true, true);
  2139.  
  2140. // Render canvas after crop box rendered
  2141. this.renderCanvas();
  2142.  
  2143. this.$dragBox.removeClass(CLASS_MODAL);
  2144. this.$cropBox.addClass(CLASS_HIDDEN);
  2145. },
  2146.  
  2147. /**
  2148. * Replace the image's src and rebuild the cropper
  2149. *
  2150. * @param {String} url
  2151. * @param {Boolean} onlyColorChanged (optional)
  2152. */
  2153. replace: function (url, onlyColorChanged) {
  2154. if (!this.isDisabled && url) {
  2155. if (this.isImg) {
  2156. this.$element.attr('src', url);
  2157. }
  2158.  
  2159. if (onlyColorChanged) {
  2160. this.url = url;
  2161. this.$clone.attr('src', url);
  2162.  
  2163. if (this.isBuilt) {
  2164. this.$preview.find('img').add(this.$clone2).attr('src', url);
  2165. }
  2166. } else {
  2167. if (this.isImg) {
  2168. this.isReplaced = true;
  2169. }
  2170.  
  2171. // Clear previous data
  2172. this.options.data = null;
  2173. this.load(url);
  2174. }
  2175. }
  2176. },
  2177.  
  2178. // Enable (unfreeze) the cropper
  2179. enable: function () {
  2180. if (this.isBuilt) {
  2181. this.isDisabled = false;
  2182. this.$cropper.removeClass(CLASS_DISABLED);
  2183. }
  2184. },
  2185.  
  2186. // Disable (freeze) the cropper
  2187. disable: function () {
  2188. if (this.isBuilt) {
  2189. this.isDisabled = true;
  2190. this.$cropper.addClass(CLASS_DISABLED);
  2191. }
  2192. },
  2193.  
  2194. // Destroy the cropper and remove the instance from the image
  2195. destroy: function () {
  2196. var $this = this.$element;
  2197.  
  2198. if (this.isLoaded) {
  2199. if (this.isImg && this.isReplaced) {
  2200. $this.attr('src', this.originalUrl);
  2201. }
  2202.  
  2203. this.unbuild();
  2204. $this.removeClass(CLASS_HIDDEN);
  2205. } else {
  2206. if (this.isImg) {
  2207. $this.off(EVENT_LOAD, this.start);
  2208. } else if (this.$clone) {
  2209. this.$clone.remove();
  2210. }
  2211. }
  2212.  
  2213. $this.removeData(NAMESPACE);
  2214. },
  2215.  
  2216. /**
  2217. * Move the canvas with relative offsets
  2218. *
  2219. * @param {Number} offsetX
  2220. * @param {Number} offsetY (optional)
  2221. */
  2222. move: function (offsetX, offsetY) {
  2223. var canvas = this.canvas;
  2224.  
  2225. this.moveTo(
  2226. isUndefined(offsetX) ? offsetX : canvas.left + num(offsetX),
  2227. isUndefined(offsetY) ? offsetY : canvas.top + num(offsetY)
  2228. );
  2229. },
  2230.  
  2231. /**
  2232. * Move the canvas to an absolute point
  2233. *
  2234. * @param {Number} x
  2235. * @param {Number} y (optional)
  2236. */
  2237. moveTo: function (x, y) {
  2238. var canvas = this.canvas;
  2239. var isChanged = false;
  2240.  
  2241. // If "y" is not present, its default value is "x"
  2242. if (isUndefined(y)) {
  2243. y = x;
  2244. }
  2245.  
  2246. x = num(x);
  2247. y = num(y);
  2248.  
  2249. if (this.isBuilt && !this.isDisabled && this.options.movable) {
  2250. if (isNumber(x)) {
  2251. canvas.left = x;
  2252. isChanged = true;
  2253. }
  2254.  
  2255. if (isNumber(y)) {
  2256. canvas.top = y;
  2257. isChanged = true;
  2258. }
  2259.  
  2260. if (isChanged) {
  2261. this.renderCanvas(true);
  2262. }
  2263. }
  2264. },
  2265.  
  2266. /**
  2267. * Zoom the canvas with a relative ratio
  2268. *
  2269. * @param {Number} ratio
  2270. * @param {jQuery Event} _event (private)
  2271. */
  2272. zoom: function (ratio, _event) {
  2273. var canvas = this.canvas;
  2274.  
  2275. ratio = num(ratio);
  2276.  
  2277. if (ratio < 0) {
  2278. ratio = 1 / (1 - ratio);
  2279. } else {
  2280. ratio = 1 + ratio;
  2281. }
  2282.  
  2283. this.zoomTo(canvas.width * ratio / canvas.naturalWidth, _event);
  2284. },
  2285.  
  2286. /**
  2287. * Zoom the canvas to an absolute ratio
  2288. *
  2289. * @param {Number} ratio
  2290. * @param {jQuery Event} _event (private)
  2291. */
  2292. zoomTo: function (ratio, _event) {
  2293. var options = this.options;
  2294. var canvas = this.canvas;
  2295. var width = canvas.width;
  2296. var height = canvas.height;
  2297. var naturalWidth = canvas.naturalWidth;
  2298. var naturalHeight = canvas.naturalHeight;
  2299. var originalEvent;
  2300. var newWidth;
  2301. var newHeight;
  2302. var offset;
  2303. var center;
  2304.  
  2305. ratio = num(ratio);
  2306.  
  2307. if (ratio >= 0 && this.isBuilt && !this.isDisabled && options.zoomable) {
  2308. newWidth = naturalWidth * ratio;
  2309. newHeight = naturalHeight * ratio;
  2310.  
  2311. if (_event) {
  2312. originalEvent = _event.originalEvent;
  2313. }
  2314.  
  2315. if (this.trigger(EVENT_ZOOM, {
  2316. originalEvent: originalEvent,
  2317. oldRatio: width / naturalWidth,
  2318. ratio: newWidth / naturalWidth
  2319. }).isDefaultPrevented()) {
  2320. return;
  2321. }
  2322.  
  2323. if (originalEvent) {
  2324. offset = this.$cropper.offset();
  2325. center = originalEvent.touches ? getTouchesCenter(originalEvent.touches) : {
  2326. pageX: _event.pageX || originalEvent.pageX || 0,
  2327. pageY: _event.pageY || originalEvent.pageY || 0
  2328. };
  2329.  
  2330. // Zoom from the triggering point of the event
  2331. canvas.left -= (newWidth - width) * (
  2332. ((center.pageX - offset.left) - canvas.left) / width
  2333. );
  2334. canvas.top -= (newHeight - height) * (
  2335. ((center.pageY - offset.top) - canvas.top) / height
  2336. );
  2337. } else {
  2338.  
  2339. // Zoom from the center of the canvas
  2340. canvas.left -= (newWidth - width) / 2;
  2341. canvas.top -= (newHeight - height) / 2;
  2342. }
  2343.  
  2344. canvas.width = newWidth;
  2345. canvas.height = newHeight;
  2346. this.renderCanvas(true);
  2347. }
  2348. },
  2349.  
  2350. /**
  2351. * Rotate the canvas with a relative degree
  2352. *
  2353. * @param {Number} degree
  2354. */
  2355. rotate: function (degree) {
  2356. this.rotateTo((this.image.rotate || 0) + num(degree));
  2357. },
  2358.  
  2359. /**
  2360. * Rotate the canvas to an absolute degree
  2361. * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate()
  2362. *
  2363. * @param {Number} degree
  2364. */
  2365. rotateTo: function (degree) {
  2366. degree = num(degree);
  2367.  
  2368. if (isNumber(degree) && this.isBuilt && !this.isDisabled && this.options.rotatable) {
  2369. this.image.rotate = degree % 360;
  2370. this.isRotated = true;
  2371. this.renderCanvas(true);
  2372. }
  2373. },
  2374.  
  2375. /**
  2376. * Scale the image
  2377. * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale()
  2378. *
  2379. * @param {Number} scaleX
  2380. * @param {Number} scaleY (optional)
  2381. */
  2382. scale: function (scaleX, scaleY) {
  2383. var image = this.image;
  2384. var isChanged = false;
  2385.  
  2386. // If "scaleY" is not present, its default value is "scaleX"
  2387. if (isUndefined(scaleY)) {
  2388. scaleY = scaleX;
  2389. }
  2390.  
  2391. scaleX = num(scaleX);
  2392. scaleY = num(scaleY);
  2393.  
  2394. if (this.isBuilt && !this.isDisabled && this.options.scalable) {
  2395. if (isNumber(scaleX)) {
  2396. image.scaleX = scaleX;
  2397. isChanged = true;
  2398. }
  2399.  
  2400. if (isNumber(scaleY)) {
  2401. image.scaleY = scaleY;
  2402. isChanged = true;
  2403. }
  2404.  
  2405. if (isChanged) {
  2406. this.renderImage(true);
  2407. }
  2408. }
  2409. },
  2410.  
  2411. /**
  2412. * Scale the abscissa of the image
  2413. *
  2414. * @param {Number} scaleX
  2415. */
  2416. scaleX: function (scaleX) {
  2417. var scaleY = this.image.scaleY;
  2418.  
  2419. this.scale(scaleX, isNumber(scaleY) ? scaleY : 1);
  2420. },
  2421.  
  2422. /**
  2423. * Scale the ordinate of the image
  2424. *
  2425. * @param {Number} scaleY
  2426. */
  2427. scaleY: function (scaleY) {
  2428. var scaleX = this.image.scaleX;
  2429.  
  2430. this.scale(isNumber(scaleX) ? scaleX : 1, scaleY);
  2431. },
  2432.  
  2433. /**
  2434. * Get the cropped area position and size data (base on the original image)
  2435. *
  2436. * @param {Boolean} isRounded (optional)
  2437. * @return {Object} data
  2438. */
  2439. getData: function (isRounded) {
  2440. var options = this.options;
  2441. var image = this.image;
  2442. var canvas = this.canvas;
  2443. var cropBox = this.cropBox;
  2444. var ratio;
  2445. var data;
  2446.  
  2447. if (this.isBuilt && this.isCropped) {
  2448. data = {
  2449. x: cropBox.left - canvas.left,
  2450. y: cropBox.top - canvas.top,
  2451. width: cropBox.width,
  2452. height: cropBox.height
  2453. };
  2454.  
  2455. ratio = image.width / image.naturalWidth;
  2456.  
  2457. $.each(data, function (i, n) {
  2458. n = n / ratio;
  2459. data[i] = isRounded ? round(n) : n;
  2460. });
  2461.  
  2462. } else {
  2463. data = {
  2464. x: 0,
  2465. y: 0,
  2466. width: 0,
  2467. height: 0
  2468. };
  2469. }
  2470.  
  2471. if (options.rotatable) {
  2472. data.rotate = image.rotate || 0;
  2473. }
  2474.  
  2475. if (options.scalable) {
  2476. data.scaleX = image.scaleX || 1;
  2477. data.scaleY = image.scaleY || 1;
  2478. }
  2479.  
  2480. return data;
  2481. },
  2482.  
  2483. /**
  2484. * Set the cropped area position and size with new data
  2485. *
  2486. * @param {Object} data
  2487. */
  2488. setData: function (data) {
  2489. var options = this.options;
  2490. var image = this.image;
  2491. var canvas = this.canvas;
  2492. var cropBoxData = {};
  2493. var isRotated;
  2494. var isScaled;
  2495. var ratio;
  2496.  
  2497. if ($.isFunction(data)) {
  2498. data = data.call(this.element);
  2499. }
  2500.  
  2501. if (this.isBuilt && !this.isDisabled && $.isPlainObject(data)) {
  2502. if (options.rotatable) {
  2503. if (isNumber(data.rotate) && data.rotate !== image.rotate) {
  2504. image.rotate = data.rotate;
  2505. this.isRotated = isRotated = true;
  2506. }
  2507. }
  2508.  
  2509. if (options.scalable) {
  2510. if (isNumber(data.scaleX) && data.scaleX !== image.scaleX) {
  2511. image.scaleX = data.scaleX;
  2512. isScaled = true;
  2513. }
  2514.  
  2515. if (isNumber(data.scaleY) && data.scaleY !== image.scaleY) {
  2516. image.scaleY = data.scaleY;
  2517. isScaled = true;
  2518. }
  2519. }
  2520.  
  2521. if (isRotated) {
  2522. this.renderCanvas();
  2523. } else if (isScaled) {
  2524. this.renderImage();
  2525. }
  2526.  
  2527. ratio = image.width / image.naturalWidth;
  2528.  
  2529. if (isNumber(data.x)) {
  2530. cropBoxData.left = data.x * ratio + canvas.left;
  2531. }
  2532.  
  2533. if (isNumber(data.y)) {
  2534. cropBoxData.top = data.y * ratio + canvas.top;
  2535. }
  2536.  
  2537. if (isNumber(data.width)) {
  2538. cropBoxData.width = data.width * ratio;
  2539. }
  2540.  
  2541. if (isNumber(data.height)) {
  2542. cropBoxData.height = data.height * ratio;
  2543. }
  2544.  
  2545. this.setCropBoxData(cropBoxData);
  2546. }
  2547. },
  2548.  
  2549. /**
  2550. * Get the container size data
  2551. *
  2552. * @return {Object} data
  2553. */
  2554. getContainerData: function () {
  2555. return this.isBuilt ? this.container : {};
  2556. },
  2557.  
  2558. /**
  2559. * Get the image position and size data
  2560. *
  2561. * @return {Object} data
  2562. */
  2563. getImageData: function () {
  2564. return this.isLoaded ? this.image : {};
  2565. },
  2566.  
  2567. /**
  2568. * Get the canvas position and size data
  2569. *
  2570. * @return {Object} data
  2571. */
  2572. getCanvasData: function () {
  2573. var canvas = this.canvas;
  2574. var data = {};
  2575.  
  2576. if (this.isBuilt) {
  2577. $.each([
  2578. 'left',
  2579. 'top',
  2580. 'width',
  2581. 'height',
  2582. 'naturalWidth',
  2583. 'naturalHeight'
  2584. ], function (i, n) {
  2585. data[n] = canvas[n];
  2586. });
  2587. }
  2588.  
  2589. return data;
  2590. },
  2591.  
  2592. /**
  2593. * Set the canvas position and size with new data
  2594. *
  2595. * @param {Object} data
  2596. */
  2597. setCanvasData: function (data) {
  2598. var canvas = this.canvas;
  2599. var aspectRatio = canvas.aspectRatio;
  2600.  
  2601. if ($.isFunction(data)) {
  2602. data = data.call(this.$element);
  2603. }
  2604.  
  2605. if (this.isBuilt && !this.isDisabled && $.isPlainObject(data)) {
  2606. if (isNumber(data.left)) {
  2607. canvas.left = data.left;
  2608. }
  2609.  
  2610. if (isNumber(data.top)) {
  2611. canvas.top = data.top;
  2612. }
  2613.  
  2614. if (isNumber(data.width)) {
  2615. canvas.width = data.width;
  2616. canvas.height = data.width / aspectRatio;
  2617. } else if (isNumber(data.height)) {
  2618. canvas.height = data.height;
  2619. canvas.width = data.height * aspectRatio;
  2620. }
  2621.  
  2622. this.renderCanvas(true);
  2623. }
  2624. },
  2625.  
  2626. /**
  2627. * Get the crop box position and size data
  2628. *
  2629. * @return {Object} data
  2630. */
  2631. getCropBoxData: function () {
  2632. var cropBox = this.cropBox;
  2633. var data;
  2634.  
  2635. if (this.isBuilt && this.isCropped) {
  2636. data = {
  2637. left: cropBox.left,
  2638. top: cropBox.top,
  2639. width: cropBox.width,
  2640. height: cropBox.height
  2641. };
  2642. }
  2643.  
  2644. return data || {};
  2645. },
  2646.  
  2647. /**
  2648. * Set the crop box position and size with new data
  2649. *
  2650. * @param {Object} data
  2651. */
  2652. setCropBoxData: function (data) {
  2653. var cropBox = this.cropBox;
  2654. var aspectRatio = this.options.aspectRatio;
  2655. var isWidthChanged;
  2656. var isHeightChanged;
  2657.  
  2658. if ($.isFunction(data)) {
  2659. data = data.call(this.$element);
  2660. }
  2661.  
  2662. if (this.isBuilt && this.isCropped && !this.isDisabled && $.isPlainObject(data)) {
  2663.  
  2664. if (isNumber(data.left)) {
  2665. cropBox.left = data.left;
  2666. }
  2667.  
  2668. if (isNumber(data.top)) {
  2669. cropBox.top = data.top;
  2670. }
  2671.  
  2672. if (isNumber(data.width)) {
  2673. isWidthChanged = true;
  2674. cropBox.width = data.width;
  2675. }
  2676.  
  2677. if (isNumber(data.height)) {
  2678. isHeightChanged = true;
  2679. cropBox.height = data.height;
  2680. }
  2681.  
  2682. if (aspectRatio) {
  2683. if (isWidthChanged) {
  2684. cropBox.height = cropBox.width / aspectRatio;
  2685. } else if (isHeightChanged) {
  2686. cropBox.width = cropBox.height * aspectRatio;
  2687. }
  2688. }
  2689.  
  2690. this.renderCropBox();
  2691. }
  2692. },
  2693.  
  2694. /**
  2695. * Get a canvas drawn the cropped image
  2696. *
  2697. * @param {Object} options (optional)
  2698. * @return {HTMLCanvasElement} canvas
  2699. */
  2700. getCroppedCanvas: function (options) {
  2701. var originalWidth;
  2702. var originalHeight;
  2703. var canvasWidth;
  2704. var canvasHeight;
  2705. var scaledWidth;
  2706. var scaledHeight;
  2707. var scaledRatio;
  2708. var aspectRatio;
  2709. var canvas;
  2710. var context;
  2711. var data;
  2712.  
  2713. if (!this.isBuilt || !SUPPORT_CANVAS) {
  2714. return;
  2715. }
  2716.  
  2717. if (!this.isCropped) {
  2718. return getSourceCanvas(this.$clone[0], this.image);
  2719. }
  2720.  
  2721. if (!$.isPlainObject(options)) {
  2722. options = {};
  2723. }
  2724.  
  2725. data = this.getData();
  2726. originalWidth = data.width;
  2727. originalHeight = data.height;
  2728. aspectRatio = originalWidth / originalHeight;
  2729.  
  2730. if ($.isPlainObject(options)) {
  2731. scaledWidth = options.width;
  2732. scaledHeight = options.height;
  2733.  
  2734. if (scaledWidth) {
  2735. scaledHeight = scaledWidth / aspectRatio;
  2736. scaledRatio = scaledWidth / originalWidth;
  2737. } else if (scaledHeight) {
  2738. scaledWidth = scaledHeight * aspectRatio;
  2739. scaledRatio = scaledHeight / originalHeight;
  2740. }
  2741. }
  2742.  
  2743. // The canvas element will use `Math.floor` on a float number, so floor first
  2744. canvasWidth = floor(scaledWidth || originalWidth);
  2745. canvasHeight = floor(scaledHeight || originalHeight);
  2746.  
  2747. canvas = $('<canvas>')[0];
  2748. canvas.width = canvasWidth;
  2749. canvas.height = canvasHeight;
  2750. context = canvas.getContext('2d');
  2751.  
  2752. if (options.fillColor) {
  2753. context.fillStyle = options.fillColor;
  2754. context.fillRect(0, 0, canvasWidth, canvasHeight);
  2755. }
  2756.  
  2757. // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage
  2758. context.drawImage.apply(context, (function () {
  2759. var source = getSourceCanvas(this.$clone[0], this.image);
  2760. var sourceWidth = source.width;
  2761. var sourceHeight = source.height;
  2762. var canvas = this.canvas;
  2763. var params = [source];
  2764.  
  2765. // Source canvas
  2766. var srcX = data.x + canvas.naturalWidth * (abs(data.scaleX || 1) - 1) / 2;
  2767. var srcY = data.y + canvas.naturalHeight * (abs(data.scaleY || 1) - 1) / 2;
  2768. var srcWidth;
  2769. var srcHeight;
  2770.  
  2771. // Destination canvas
  2772. var dstX;
  2773. var dstY;
  2774. var dstWidth;
  2775. var dstHeight;
  2776.  
  2777. if (srcX <= -originalWidth || srcX > sourceWidth) {
  2778. srcX = srcWidth = dstX = dstWidth = 0;
  2779. } else if (srcX <= 0) {
  2780. dstX = -srcX;
  2781. srcX = 0;
  2782. srcWidth = dstWidth = min(sourceWidth, originalWidth + srcX);
  2783. } else if (srcX <= sourceWidth) {
  2784. dstX = 0;
  2785. srcWidth = dstWidth = min(originalWidth, sourceWidth - srcX);
  2786. }
  2787.  
  2788. if (srcWidth <= 0 || srcY <= -originalHeight || srcY > sourceHeight) {
  2789. srcY = srcHeight = dstY = dstHeight = 0;
  2790. } else if (srcY <= 0) {
  2791. dstY = -srcY;
  2792. srcY = 0;
  2793. srcHeight = dstHeight = min(sourceHeight, originalHeight + srcY);
  2794. } else if (srcY <= sourceHeight) {
  2795. dstY = 0;
  2796. srcHeight = dstHeight = min(originalHeight, sourceHeight - srcY);
  2797. }
  2798.  
  2799. // All the numerical parameters should be integer for `drawImage` (#476)
  2800. params.push(floor(srcX), floor(srcY), floor(srcWidth), floor(srcHeight));
  2801.  
  2802. // Scale destination sizes
  2803. if (scaledRatio) {
  2804. dstX *= scaledRatio;
  2805. dstY *= scaledRatio;
  2806. dstWidth *= scaledRatio;
  2807. dstHeight *= scaledRatio;
  2808. }
  2809.  
  2810. // Avoid "IndexSizeError" in IE and Firefox
  2811. if (dstWidth > 0 && dstHeight > 0) {
  2812. params.push(floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight));
  2813. }
  2814.  
  2815. return params;
  2816. }).call(this));
  2817.  
  2818. return canvas;
  2819. },
  2820.  
  2821. /**
  2822. * Change the aspect ratio of the crop box
  2823. *
  2824. * @param {Number} aspectRatio
  2825. */
  2826. setAspectRatio: function (aspectRatio) {
  2827. var options = this.options;
  2828.  
  2829. if (!this.isDisabled && !isUndefined(aspectRatio)) {
  2830.  
  2831. // 0 -> NaN
  2832. options.aspectRatio = max(0, aspectRatio) || NaN;
  2833.  
  2834. if (this.isBuilt) {
  2835. this.initCropBox();
  2836.  
  2837. if (this.isCropped) {
  2838. this.renderCropBox();
  2839. }
  2840. }
  2841. }
  2842. },
  2843.  
  2844. /**
  2845. * Change the drag mode
  2846. *
  2847. * @param {String} mode (optional)
  2848. */
  2849. setDragMode: function (mode) {
  2850. var options = this.options;
  2851. var croppable;
  2852. var movable;
  2853.  
  2854. if (this.isLoaded && !this.isDisabled) {
  2855. croppable = mode === ACTION_CROP;
  2856. movable = options.movable && mode === ACTION_MOVE;
  2857. mode = (croppable || movable) ? mode : ACTION_NONE;
  2858.  
  2859. this.$dragBox.
  2860. data(DATA_ACTION, mode).
  2861. toggleClass(CLASS_CROP, croppable).
  2862. toggleClass(CLASS_MOVE, movable);
  2863.  
  2864. if (!options.cropBoxMovable) {
  2865.  
  2866. // Sync drag mode to crop box when it is not movable(#300)
  2867. this.$face.
  2868. data(DATA_ACTION, mode).
  2869. toggleClass(CLASS_CROP, croppable).
  2870. toggleClass(CLASS_MOVE, movable);
  2871. }
  2872. }
  2873. }
  2874. };
  2875.  
  2876. Cropper.DEFAULTS = {
  2877.  
  2878. // Define the view mode of the cropper
  2879. viewMode: 0, // 0, 1, 2, 3
  2880.  
  2881. // Define the dragging mode of the cropper
  2882. dragMode: 'crop', // 'crop', 'move' or 'none'
  2883.  
  2884. // Define the aspect ratio of the crop box
  2885. aspectRatio: NaN,
  2886.  
  2887. // An object with the previous cropping result data
  2888. data: null,
  2889.  
  2890. // A jQuery selector for adding extra containers to preview
  2891. preview: '',
  2892.  
  2893. // Re-render the cropper when resize the window
  2894. responsive: true,
  2895.  
  2896. // Restore the cropped area after resize the window
  2897. restore: true,
  2898.  
  2899. // Check if the current image is a cross-origin image
  2900. checkCrossOrigin: true,
  2901.  
  2902. // Check the current image's Exif Orientation information
  2903. checkOrientation: true,
  2904.  
  2905. // Show the black modal
  2906. modal: true,
  2907.  
  2908. // Show the dashed lines for guiding
  2909. guides: true,
  2910.  
  2911. // Show the center indicator for guiding
  2912. center: true,
  2913.  
  2914. // Show the white modal to highlight the crop box
  2915. highlight: true,
  2916.  
  2917. // Show the grid background
  2918. background: true,
  2919.  
  2920. // Enable to crop the image automatically when initialize
  2921. autoCrop: true,
  2922.  
  2923. // Define the percentage of automatic cropping area when initializes
  2924. autoCropArea: 0.8,
  2925.  
  2926. // Enable to move the image
  2927. movable: true,
  2928.  
  2929. // Enable to rotate the image
  2930. rotatable: true,
  2931.  
  2932. // Enable to scale the image
  2933. scalable: true,
  2934.  
  2935. // Enable to zoom the image
  2936. zoomable: true,
  2937.  
  2938. // Enable to zoom the image by dragging touch
  2939. zoomOnTouch: true,
  2940.  
  2941. // Enable to zoom the image by wheeling mouse
  2942. zoomOnWheel: true,
  2943.  
  2944. // Define zoom ratio when zoom the image by wheeling mouse
  2945. wheelZoomRatio: 0.1,
  2946.  
  2947. // Enable to move the crop box
  2948. cropBoxMovable: true,
  2949.  
  2950. // Enable to resize the crop box
  2951. cropBoxResizable: true,
  2952.  
  2953. // Toggle drag mode between "crop" and "move" when click twice on the cropper
  2954. toggleDragModeOnDblclick: true,
  2955.  
  2956. // Size limitation
  2957. minCanvasWidth: 0,
  2958. minCanvasHeight: 0,
  2959. minCropBoxWidth: 0,
  2960. minCropBoxHeight: 0,
  2961. minContainerWidth: 200,
  2962. minContainerHeight: 100,
  2963.  
  2964. // Shortcuts of events
  2965. build: null,
  2966. built: null,
  2967. cropstart: null,
  2968. cropmove: null,
  2969. cropend: null,
  2970. crop: null,
  2971. zoom: null
  2972. };
  2973.  
  2974. Cropper.setDefaults = function (options) {
  2975. $.extend(Cropper.DEFAULTS, options);
  2976. };
  2977.  
  2978. Cropper.TEMPLATE = (
  2979. '<div class="cropper-container">' +
  2980. '<div class="cropper-wrap-box">' +
  2981. '<div class="cropper-canvas"></div>' +
  2982. '</div>' +
  2983. '<div class="cropper-drag-box"></div>' +
  2984.  
  2985. '</div>'
  2986. );
  2987.  
  2988. // Save the other cropper
  2989. Cropper.other = $.fn.cropper;
  2990.  
  2991. // Register as jQuery plugin
  2992. $.fn.cropper = function (option) {
  2993. var args = toArray(arguments, 1);
  2994. var result;
  2995.  
  2996. this.each(function () {
  2997. var $this = $(this);
  2998. var data = $this.data(NAMESPACE);
  2999. var options;
  3000. var fn;
  3001.  
  3002. if (!data) {
  3003. if (/destroy/.test(option)) {
  3004. return;
  3005. }
  3006.  
  3007. options = $.extend({}, $this.data(), $.isPlainObject(option) && option);
  3008. $this.data(NAMESPACE, (data = new Cropper(this, options)));
  3009. }
  3010.  
  3011. if (typeof option === 'string' && $.isFunction(fn = data[option])) {
  3012. result = fn.apply(data, args);
  3013. }
  3014. });
  3015.  
  3016. return isUndefined(result) ? this : result;
  3017. };
  3018.  
  3019. $.fn.cropper.Constructor = Cropper;
  3020. $.fn.cropper.setDefaults = Cropper.setDefaults;
  3021.  
  3022. // No conflict
  3023. $.fn.cropper.noConflict = function () {
  3024. $.fn.cropper = Cropper.other;
  3025. return this;
  3026. };
  3027.  
  3028. });

完成。

HTML5 canvas处理图片的各种效果,包括放大缩小涂鸦等的更多相关文章

  1. 如何使用 HTML5 Canvas 制作水波纹效果

    今天,我们继续分享 JavaScript 实现的效果例子,这篇文章会介绍使用 JavaScript 实现水波纹效果.水波效果以图片为背景,点击图片任意位置都会触发.有时候,我们使用普通的 Javasc ...

  2. 基于HTML5 Canvas可撕裂布料效果

    分享一款布料效果的 HTML5 Canvas 应用演示,效果逼真.你会看到,借助 Canvas 的强大绘图和动画功能,只需很少的代码就能实现让您屏息凝神的效果. 在线预览   源码下载 实现的代码. ...

  3. HTML5 canvas炫酷棱镜效果的幻灯片特效

    这是一款效果很炫酷华丽的HTML5 canvas带棱镜效果的幻灯片特效. 这个特效在每个幻灯片的前面放置一个图形.并将图形制作为三棱镜效果.它底下的幻灯片图片会被"折射"到棱镜上面 ...

  4. 八大疯狂的HTML5 Canvas及WebGL动画效果——8 CRAZY ANIMATIONS WITH WEBGL AND HTML5 CANVAS【收藏】

    HTML5, WebGL and Javascript have changed the way animation used to be. Past few years, we can only a ...

  5. [js高手之路]html5 canvas动画教程 - 下雪效果

    利用canvas,实现一个下雪的效果,我们先预览下效果: 我们先分析下这个效果: 1,随机产生雪花 2,雪花的产生不是同时产生,而是有先后顺序的 3,雪花怎么表示 4,怎么源源不断的下雪 5,雪花有大 ...

  6. 基于HTML5 Canvas 实现的 Loading 效果

    Sonic.js 是一个很小的 JavaScript 类,用于创建基于 HTML5 画布的加载图像.更强大的是 Sonic.js 还提供了基于现成的例子的创建工具,可以帮助你实现更多自定义的(Load ...

  7. HTML5 Canvas绘制的下雪效果

    在HTML页面的HEAD区域直接引入snow.js即可,如下:<script type="text/javascript" src="js/snow.js" ...

  8. HTML5:使用Canvas和Input range控件放大缩小图片,剪裁,并上传图片

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  9. 基于HTML5 Canvas生成粒子效果的人物头像

    前面我们分享过一个HTML5 Canvas实现的图像马赛克模糊效果,HTML5处理图片真的非常简单.今天我们要再利用HTML5 Canvas实现一个粒子效果的人物头像,你可以任意选择一张头像图片,接下 ...

随机推荐

  1. docker-freebsd-20150625

    http://www.docker.org.cn/book/docker/prepare-docker-1.html https://wiki.freebsd.org/Docker pkg insta ...

  2. yum安装配置

    1.删除redhat原有的yum rpm -aq|grep yum|xargs rpm -e --nodeps 2.下载yum安装文件 http://mirrors.163.com/centos/6/ ...

  3. Python开发【前端】:汇总

    页面模板 1.EasyUI(推荐指数★) JQuery EasyUI中文网 下载 使用方法:把文件下载到本地.直接从官网上把源码拷贝过来,更改下js的路径即可 优点:功能非常多.非常齐全 偏做后台管理 ...

  4. Android Studio中获取查看签名SHA1证书指纹数据或MD5的方法

    原来在Eclipse中获取SHA1或者MD5,在IDE界面上就可以查找到. 切换到Android Studio后,如何查看呢?找了半天没找到.那就老办法命令行. 第一步.打开Android Studi ...

  5. docker 学习

    vim /usr/lib/systemd/system/docker.service ExecStart=/usr/bin/docker daemon --bip=172.18.42.1/16 --r ...

  6. iOS 版 MWeb 发布到自建 Wordpress 和 Metaweblog API 使用指南

    MWeb 的发布服务的使用方法是先增加发布服务,再使用.在 iOS 中,要增加发布服务,可以在首页中,点左上角的 "设置" 按钮,进入设置界面,并滑动到底部,就会看到增加发布服务的 ...

  7. ibatis输入多个参数

    ibatis输入多个参数     在ibatis中,会发现其输入参数只能有一个,于是当出现需要进行多个输入参数的时候,就要想点办法了,我看到的有以下两种比较好的方法能够解决这个问题1) 用String ...

  8. Quartus II USB-Blaster驱动解决

    Quartus II USB-Blaster驱动解决 之前安装Quartus II 13.0,但FPGA开发板链接的USB-Blaster链接无法被Quartus识别,改装Quartus II 11. ...

  9. Windows Phone 十六、HttpClient

    HttpClient 对象也可以实现网络请求 相对于 HttpWebRequest 对象来说,HttpClient 操作更简单,功能更强大 HttpClient 提供一系列比较简单的API来实现基本的 ...

  10. 夺命雷公狗-----React---24--小案例之react经典案例todos(单条任务的删除)

    我们的组建分析图 我们组建需要的是删除,数据流方式如下所示: 为了更方便下一步操作,先写个函数他 然后在Ul组建里面对她进行处理 然后在Zong组建里对数据进行处理,如下所示: 但是悲剧的一幕出现了, ...