blob: df02483861703559b7735b11e26d8ebbbff76c1a [file] [log] [blame]
guangchao.xu070005a2020-12-07 09:56:40 +08001/**
2 * we-cropper v1.3.9
3 * (c) 2020 dlhandsome
4 * @license MIT
5 */
6(function(global, factory) {
7 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
8 typeof define === 'function' && define.amd ? define(factory) :
9 (global.WeCropper = factory());
10}(this, (function() {
11 'use strict';
12
13 var device = void 0;
14 var TOUCH_STATE = ['touchstarted', 'touchmoved', 'touchended'];
15
16 function firstLetterUpper(str) {
17 return str.charAt(0).toUpperCase() + str.slice(1)
18 }
19
20 function setTouchState(instance) {
21 var arg = [],
22 len = arguments.length - 1;
23 while (len-- > 0) arg[len] = arguments[len + 1];
24
25 TOUCH_STATE.forEach(function(key, i) {
26 if (arg[i] !== undefined) {
27 instance[key] = arg[i];
28 }
29 });
30 }
31
32 function validator(instance, o) {
33 Object.defineProperties(instance, o);
34 }
35
36 function getDevice() {
37 if (!device) {
38 device = uni.getSystemInfoSync();
39 }
40 return device
41 }
42
43 var tmp = {};
44
45 var ref = getDevice();
46 var pixelRatio = ref.pixelRatio;
47
48 var DEFAULT = {
49 id: {
50 default: 'cropper',
51 get: function get() {
52 return tmp.id
53 },
54 set: function set(value) {
55 if (typeof(value) !== 'string') {
56 console.error(("id:" + value + " is invalid"));
57 }
58 tmp.id = value;
59 }
60 },
61 width: {
62 default: 750,
63 get: function get() {
64 return tmp.width
65 },
66 set: function set(value) {
67 if (typeof(value) !== 'number') {
68 console.error(("width:" + value + " is invalid"));
69 }
70 tmp.width = value;
71 }
72 },
73 height: {
74 default: 750,
75 get: function get() {
76 return tmp.height
77 },
78 set: function set(value) {
79 if (typeof(value) !== 'number') {
80 console.error(("height:" + value + " is invalid"));
81 }
82 tmp.height = value;
83 }
84 },
85 pixelRatio: {
86 default: pixelRatio,
87 get: function get() {
88 return tmp.pixelRatio
89 },
90 set: function set(value) {
91 if (typeof(value) !== 'number') {
92 console.error(("pixelRatio:" + value + " is invalid"));
93 }
94 tmp.pixelRatio = value;
95 }
96 },
97 scale: {
98 default: 2.5,
99 get: function get() {
100 return tmp.scale
101 },
102 set: function set(value) {
103 if (typeof(value) !== 'number') {
104 console.error(("scale:" + value + " is invalid"));
105 }
106 tmp.scale = value;
107 }
108 },
109 zoom: {
110 default: 5,
111 get: function get() {
112 return tmp.zoom
113 },
114 set: function set(value) {
115 if (typeof(value) !== 'number') {
116 console.error(("zoom:" + value + " is invalid"));
117 } else if (value < 0 || value > 10) {
118 console.error("zoom should be ranged in 0 ~ 10");
119 }
120 tmp.zoom = value;
121 }
122 },
123 src: {
124 default: '',
125 get: function get() {
126 return tmp.src
127 },
128 set: function set(value) {
129 if (typeof(value) !== 'string') {
130 console.error(("src:" + value + " is invalid"));
131 }
132 tmp.src = value;
133 }
134 },
135 cut: {
136 default: {},
137 get: function get() {
138 return tmp.cut
139 },
140 set: function set(value) {
141 if (typeof(value) !== 'object') {
142 console.error(("cut:" + value + " is invalid"));
143 }
144 tmp.cut = value;
145 }
146 },
147 boundStyle: {
148 default: {},
149 get: function get() {
150 return tmp.boundStyle
151 },
152 set: function set(value) {
153 if (typeof(value) !== 'object') {
154 console.error(("boundStyle:" + value + " is invalid"));
155 }
156 tmp.boundStyle = value;
157 }
158 },
159 onReady: {
160 default: null,
161 get: function get() {
162 return tmp.ready
163 },
164 set: function set(value) {
165 tmp.ready = value;
166 }
167 },
168 onBeforeImageLoad: {
169 default: null,
170 get: function get() {
171 return tmp.beforeImageLoad
172 },
173 set: function set(value) {
174 tmp.beforeImageLoad = value;
175 }
176 },
177 onImageLoad: {
178 default: null,
179 get: function get() {
180 return tmp.imageLoad
181 },
182 set: function set(value) {
183 tmp.imageLoad = value;
184 }
185 },
186 onBeforeDraw: {
187 default: null,
188 get: function get() {
189 return tmp.beforeDraw
190 },
191 set: function set(value) {
192 tmp.beforeDraw = value;
193 }
194 }
195 };
196
197 var ref$1 = getDevice();
198 var windowWidth = ref$1.windowWidth;
199
200 function prepare() {
201 var self = this;
202
203 // v1.4.0 版本中将不再自动绑定we-cropper实例
204 self.attachPage = function() {
205 var pages = getCurrentPages();
206 // 获取到当前page上下文
207 var pageContext = pages[pages.length - 1];
208 // 把this依附在Page上下文的wecropper属性上,便于在page钩子函数中访问
209 Object.defineProperty(pageContext, 'wecropper', {
210 get: function get() {
211 console.warn(
212 'Instance will not be automatically bound to the page after v1.4.0\n\n' +
213 'Please use a custom instance name instead\n\n' +
214 'Example: \n' +
215 'this.mycropper = new WeCropper(options)\n\n' +
216 '// ...\n' +
217 'this.mycropper.getCropperImage()'
218 );
219 return self
220 },
221 configurable: true
222 });
223 };
224
225 self.createCtx = function() {
226 var id = self.id;
227 var targetId = self.targetId;
228
229 if (id) {
230 self.ctx = self.ctx || uni.createCanvasContext(id);
231 self.targetCtx = self.targetCtx || uni.createCanvasContext(targetId);
232 } else {
233 console.error("constructor: create canvas context failed, 'id' must be valuable");
234 }
235 };
236
237 self.deviceRadio = windowWidth / 750;
238 }
239
240 var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !==
241 'undefined' ? self : {};
242
243
244
245
246
247 function createCommonjsModule(fn, module) {
248 return module = {
249 exports: {}
250 }, fn(module, module.exports), module.exports;
251 }
252
253 var tools = createCommonjsModule(function(module, exports) {
254 /**
255 * String type check
256 */
257 exports.isStr = function(v) {
258 return typeof v === 'string';
259 };
260 /**
261 * Number type check
262 */
263 exports.isNum = function(v) {
264 return typeof v === 'number';
265 };
266 /**
267 * Array type check
268 */
269 exports.isArr = Array.isArray;
270 /**
271 * undefined type check
272 */
273 exports.isUndef = function(v) {
274 return v === undefined;
275 };
276
277 exports.isTrue = function(v) {
278 return v === true;
279 };
280
281 exports.isFalse = function(v) {
282 return v === false;
283 };
284 /**
285 * Function type check
286 */
287 exports.isFunc = function(v) {
288 return typeof v === 'function';
289 };
290 /**
291 * Quick object check - this is primarily used to tell
292 * Objects from primitive values when we know the value
293 * is a JSON-compliant type.
294 */
295 exports.isObj = exports.isObject = function(obj) {
296 return obj !== null && typeof obj === 'object'
297 };
298
299 /**
300 * Strict object type check. Only returns true
301 * for plain JavaScript objects.
302 */
303 var _toString = Object.prototype.toString;
304 exports.isPlainObject = function(obj) {
305 return _toString.call(obj) === '[object Object]'
306 };
307
308 /**
309 * Check whether the object has the property.
310 */
311 var hasOwnProperty = Object.prototype.hasOwnProperty;
312 exports.hasOwn = function(obj, key) {
313 return hasOwnProperty.call(obj, key)
314 };
315
316 /**
317 * Perform no operation.
318 * Stubbing args to make Flow happy without leaving useless transpiled code
319 * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/)
320 */
321 exports.noop = function(a, b, c) {};
322
323 /**
324 * Check if val is a valid array index.
325 */
326 exports.isValidArrayIndex = function(val) {
327 var n = parseFloat(String(val));
328 return n >= 0 && Math.floor(n) === n && isFinite(val)
329 };
330 });
331
332 var tools_7 = tools.isFunc;
333 var tools_10 = tools.isPlainObject;
334
335 var EVENT_TYPE = ['ready', 'beforeImageLoad', 'beforeDraw', 'imageLoad'];
336
337 function observer() {
338 var self = this;
339
340 self.on = function(event, fn) {
341 if (EVENT_TYPE.indexOf(event) > -1) {
342 if (tools_7(fn)) {
343 event === 'ready' ?
344 fn(self) :
345 self[("on" + (firstLetterUpper(event)))] = fn;
346 }
347 } else {
348 console.error(("event: " + event + " is invalid"));
349 }
350 return self
351 };
352 }
353
354 function wxPromise(fn) {
355 return function(obj) {
356 var args = [],
357 len = arguments.length - 1;
358 while (len-- > 0) args[len] = arguments[len + 1];
359
360 if (obj === void 0) obj = {};
361 return new Promise(function(resolve, reject) {
362 obj.success = function(res) {
363 resolve(res);
364 };
365 obj.fail = function(err) {
366 reject(err);
367 };
368 fn.apply(void 0, [obj].concat(args));
369 })
370 }
371 }
372
373 function draw(ctx, reserve) {
374 if (reserve === void 0) reserve = false;
375
376 return new Promise(function(resolve) {
377 ctx.draw(reserve, resolve);
378 })
379 }
380
381 var getImageInfo = wxPromise(uni.getImageInfo);
382
383 var canvasToTempFilePath = wxPromise(uni.canvasToTempFilePath);
384
385 var base64 = createCommonjsModule(function(module, exports) {
386 /*! http://mths.be/base64 v0.1.0 by @mathias | MIT license */
387 (function(root) {
388
389 // Detect free variables `exports`.
390 var freeExports = 'object' == 'object' && exports;
391
392 // Detect free variable `module`.
393 var freeModule = 'object' == 'object' && module &&
394 module.exports == freeExports && module;
395
396 // Detect free variable `global`, from Node.js or Browserified code, and use
397 // it as `root`.
398 var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal;
399 if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
400 root = freeGlobal;
401 }
402
403 /*--------------------------------------------------------------------------*/
404
405 var InvalidCharacterError = function(message) {
406 this.message = message;
407 };
408 InvalidCharacterError.prototype = new Error;
409 InvalidCharacterError.prototype.name = 'InvalidCharacterError';
410
411 var error = function(message) {
412 // Note: the error messages used throughout this file match those used by
413 // the native `atob`/`btoa` implementation in Chromium.
414 throw new InvalidCharacterError(message);
415 };
416
417 var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
418 // http://whatwg.org/html/common-microsyntaxes.html#space-character
419 var REGEX_SPACE_CHARACTERS = /[\t\n\f\r ]/g;
420
421 // `decode` is designed to be fully compatible with `atob` as described in the
422 // HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob
423 // The optimized base64-decoding algorithm used is based on @atk’s excellent
424 // implementation. https://gist.github.com/atk/1020396
425 var decode = function(input) {
426 input = String(input)
427 .replace(REGEX_SPACE_CHARACTERS, '');
428 var length = input.length;
429 if (length % 4 == 0) {
430 input = input.replace(/==?$/, '');
431 length = input.length;
432 }
433 if (
434 length % 4 == 1 ||
435 // http://whatwg.org/C#alphanumeric-ascii-characters
436 /[^+a-zA-Z0-9/]/.test(input)
437 ) {
438 error(
439 'Invalid character: the string to be decoded is not correctly encoded.'
440 );
441 }
442 var bitCounter = 0;
443 var bitStorage;
444 var buffer;
445 var output = '';
446 var position = -1;
447 while (++position < length) {
448 buffer = TABLE.indexOf(input.charAt(position));
449 bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;
450 // Unless this is the first of a group of 4 characters…
451 if (bitCounter++ % 4) {
452 // …convert the first 8 bits to a single ASCII character.
453 output += String.fromCharCode(
454 0xFF & bitStorage >> (-2 * bitCounter & 6)
455 );
456 }
457 }
458 return output;
459 };
460
461 // `encode` is designed to be fully compatible with `btoa` as described in the
462 // HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoa
463 var encode = function(input) {
464 input = String(input);
465 if (/[^\0-\xFF]/.test(input)) {
466 // Note: no need to special-case astral symbols here, as surrogates are
467 // matched, and the input is supposed to only contain ASCII anyway.
468 error(
469 'The string to be encoded contains characters outside of the ' +
470 'Latin1 range.'
471 );
472 }
473 var padding = input.length % 3;
474 var output = '';
475 var position = -1;
476 var a;
477 var b;
478 var c;
479 var buffer;
480 // Make sure any padding is handled outside of the loop.
481 var length = input.length - padding;
482
483 while (++position < length) {
484 // Read three bytes, i.e. 24 bits.
485 a = input.charCodeAt(position) << 16;
486 b = input.charCodeAt(++position) << 8;
487 c = input.charCodeAt(++position);
488 buffer = a + b + c;
489 // Turn the 24 bits into four chunks of 6 bits each, and append the
490 // matching character for each of them to the output.
491 output += (
492 TABLE.charAt(buffer >> 18 & 0x3F) +
493 TABLE.charAt(buffer >> 12 & 0x3F) +
494 TABLE.charAt(buffer >> 6 & 0x3F) +
495 TABLE.charAt(buffer & 0x3F)
496 );
497 }
498
499 if (padding == 2) {
500 a = input.charCodeAt(position) << 8;
501 b = input.charCodeAt(++position);
502 buffer = a + b;
503 output += (
504 TABLE.charAt(buffer >> 10) +
505 TABLE.charAt((buffer >> 4) & 0x3F) +
506 TABLE.charAt((buffer << 2) & 0x3F) +
507 '='
508 );
509 } else if (padding == 1) {
510 buffer = input.charCodeAt(position);
511 output += (
512 TABLE.charAt(buffer >> 2) +
513 TABLE.charAt((buffer << 4) & 0x3F) +
514 '=='
515 );
516 }
517
518 return output;
519 };
520
521 var base64 = {
522 'encode': encode,
523 'decode': decode,
524 'version': '0.1.0'
525 };
526
527 // Some AMD build optimizers, like r.js, check for specific condition patterns
528 // like the following:
529 if (
530 typeof undefined == 'function' &&
531 typeof undefined.amd == 'object' &&
532 undefined.amd
533 ) {
534 undefined(function() {
535 return base64;
536 });
537 } else if (freeExports && !freeExports.nodeType) {
538 if (freeModule) { // in Node.js or RingoJS v0.8.0+
539 freeModule.exports = base64;
540 } else { // in Narwhal or RingoJS v0.7.0-
541 for (var key in base64) {
542 base64.hasOwnProperty(key) && (freeExports[key] = base64[key]);
543 }
544 }
545 } else { // in Rhino or a web browser
546 root.base64 = base64;
547 }
548
549 }(commonjsGlobal));
550 });
551
552 function makeURI(strData, type) {
553 return 'data:' + type + ';base64,' + strData
554 }
555
556 function fixType(type) {
557 type = type.toLowerCase().replace(/jpg/i, 'jpeg');
558 var r = type.match(/png|jpeg|bmp|gif/)[0];
559 return 'image/' + r
560 }
561
562 function encodeData(data) {
563 var str = '';
564 if (typeof data === 'string') {
565 str = data;
566 } else {
567 for (var i = 0; i < data.length; i++) {
568 str += String.fromCharCode(data[i]);
569 }
570 }
571 return base64.encode(str)
572 }
573
574 /**
575 * 获取图像区域隐含的像素数据
576 * @param canvasId canvas标识
577 * @param x 将要被提取的图像数据矩形区域的左上角 x 坐标
578 * @param y 将要被提取的图像数据矩形区域的左上角 y 坐标
579 * @param width 将要被提取的图像数据矩形区域的宽度
580 * @param height 将要被提取的图像数据矩形区域的高度
581 * @param done 完成回调
582 */
583 function getImageData(canvasId, x, y, width, height, done) {
584 uni.canvasGetImageData({
585 canvasId: canvasId,
586 x: x,
587 y: y,
588 width: width,
589 height: height,
590 success: function success(res) {
591 done(res, null);
592 },
593 fail: function fail(res) {
594 done(null, res);
595 }
596 });
597 }
598
599 /**
600 * 生成bmp格式图片
601 * 按照规则生成图片响应头和响应体
602 * @param oData 用来描述 canvas 区域隐含的像素数据 { data, width, height } = oData
603 * @returns {*} base64字符串
604 */
605 function genBitmapImage(oData) {
606 //
607 // BITMAPFILEHEADER: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx
608 // BITMAPINFOHEADER: http://msdn.microsoft.com/en-us/library/dd183376.aspx
609 //
610 var biWidth = oData.width;
611 var biHeight = oData.height;
612 var biSizeImage = biWidth * biHeight * 3;
613 var bfSize = biSizeImage + 54; // total header size = 54 bytes
614
615 //
616 // typedef struct tagBITMAPFILEHEADER {
617 // WORD bfType;
618 // DWORD bfSize;
619 // WORD bfReserved1;
620 // WORD bfReserved2;
621 // DWORD bfOffBits;
622 // } BITMAPFILEHEADER;
623 //
624 var BITMAPFILEHEADER = [
625 // WORD bfType -- The file type signature; must be "BM"
626 0x42, 0x4D,
627 // DWORD bfSize -- The size, in bytes, of the bitmap file
628 bfSize & 0xff, bfSize >> 8 & 0xff, bfSize >> 16 & 0xff, bfSize >> 24 & 0xff,
629 // WORD bfReserved1 -- Reserved; must be zero
630 0, 0,
631 // WORD bfReserved2 -- Reserved; must be zero
632 0, 0,
633 // DWORD bfOffBits -- The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits.
634 54, 0, 0, 0
635 ];
636
637 //
638 // typedef struct tagBITMAPINFOHEADER {
639 // DWORD biSize;
640 // LONG biWidth;
641 // LONG biHeight;
642 // WORD biPlanes;
643 // WORD biBitCount;
644 // DWORD biCompression;
645 // DWORD biSizeImage;
646 // LONG biXPelsPerMeter;
647 // LONG biYPelsPerMeter;
648 // DWORD biClrUsed;
649 // DWORD biClrImportant;
650 // } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
651 //
652 var BITMAPINFOHEADER = [
653 // DWORD biSize -- The number of bytes required by the structure
654 40, 0, 0, 0,
655 // LONG biWidth -- The width of the bitmap, in pixels
656 biWidth & 0xff, biWidth >> 8 & 0xff, biWidth >> 16 & 0xff, biWidth >> 24 & 0xff,
657 // LONG biHeight -- The height of the bitmap, in pixels
658 biHeight & 0xff, biHeight >> 8 & 0xff, biHeight >> 16 & 0xff, biHeight >> 24 & 0xff,
659 // WORD biPlanes -- The number of planes for the target device. This value must be set to 1
660 1, 0,
661 // WORD biBitCount -- The number of bits-per-pixel, 24 bits-per-pixel -- the bitmap
662 // has a maximum of 2^24 colors (16777216, Truecolor)
663 24, 0,
664 // DWORD biCompression -- The type of compression, BI_RGB (code 0) -- uncompressed
665 0, 0, 0, 0,
666 // DWORD biSizeImage -- The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps
667 biSizeImage & 0xff, biSizeImage >> 8 & 0xff, biSizeImage >> 16 & 0xff, biSizeImage >> 24 & 0xff,
668 // LONG biXPelsPerMeter, unused
669 0, 0, 0, 0,
670 // LONG biYPelsPerMeter, unused
671 0, 0, 0, 0,
672 // DWORD biClrUsed, the number of color indexes of palette, unused
673 0, 0, 0, 0,
674 // DWORD biClrImportant, unused
675 0, 0, 0, 0
676 ];
677
678 var iPadding = (4 - ((biWidth * 3) % 4)) % 4;
679
680 var aImgData = oData.data;
681
682 var strPixelData = '';
683 var biWidth4 = biWidth << 2;
684 var y = biHeight;
685 var fromCharCode = String.fromCharCode;
686
687 do {
688 var iOffsetY = biWidth4 * (y - 1);
689 var strPixelRow = '';
690 for (var x = 0; x < biWidth; x++) {
691 var iOffsetX = x << 2;
692 strPixelRow += fromCharCode(aImgData[iOffsetY + iOffsetX + 2]) +
693 fromCharCode(aImgData[iOffsetY + iOffsetX + 1]) +
694 fromCharCode(aImgData[iOffsetY + iOffsetX]);
695 }
696
697 for (var c = 0; c < iPadding; c++) {
698 strPixelRow += String.fromCharCode(0);
699 }
700
701 strPixelData += strPixelRow;
702 } while (--y)
703
704 var strEncoded = encodeData(BITMAPFILEHEADER.concat(BITMAPINFOHEADER)) + encodeData(strPixelData);
705
706 return strEncoded
707 }
708
709 /**
710 * 转换为图片base64
711 * @param canvasId canvas标识
712 * @param x 将要被提取的图像数据矩形区域的左上角 x 坐标
713 * @param y 将要被提取的图像数据矩形区域的左上角 y 坐标
714 * @param width 将要被提取的图像数据矩形区域的宽度
715 * @param height 将要被提取的图像数据矩形区域的高度
716 * @param type 转换图片类型
717 * @param done 完成回调
718 */
719 function convertToImage(canvasId, x, y, width, height, type, done) {
720 if (done === void 0) done = function() {};
721
722 if (type === undefined) {
723 type = 'png';
724 }
725 type = fixType(type);
726 if (/bmp/.test(type)) {
727 getImageData(canvasId, x, y, width, height, function(data, err) {
728 var strData = genBitmapImage(data);
729 tools_7(done) && done(makeURI(strData, 'image/' + type), err);
730 });
731 } else {
732 console.error('暂不支持生成\'' + type + '\'类型的base64图片');
733 }
734 }
735
736 var CanvasToBase64 = {
737 convertToImage: convertToImage,
738 // convertToPNG: function (width, height, done) {
739 // return convertToImage(width, height, 'png', done)
740 // },
741 // convertToJPEG: function (width, height, done) {
742 // return convertToImage(width, height, 'jpeg', done)
743 // },
744 // convertToGIF: function (width, height, done) {
745 // return convertToImage(width, height, 'gif', done)
746 // },
747 convertToBMP: function(ref, done) {
748 if (ref === void 0) ref = {};
749 var canvasId = ref.canvasId;
750 var x = ref.x;
751 var y = ref.y;
752 var width = ref.width;
753 var height = ref.height;
754 if (done === void 0) done = function() {};
755
756 return convertToImage(canvasId, x, y, width, height, 'bmp', done)
757 }
758 };
759
760 function methods() {
761 var self = this;
762
763 var boundWidth = self.width; // 裁剪框默认宽度,即整个画布宽度
764 var boundHeight = self.height; // 裁剪框默认高度,即整个画布高度
765
766 var id = self.id;
767 var targetId = self.targetId;
768 var pixelRatio = self.pixelRatio;
769
770 var ref = self.cut;
771 var x = ref.x;
772 if (x === void 0) x = 0;
773 var y = ref.y;
774 if (y === void 0) y = 0;
775 var width = ref.width;
776 if (width === void 0) width = boundWidth;
777 var height = ref.height;
778 if (height === void 0) height = boundHeight;
779
780 self.updateCanvas = function(done) {
781 if (self.croperTarget) {
782 // 画布绘制图片
783 self.ctx.drawImage(
784 self.croperTarget,
785 self.imgLeft,
786 self.imgTop,
787 self.scaleWidth,
788 self.scaleHeight
789 );
790 }
791 tools_7(self.onBeforeDraw) && self.onBeforeDraw(self.ctx, self);
792
793 self.setBoundStyle(self.boundStyle); // 设置边界样式
794
795 self.ctx.draw(false, done);
796 return self
797 };
798
799 self.pushOrigin = self.pushOrign = function(src) {
800 self.src = src;
801
802 tools_7(self.onBeforeImageLoad) && self.onBeforeImageLoad(self.ctx, self);
803
804 return getImageInfo({
805 src: src
806 })
807 .then(function(res) {
808 var innerAspectRadio = res.width / res.height;
809 var customAspectRadio = width / height;
810
811 self.croperTarget = res.path;
812
813 if (innerAspectRadio < customAspectRadio) {
814 self.rectX = x;
815 self.baseWidth = width;
816 self.baseHeight = width / innerAspectRadio;
817 self.rectY = y - Math.abs((height - self.baseHeight) / 2);
818 } else {
819 self.rectY = y;
820 self.baseWidth = height * innerAspectRadio;
821 self.baseHeight = height;
822 self.rectX = x - Math.abs((width - self.baseWidth) / 2);
823 }
824
825 self.imgLeft = self.rectX;
826 self.imgTop = self.rectY;
827 self.scaleWidth = self.baseWidth;
828 self.scaleHeight = self.baseHeight;
829
830 self.update();
831
832 return new Promise(function(resolve) {
833 self.updateCanvas(resolve);
834 })
835 })
836 .then(function() {
837 tools_7(self.onImageLoad) && self.onImageLoad(self.ctx, self);
838 })
839 };
840
841 self.removeImage = function() {
842 self.src = '';
843 self.croperTarget = '';
844 return draw(self.ctx)
845 };
846
847 self.getCropperBase64 = function(done) {
848 if (done === void 0) done = function() {};
849
850 CanvasToBase64.convertToBMP({
851 canvasId: id,
852 x: x,
853 y: y,
854 width: width,
855 height: height
856 }, done);
857 };
858
859 self.getCropperImage = function(opt, fn) {
860 var customOptions = opt;
861
862 var canvasOptions = {
863 canvasId: id,
864 x: x,
865 y: y,
866 width: width,
867 height: height
868 };
869
870 var task = function() {
871 return Promise.resolve();
872 };
873
874 if (
875 tools_10(customOptions) &&
876 customOptions.original
877 ) {
878 // original mode
879 task = function() {
880 self.targetCtx.drawImage(
881 self.croperTarget,
882 self.imgLeft * pixelRatio,
883 self.imgTop * pixelRatio,
884 self.scaleWidth * pixelRatio,
885 self.scaleHeight * pixelRatio
886 );
887
888 canvasOptions = {
889 canvasId: targetId,
890 x: x * pixelRatio,
891 y: y * pixelRatio,
892 width: width * pixelRatio,
893 height: height * pixelRatio
894 };
895
896 return draw(self.targetCtx)
897 };
898 }
899
900 return task()
901 .then(function() {
902 if (tools_10(customOptions)) {
903 canvasOptions = Object.assign({}, canvasOptions, customOptions);
904 }
905
906 if (tools_7(customOptions)) {
907 fn = customOptions;
908 }
909
910 var arg = canvasOptions.componentContext ?
911 [canvasOptions, canvasOptions.componentContext] :
912 [canvasOptions];
913
914 return canvasToTempFilePath.apply(null, arg)
915 })
916 .then(function(res) {
917 var tempFilePath = res.tempFilePath;
918
919 return tools_7(fn) ?
920 fn.call(self, tempFilePath, null) :
921 tempFilePath
922 })
923 .catch(function(err) {
924 if (tools_7(fn)) {
925 fn.call(self, null, err);
926 } else {
927 throw err
928 }
929 })
930 };
931 }
932
933 /**
934 * 获取最新缩放值
935 * @param oldScale 上一次触摸结束后的缩放值
936 * @param oldDistance 上一次触摸结束后的双指距离
937 * @param zoom 缩放系数
938 * @param touch0 第一指touch对象
939 * @param touch1 第二指touch对象
940 * @returns {*}
941 */
942 var getNewScale = function(oldScale, oldDistance, zoom, touch0, touch1) {
943 var xMove, yMove, newDistance;
944 // 计算二指最新距离
945 xMove = Math.round(touch1.x - touch0.x);
946 yMove = Math.round(touch1.y - touch0.y);
947 newDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));
948
949 return oldScale + 0.001 * zoom * (newDistance - oldDistance)
950 };
951
952 function update() {
953 var self = this;
954
955 if (!self.src) {
956 return
957 }
958
959 self.__oneTouchStart = function(touch) {
960 self.touchX0 = Math.round(touch.x);
961 self.touchY0 = Math.round(touch.y);
962 };
963
964 self.__oneTouchMove = function(touch) {
965 var xMove, yMove;
966 // 计算单指移动的距离
967 if (self.touchended) {
968 return self.updateCanvas()
969 }
970 xMove = Math.round(touch.x - self.touchX0);
971 yMove = Math.round(touch.y - self.touchY0);
972
973 var imgLeft = Math.round(self.rectX + xMove);
974 var imgTop = Math.round(self.rectY + yMove);
975
976 self.outsideBound(imgLeft, imgTop);
977
978 self.updateCanvas();
979 };
980
981 self.__twoTouchStart = function(touch0, touch1) {
982 var xMove, yMove, oldDistance;
983
984 self.touchX1 = Math.round(self.rectX + self.scaleWidth / 2);
985 self.touchY1 = Math.round(self.rectY + self.scaleHeight / 2);
986
987 // 计算两指距离
988 xMove = Math.round(touch1.x - touch0.x);
989 yMove = Math.round(touch1.y - touch0.y);
990 oldDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));
991
992 self.oldDistance = oldDistance;
993 };
994
995 self.__twoTouchMove = function(touch0, touch1) {
996 var oldScale = self.oldScale;
997 var oldDistance = self.oldDistance;
998 var scale = self.scale;
999 var zoom = self.zoom;
1000
1001 self.newScale = getNewScale(oldScale, oldDistance, zoom, touch0, touch1);
1002
1003 // 设定缩放范围
1004 self.newScale <= 1 && (self.newScale = 1);
1005 self.newScale >= scale && (self.newScale = scale);
1006
1007 self.scaleWidth = Math.round(self.newScale * self.baseWidth);
1008 self.scaleHeight = Math.round(self.newScale * self.baseHeight);
1009 var imgLeft = Math.round(self.touchX1 - self.scaleWidth / 2);
1010 var imgTop = Math.round(self.touchY1 - self.scaleHeight / 2);
1011
1012 self.outsideBound(imgLeft, imgTop);
1013
1014 self.updateCanvas();
1015 };
1016
1017 self.__xtouchEnd = function() {
1018 self.oldScale = self.newScale;
1019 self.rectX = self.imgLeft;
1020 self.rectY = self.imgTop;
1021 };
1022 }
1023
1024 var handle = {
1025 // 图片手势初始监测
1026 touchStart: function touchStart(e) {
1027 var self = this;
1028 var ref = e.touches;
1029 var touch0 = ref[0];
1030 var touch1 = ref[1];
1031
1032 if (!self.src) {
1033 return
1034 }
1035
1036 setTouchState(self, true, null, null);
1037
1038 // 计算第一个触摸点的位置,并参照改点进行缩放
1039 self.__oneTouchStart(touch0);
1040
1041 // 两指手势触发
1042 if (e.touches.length >= 2) {
1043 self.__twoTouchStart(touch0, touch1);
1044 }
1045 },
1046
1047 // 图片手势动态缩放
1048 touchMove: function touchMove(e) {
1049 var self = this;
1050 var ref = e.touches;
1051 var touch0 = ref[0];
1052 var touch1 = ref[1];
1053
1054 if (!self.src) {
1055 return
1056 }
1057
1058 setTouchState(self, null, true);
1059
1060 // 单指手势时触发
1061 if (e.touches.length === 1) {
1062 self.__oneTouchMove(touch0);
1063 }
1064 // 两指手势触发
1065 if (e.touches.length >= 2) {
1066 self.__twoTouchMove(touch0, touch1);
1067 }
1068 },
1069
1070 touchEnd: function touchEnd(e) {
1071 var self = this;
1072
1073 if (!self.src) {
1074 return
1075 }
1076
1077 setTouchState(self, false, false, true);
1078 self.__xtouchEnd();
1079 }
1080 };
1081
1082 function cut() {
1083 var self = this;
1084 var boundWidth = self.width; // 裁剪框默认宽度,即整个画布宽度
1085 var boundHeight = self.height;
1086 // 裁剪框默认高度,即整个画布高度
1087 var ref = self.cut;
1088 var x = ref.x;
1089 if (x === void 0) x = 0;
1090 var y = ref.y;
1091 if (y === void 0) y = 0;
1092 var width = ref.width;
1093 if (width === void 0) width = boundWidth;
1094 var height = ref.height;
1095 if (height === void 0) height = boundHeight;
1096
1097 /**
1098 * 设置边界
1099 * @param imgLeft 图片左上角横坐标值
1100 * @param imgTop 图片左上角纵坐标值
1101 */
1102 self.outsideBound = function(imgLeft, imgTop) {
1103 self.imgLeft = imgLeft >= x ?
1104 x :
1105 self.scaleWidth + imgLeft - x <= width ?
1106 x + width - self.scaleWidth :
1107 imgLeft;
1108
1109 self.imgTop = imgTop >= y ?
1110 y :
1111 self.scaleHeight + imgTop - y <= height ?
1112 y + height - self.scaleHeight :
1113 imgTop;
1114 };
1115
1116 /**
1117 * 设置边界样式
1118 * @param color 边界颜色
1119 */
1120 self.setBoundStyle = function(ref) {
1121 if (ref === void 0) ref = {};
1122 var color = ref.color;
1123 if (color === void 0) color = '#04b00f';
1124 var mask = ref.mask;
1125 if (mask === void 0) mask = 'rgba(0, 0, 0, 0.3)';
1126 var lineWidth = ref.lineWidth;
1127 if (lineWidth === void 0) lineWidth = 1;
1128
1129 var half = lineWidth / 2;
1130 var boundOption = [{
1131 start: {
1132 x: x - half,
1133 y: y + 10 - half
1134 },
1135 step1: {
1136 x: x - half,
1137 y: y - half
1138 },
1139 step2: {
1140 x: x + 10 - half,
1141 y: y - half
1142 }
1143 },
1144 {
1145 start: {
1146 x: x - half,
1147 y: y + height - 10 + half
1148 },
1149 step1: {
1150 x: x - half,
1151 y: y + height + half
1152 },
1153 step2: {
1154 x: x + 10 - half,
1155 y: y + height + half
1156 }
1157 },
1158 {
1159 start: {
1160 x: x + width - 10 + half,
1161 y: y - half
1162 },
1163 step1: {
1164 x: x + width + half,
1165 y: y - half
1166 },
1167 step2: {
1168 x: x + width + half,
1169 y: y + 10 - half
1170 }
1171 },
1172 {
1173 start: {
1174 x: x + width + half,
1175 y: y + height - 10 + half
1176 },
1177 step1: {
1178 x: x + width + half,
1179 y: y + height + half
1180 },
1181 step2: {
1182 x: x + width - 10 + half,
1183 y: y + height + half
1184 }
1185 }
1186 ];
1187
1188 // 绘制半透明层
1189 self.ctx.beginPath();
1190 self.ctx.setFillStyle(mask);
1191 self.ctx.fillRect(0, 0, x, boundHeight);
1192 self.ctx.fillRect(x, 0, width, y);
1193 self.ctx.fillRect(x, y + height, width, boundHeight - y - height);
1194 self.ctx.fillRect(x + width, 0, boundWidth - x - width, boundHeight);
1195 self.ctx.fill();
1196
1197 boundOption.forEach(function(op) {
1198 self.ctx.beginPath();
1199 self.ctx.setStrokeStyle(color);
1200 self.ctx.setLineWidth(lineWidth);
1201 self.ctx.moveTo(op.start.x, op.start.y);
1202 self.ctx.lineTo(op.step1.x, op.step1.y);
1203 self.ctx.lineTo(op.step2.x, op.step2.y);
1204 self.ctx.stroke();
1205 });
1206 };
1207 }
1208
1209 var version = "1.3.9";
1210
1211 var WeCropper = function WeCropper(params) {
1212 var self = this;
1213 var _default = {};
1214
1215 validator(self, DEFAULT);
1216
1217 Object.keys(DEFAULT).forEach(function(key) {
1218 _default[key] = DEFAULT[key].default;
1219 });
1220 Object.assign(self, _default, params);
1221
1222 self.prepare();
1223 self.attachPage();
1224 self.createCtx();
1225 self.observer();
1226 self.cutt();
1227 self.methods();
1228 self.init();
1229 self.update();
1230
1231 return self
1232 };
1233
1234 WeCropper.prototype.init = function init() {
1235 var self = this;
1236 var src = self.src;
1237
1238 self.version = version;
1239
1240 typeof self.onReady === 'function' && self.onReady(self.ctx, self);
1241
1242 if (src) {
1243 self.pushOrign(src);
1244 } else {
1245 self.updateCanvas();
1246 }
1247 setTouchState(self, false, false, false);
1248
1249 self.oldScale = 1;
1250 self.newScale = 1;
1251
1252 return self
1253 };
1254
1255 Object.assign(WeCropper.prototype, handle);
1256
1257 WeCropper.prototype.prepare = prepare;
1258 WeCropper.prototype.observer = observer;
1259 WeCropper.prototype.methods = methods;
1260 WeCropper.prototype.cutt = cut;
1261 WeCropper.prototype.update = update;
1262
1263 return WeCropper;
1264
1265})));