jquery.cropbox.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. (function() {
  2. // helper functions
  3. function is_touch_device() {
  4. return 'ontouchstart' in window || // works on most browsers
  5. 'onmsgesturechange' in window; // works on ie10
  6. }
  7. function fill(value, target, container) {
  8. if (value + target < container)
  9. value = container - target;
  10. return value > 0 ? 0 : value;
  11. }
  12. function uri2blob(dataURI) {
  13. var uriComponents = dataURI.split(',');
  14. var byteString = atob(uriComponents[1]);
  15. var mimeString = uriComponents[0].split(':')[1].split(';')[0];
  16. var ab = new ArrayBuffer(byteString.length);
  17. var ia = new Uint8Array(ab);
  18. for (var i = 0; i < byteString.length; i++)
  19. ia[i] = byteString.charCodeAt(i);
  20. return new Blob([ab], { type: mimeString });
  21. }
  22. var pluginName = 'cropbox';
  23. function factory($) {
  24. function Crop($image, options) {
  25. this.width = null;
  26. this.height = null;
  27. this.img_width = null;
  28. this.img_height = null;
  29. this.img_left = 0;
  30. this.img_top = 0;
  31. this.minPercent = null;
  32. this.options = options;
  33. this.$image = $image;
  34. this.$image.hide().prop('draggable', false).addClass('cropImage').wrap('<div class="cropFrame" />'); // wrap image in frame;
  35. this.$frame = this.$image.parent();
  36. this.init();
  37. }
  38. Crop.prototype = {
  39. init: function () {
  40. var self = this;
  41. var defaultControls = $('<div/>', { 'class' : 'cropControls' })
  42. .append($('<span>拖动选择合适区域</span>'))
  43. .append($('<a/>', { 'class' : 'cropZoomIn' }).on('click', $.proxy(this.zoomIn, this)))
  44. .append($('<a/>', { 'class' : 'cropZoomOut' }).on('click', $.proxy(this.zoomOut, this)));
  45. this.$frame.append(this.options.controls || defaultControls);
  46. this.updateOptions();
  47. if (typeof $.fn.hammer === 'function' || typeof Hammer !== 'undefined') {
  48. var hammerit, dragData;
  49. if (typeof $.fn.hammer === 'function')
  50. hammerit = this.$image.hammer();
  51. else
  52. hammerit = Hammer(this.$image.get(0));
  53. hammerit.on('touch', function(e) {
  54. e.gesture.preventDefault();
  55. }).on("dragleft dragright dragup dragdown", function(e) {
  56. if (!dragData)
  57. dragData = {
  58. startX: self.img_left,
  59. startY: self.img_top,
  60. };
  61. dragData.dx = e.gesture.deltaX;
  62. dragData.dy = e.gesture.deltaY;
  63. e.gesture.preventDefault();
  64. e.gesture.stopPropagation();
  65. self.drag.call(self, dragData, true);
  66. }).on('release', function(e) {
  67. e.gesture.preventDefault();
  68. dragData = null;
  69. self.update.call(self);
  70. }).on('doubletap', function(e) {
  71. e.gesture.preventDefault();
  72. self.zoomIn.call(self);
  73. }).on('pinchin', function (e) {
  74. e.gesture.preventDefault();
  75. self.zoomOut.call(self);
  76. }).on('pinchout', function (e) {
  77. e.gesture.preventDefault();
  78. self.zoomIn.call(self);
  79. });
  80. } else {
  81. this.$image.on('mousedown.' + pluginName, function(e1) {
  82. var dragData = {
  83. startX: self.img_left,
  84. startY: self.img_top,
  85. };
  86. e1.preventDefault();
  87. $(document).on('mousemove.' + pluginName, function (e2) {
  88. dragData.dx = e2.pageX - e1.pageX;
  89. dragData.dy = e2.pageY - e1.pageY;
  90. self.drag.call(self, dragData, true);
  91. }).on('mouseup.' + pluginName, function() {
  92. self.update.call(self);
  93. $(document).off('mouseup.' + pluginName);
  94. $(document).off('mousemove.' + pluginName);
  95. });
  96. });
  97. }
  98. if ($.fn.mousewheel) {
  99. this.$image.on('mousewheel.' + pluginName, function (e) {
  100. e.preventDefault();
  101. if (e.deltaY < 0)
  102. self.zoomIn.call(self);
  103. else
  104. self.zoomOut.call(self);
  105. });
  106. }
  107. },
  108. updateOptions: function () {
  109. var self = this;
  110. self.img_top = 0;
  111. self.img_left = 0;
  112. self.$image.css({width: '', left: self.img_left, top: self.img_top});
  113. self.$frame.width(self.options.width).height(self.options.height);
  114. self.$frame.off('.' + pluginName);
  115. self.$frame.removeClass('hover');
  116. if (self.options.showControls === 'always' || self.options.showControls === 'auto' && is_touch_device())
  117. self.$frame.addClass('hover');
  118. else if (self.options.showControls !== 'never') {
  119. self.$frame.on('mouseenter.' + pluginName, function () { self.$frame.addClass('hover'); });
  120. self.$frame.on('mouseleave.' + pluginName, function () { self.$frame.removeClass('hover'); });
  121. }
  122. // Image hack to get width and height on IE
  123. var img = new Image();
  124. img.src = self.$image.attr('src');
  125. img.onload = function () {
  126. self.width = img.width;
  127. self.height = img.height;
  128. img.src = '';
  129. img.onload = null;
  130. self.percent = undefined;
  131. self.fit.call(self);
  132. if (self.options.result)
  133. self.setCrop.call(self, self.options.result);
  134. else
  135. self.zoom.call(self, self.minPercent);
  136. self.$image.fadeIn('fast');
  137. };
  138. },
  139. remove: function () {
  140. var hammerit;
  141. if (typeof $.fn.hammer === 'function')
  142. hammerit = this.$image.hammer();
  143. else if (typeof Hammer !== 'undefined')
  144. hammerit = Hammer(this.$image.get(0));
  145. if (hammerit)
  146. hammerit.off('mousedown dragleft dragright dragup dragdown release doubletap pinchin pinchout');
  147. this.$frame.off('.' + pluginName);
  148. this.$image.off('.' + pluginName);
  149. this.$image.css({width: '', left: '', top: ''});
  150. this.$image.removeClass('cropImage');
  151. this.$image.removeData('cropbox');
  152. this.$image.insertAfter(this.$frame);
  153. this.$frame.removeClass('cropFrame');
  154. this.$frame.removeAttr('style');
  155. this.$frame.empty();
  156. this.$frame.hide();
  157. },
  158. fit: function () {
  159. var widthRatio = this.options.width / this.width,
  160. heightRatio = this.options.height / this.height;
  161. this.minPercent = (widthRatio >= heightRatio) ? widthRatio : heightRatio;
  162. },
  163. setCrop: function (result) {
  164. this.percent = Math.max(this.options.width/result.cropW, this.options.height/result.cropH);
  165. this.img_width = Math.ceil(this.width*this.percent);
  166. this.img_height = Math.ceil(this.height*this.percent);
  167. this.img_left = -Math.floor(result.cropX*this.percent);
  168. this.img_top = -Math.floor(result.cropY*this.percent);
  169. this.$image.css({ width: this.img_width, left: this.img_left, top: this.img_top });
  170. this.update();
  171. },
  172. zoom: function(percent) {
  173. var old_percent = this.percent;
  174. this.percent = Math.max(this.minPercent, Math.min(this.options.maxZoom, percent));
  175. this.img_width = Math.ceil(this.width * this.percent);
  176. this.img_height = Math.ceil(this.height * this.percent);
  177. if (old_percent) {
  178. var zoomFactor = this.percent / old_percent;
  179. this.img_left = fill((1 - zoomFactor) * this.options.width / 2 + zoomFactor * this.img_left, this.img_width, this.options.width);
  180. this.img_top = fill((1 - zoomFactor) * this.options.height / 2 + zoomFactor * this.img_top, this.img_height, this.options.height);
  181. } else {
  182. this.img_left = fill((this.options.width - this.img_width) / 2, this.img_width, this.options.width);
  183. this.img_top = fill((this.options.height - this.img_height) / 2, this.img_height, this.options.height);
  184. }
  185. this.$image.css({ width: this.img_width, left: this.img_left, top: this.img_top });
  186. this.update();
  187. },
  188. zoomIn: function() {
  189. this.zoom(this.percent + (1 - this.minPercent) / (this.options.zoom - 1 || 1));
  190. },
  191. zoomOut: function() {
  192. this.zoom(this.percent - (1 - this.minPercent) / (this.options.zoom - 1 || 1));
  193. },
  194. drag: function(data, skipupdate) {
  195. this.img_left = fill(data.startX + data.dx, this.img_width, this.options.width);
  196. this.img_top = fill(data.startY + data.dy, this.img_height, this.options.height);
  197. this.$image.css({ left: this.img_left, top: this.img_top });
  198. if (skipupdate)
  199. this.update();
  200. },
  201. update: function() {
  202. this.result = {
  203. cropX: -Math.ceil(this.img_left / this.percent),
  204. cropY: -Math.ceil(this.img_top / this.percent),
  205. cropW: Math.floor(this.options.width / this.percent),
  206. cropH: Math.floor(this.options.height / this.percent),
  207. stretch: this.minPercent > 1
  208. };
  209. this.$image.trigger(pluginName, [this.result, this]);
  210. },
  211. getDataURL: function () {
  212. var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d');
  213. canvas.width = this.options.width;
  214. canvas.height = this.options.height;
  215. ctx.drawImage(this.$image.get(0), this.result.cropX, this.result.cropY, this.result.cropW, this.result.cropH, 0, 0, this.options.width, this.options.height);
  216. return canvas.toDataURL();
  217. },
  218. getBlob: function () {
  219. return uri2blob(this.getDataURL());
  220. },
  221. };
  222. $.fn[pluginName] = function(options) {
  223. return this.each(function() {
  224. var inst = $.data(this, pluginName);
  225. if (!inst) {
  226. var opts = $.extend({}, $.fn[pluginName].defaultOptions, options);
  227. $.data(this, pluginName, new Crop($(this), opts));
  228. } else if (options) {
  229. $.extend(inst.options, options);
  230. inst.updateOptions();
  231. }
  232. });
  233. };
  234. $.fn[pluginName].defaultOptions = {
  235. width: 200,
  236. height: 200,
  237. zoom: 10,
  238. maxZoom: 1,
  239. controls: null,
  240. showControls: 'auto'
  241. };
  242. }
  243. if (typeof require === "function" && typeof exports === "object" && typeof module === "object")
  244. factory(require("jquery"));
  245. else if (typeof define === "function" && define.amd)
  246. define(["jquery"], factory);
  247. else
  248. factory(window.jQuery || window.Zepto);
  249. })();