weCropper.js 41 KB

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