当前位置:   article > 正文

小程序-we-cropper.js

we-cropper.js
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  3. typeof define === 'function' && define.amd ? define(factory) :
  4. (global.WeCropper = factory());
  5. }(this, (function () { 'use strict';
  6. var device = void 0;
  7. var TOUCH_STATE = ['touchstarted', 'touchmoved', 'touchended'];
  8. function isFunction (obj) {
  9. return typeof obj === 'function'
  10. }
  11. function firstLetterUpper (str) {
  12. return str.charAt(0).toUpperCase() + str.slice(1)
  13. }
  14. function setTouchState (instance) {
  15. var arg = [], len = arguments.length - 1;
  16. while ( len-- > 0 ) arg[ len ] = arguments[ len + 1 ];
  17. TOUCH_STATE.forEach(function (key, i) {
  18. if (arg[i] !== undefined) {
  19. instance[key] = arg[i];
  20. }
  21. });
  22. }
  23. function validator (instance, o) {
  24. Object.defineProperties(instance, o);
  25. }
  26. function getDevice () {
  27. if (!device) {
  28. device = wx.getSystemInfoSync();
  29. }
  30. return device
  31. }
  32. var tmp = {};
  33. var DEFAULT = {
  34. id: {
  35. default: 'cropper',
  36. get: function get () {
  37. return tmp.id
  38. },
  39. set: function set (value) {
  40. if (typeof (value) !== 'string') {
  41. console.error(("id:" + value + " is invalid"));
  42. }
  43. tmp.id = value;
  44. }
  45. },
  46. width: {
  47. default: 750,
  48. get: function get () {
  49. return tmp.width
  50. },
  51. set: function set (value) {
  52. if (typeof (value) !== 'number') {
  53. console.error(("width:" + value + " is invalid"));
  54. }
  55. tmp.width = value;
  56. }
  57. },
  58. height: {
  59. default: 750,
  60. get: function get () {
  61. return tmp.height
  62. },
  63. set: function set (value) {
  64. if (typeof (value) !== 'number') {
  65. console.error(("height:" + value + " is invalid"));
  66. }
  67. tmp.height = value;
  68. }
  69. },
  70. scale: {
  71. default: 2.5,
  72. get: function get () {
  73. return tmp.scale
  74. },
  75. set: function set (value) {
  76. if (typeof (value) !== 'number') {
  77. console.error(("scale:" + value + " is invalid"));
  78. }
  79. tmp.scale = value;
  80. }
  81. },
  82. zoom: {
  83. default: 5,
  84. get: function get () {
  85. return tmp.zoom
  86. },
  87. set: function set (value) {
  88. if (typeof (value) !== 'number') {
  89. console.error(("zoom:" + value + " is invalid"));
  90. } else if (value < 0 || value > 10) {
  91. console.error("zoom should be ranged in 0 ~ 10");
  92. }
  93. tmp.zoom = value;
  94. }
  95. },
  96. src: {
  97. default: 'cropper',
  98. get: function get () {
  99. return tmp.src
  100. },
  101. set: function set (value) {
  102. if (typeof (value) !== 'string') {
  103. console.error(("id:" + value + " is invalid"));
  104. }
  105. tmp.src = value;
  106. }
  107. },
  108. cut: {
  109. default: {},
  110. get: function get () {
  111. return tmp.cut
  112. },
  113. set: function set (value) {
  114. if (typeof (value) !== 'object') {
  115. console.error(("id:" + value + " is invalid"));
  116. }
  117. tmp.cut = value;
  118. }
  119. },
  120. onReady: {
  121. default: null,
  122. get: function get () {
  123. return tmp.ready
  124. },
  125. set: function set (value) {
  126. tmp.ready = value;
  127. }
  128. },
  129. onBeforeImageLoad: {
  130. default: null,
  131. get: function get () {
  132. return tmp.beforeImageLoad
  133. },
  134. set: function set (value) {
  135. tmp.beforeImageLoad = value;
  136. }
  137. },
  138. onImageLoad: {
  139. default: null,
  140. get: function get () {
  141. return tmp.imageLoad
  142. },
  143. set: function set (value) {
  144. tmp.imageLoad = value;
  145. }
  146. },
  147. onBeforeDraw: {
  148. default: null,
  149. get: function get () {
  150. return tmp.beforeDraw
  151. },
  152. set: function set (value) {
  153. tmp.beforeDraw = value;
  154. }
  155. }
  156. };
  157. function prepare () {
  158. var self = this;
  159. var ref = getDevice();
  160. var windowWidth = ref.windowWidth;
  161. self.attachPage = function () {
  162. var pages = getCurrentPages();
  163. // 获取到当前page上下文
  164. var pageContext = pages[pages.length - 1];
  165. // 把this依附在Page上下文的wecropper属性上,便于在page钩子函数中访问
  166. pageContext.wecropper = self;
  167. };
  168. self.createCtx = function () {
  169. var id = self.id;
  170. if (id) {
  171. self.ctx = wx.createCanvasContext(id);
  172. } else {
  173. console.error("constructor: create canvas context failed, 'id' must be valuable");
  174. }
  175. };
  176. self.deviceRadio = windowWidth / 750;
  177. }
  178. function observer () {
  179. var self = this;
  180. var EVENT_TYPE = ['ready', 'beforeImageLoad', 'beforeDraw', 'imageLoad'];
  181. self.on = function (event, fn) {
  182. if (EVENT_TYPE.indexOf(event) > -1) {
  183. if (typeof (fn) === 'function') {
  184. event === 'ready'
  185. ? fn(self)
  186. : self[("on" + (firstLetterUpper(event)))] = fn;
  187. }
  188. } else {
  189. console.error(("event: " + event + " is invalid"));
  190. }
  191. return self
  192. };
  193. }
  194. var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
  195. function createCommonjsModule(fn, module) {
  196. return module = { exports: {} }, fn(module, module.exports), module.exports;
  197. }
  198. var base64 = createCommonjsModule(function (module, exports) {
  199. /*! http://mths.be/base64 v0.1.0 by @mathias | MIT license */
  200. (function(root) {
  201. // Detect free variables `exports`.
  202. var freeExports = 'object' == 'object' && exports;
  203. // Detect free variable `module`.
  204. var freeModule = 'object' == 'object' && module &&
  205. module.exports == freeExports && module;
  206. // Detect free variable `global`, from Node.js or Browserified code, and use
  207. // it as `root`.
  208. var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal;
  209. if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
  210. root = freeGlobal;
  211. }
  212. /*--------------------------------------------------------------------------*/
  213. var InvalidCharacterError = function(message) {
  214. this.message = message;
  215. };
  216. InvalidCharacterError.prototype = new Error;
  217. InvalidCharacterError.prototype.name = 'InvalidCharacterError';
  218. var error = function(message) {
  219. // Note: the error messages used throughout this file match those used by
  220. // the native `atob`/`btoa` implementation in Chromium.
  221. throw new InvalidCharacterError(message);
  222. };
  223. var TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  224. // http://whatwg.org/html/common-microsyntaxes.html#space-character
  225. var REGEX_SPACE_CHARACTERS = /[\t\n\f\r ]/g;
  226. // `decode` is designed to be fully compatible with `atob` as described in the
  227. // HTML Standard. http://whatwg.org/html/webappapis.html#dom-windowbase64-atob
  228. // The optimized base64-decoding algorithm used is based on @atk’s excellent
  229. // implementation. https://gist.github.com/atk/1020396
  230. var decode = function(input) {
  231. input = String(input)
  232. .replace(REGEX_SPACE_CHARACTERS, '');
  233. var length = input.length;
  234. if (length % 4 == 0) {
  235. input = input.replace(/==?$/, '');
  236. length = input.length;
  237. }
  238. if (
  239. length % 4 == 1 ||
  240. // http://whatwg.org/C#alphanumeric-ascii-characters
  241. /[^+a-zA-Z0-9/]/.test(input)
  242. ) {
  243. error(
  244. 'Invalid character: the string to be decoded is not correctly encoded.'
  245. );
  246. }
  247. var bitCounter = 0;
  248. var bitStorage;
  249. var buffer;
  250. var output = '';
  251. var position = -1;
  252. while (++position < length) {
  253. buffer = TABLE.indexOf(input.charAt(position));
  254. bitStorage = bitCounter % 4 ? bitStorage * 64 + buffer : buffer;
  255. // Unless this is the first of a group of 4 characters…
  256. if (bitCounter++ % 4) {
  257. // …convert the first 8 bits to a single ASCII character.
  258. output += String.fromCharCode(
  259. 0xFF & bitStorage >> (-2 * bitCounter & 6)
  260. );
  261. }
  262. }
  263. return output;
  264. };
  265. // `encode` is designed to be fully compatible with `btoa` as described in the
  266. // HTML Standard: http://whatwg.org/html/webappapis.html#dom-windowbase64-btoa
  267. var encode = function(input) {
  268. input = String(input);
  269. if (/[^\0-\xFF]/.test(input)) {
  270. // Note: no need to special-case astral symbols here, as surrogates are
  271. // matched, and the input is supposed to only contain ASCII anyway.
  272. error(
  273. 'The string to be encoded contains characters outside of the ' +
  274. 'Latin1 range.'
  275. );
  276. }
  277. var padding = input.length % 3;
  278. var output = '';
  279. var position = -1;
  280. var a;
  281. var b;
  282. var c;
  283. var buffer;
  284. // Make sure any padding is handled outside of the loop.
  285. var length = input.length - padding;
  286. while (++position < length) {
  287. // Read three bytes, i.e. 24 bits.
  288. a = input.charCodeAt(position) << 16;
  289. b = input.charCodeAt(++position) << 8;
  290. c = input.charCodeAt(++position);
  291. buffer = a + b + c;
  292. // Turn the 24 bits into four chunks of 6 bits each, and append the
  293. // matching character for each of them to the output.
  294. output += (
  295. TABLE.charAt(buffer >> 18 & 0x3F) +
  296. TABLE.charAt(buffer >> 12 & 0x3F) +
  297. TABLE.charAt(buffer >> 6 & 0x3F) +
  298. TABLE.charAt(buffer & 0x3F)
  299. );
  300. }
  301. if (padding == 2) {
  302. a = input.charCodeAt(position) << 8;
  303. b = input.charCodeAt(++position);
  304. buffer = a + b;
  305. output += (
  306. TABLE.charAt(buffer >> 10) +
  307. TABLE.charAt((buffer >> 4) & 0x3F) +
  308. TABLE.charAt((buffer << 2) & 0x3F) +
  309. '='
  310. );
  311. } else if (padding == 1) {
  312. buffer = input.charCodeAt(position);
  313. output += (
  314. TABLE.charAt(buffer >> 2) +
  315. TABLE.charAt((buffer << 4) & 0x3F) +
  316. '=='
  317. );
  318. }
  319. return output;
  320. };
  321. var base64 = {
  322. 'encode': encode,
  323. 'decode': decode,
  324. 'version': '0.1.0'
  325. };
  326. // Some AMD build optimizers, like r.js, check for specific condition patterns
  327. // like the following:
  328. if (
  329. typeof undefined == 'function' &&
  330. typeof undefined.amd == 'object' &&
  331. undefined.amd
  332. ) {
  333. undefined(function() {
  334. return base64;
  335. });
  336. } else if (freeExports && !freeExports.nodeType) {
  337. if (freeModule) { // in Node.js or RingoJS v0.8.0+
  338. freeModule.exports = base64;
  339. } else { // in Narwhal or RingoJS v0.7.0-
  340. for (var key in base64) {
  341. base64.hasOwnProperty(key) && (freeExports[key] = base64[key]);
  342. }
  343. }
  344. } else { // in Rhino or a web browser
  345. root.base64 = base64;
  346. }
  347. }(commonjsGlobal));
  348. });
  349. function makeURI (strData, type) {
  350. return 'data:' + type + ';base64,' + strData
  351. }
  352. function fixType (type) {
  353. type = type.toLowerCase().replace(/jpg/i, 'jpeg');
  354. var r = type.match(/png|jpeg|bmp|gif/)[0];
  355. return 'image/' + r
  356. }
  357. function encodeData (data) {
  358. var str = '';
  359. if (typeof data === 'string') {
  360. str = data;
  361. } else {
  362. for (var i = 0; i < data.length; i++) {
  363. str += String.fromCharCode(data[i]);
  364. }
  365. }
  366. return base64.encode(str)
  367. }
  368. /**
  369. * 获取图像区域隐含的像素数据
  370. * @param canvasId canvas标识
  371. * @param x 将要被提取的图像数据矩形区域的左上角 x 坐标
  372. * @param y 将要被提取的图像数据矩形区域的左上角 y 坐标
  373. * @param width 将要被提取的图像数据矩形区域的宽度
  374. * @param height 将要被提取的图像数据矩形区域的高度
  375. * @param done 完成回调
  376. */
  377. function getImageData (canvasId, x, y, width, height, done) {
  378. wx.canvasGetImageData({
  379. canvasId: canvasId,
  380. x: x,
  381. y: y,
  382. width: width,
  383. height: height,
  384. success: function success (res) {
  385. done(res);
  386. },
  387. fail: function fail (res) {
  388. done(null);
  389. console.error('canvasGetImageData error: ' + res);
  390. }
  391. });
  392. }
  393. /**
  394. * 生成bmp格式图片
  395. * 按照规则生成图片响应头和响应体
  396. * @param oData 用来描述 canvas 区域隐含的像素数据 { data, width, height } = oData
  397. * @returns {*} base64字符串
  398. */
  399. function genBitmapImage (oData) {
  400. //
  401. // BITMAPFILEHEADER: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx
  402. // BITMAPINFOHEADER: http://msdn.microsoft.com/en-us/library/dd183376.aspx
  403. //
  404. var biWidth = oData.width;
  405. var biHeight = oData.height;
  406. var biSizeImage = biWidth * biHeight * 3;
  407. var bfSize = biSizeImage + 54; // total header size = 54 bytes
  408. //
  409. // typedef struct tagBITMAPFILEHEADER {
  410. // WORD bfType;
  411. // DWORD bfSize;
  412. // WORD bfReserved1;
  413. // WORD bfReserved2;
  414. // DWORD bfOffBits;
  415. // } BITMAPFILEHEADER;
  416. //
  417. var BITMAPFILEHEADER = [
  418. // WORD bfType -- The file type signature; must be "BM"
  419. 0x42, 0x4D,
  420. // DWORD bfSize -- The size, in bytes, of the bitmap file
  421. bfSize & 0xff, bfSize >> 8 & 0xff, bfSize >> 16 & 0xff, bfSize >> 24 & 0xff,
  422. // WORD bfReserved1 -- Reserved; must be zero
  423. 0, 0,
  424. // WORD bfReserved2 -- Reserved; must be zero
  425. 0, 0,
  426. // DWORD bfOffBits -- The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits.
  427. 54, 0, 0, 0
  428. ];
  429. //
  430. // typedef struct tagBITMAPINFOHEADER {
  431. // DWORD biSize;
  432. // LONG biWidth;
  433. // LONG biHeight;
  434. // WORD biPlanes;
  435. // WORD biBitCount;
  436. // DWORD biCompression;
  437. // DWORD biSizeImage;
  438. // LONG biXPelsPerMeter;
  439. // LONG biYPelsPerMeter;
  440. // DWORD biClrUsed;
  441. // DWORD biClrImportant;
  442. // } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
  443. //
  444. var BITMAPINFOHEADER = [
  445. // DWORD biSize -- The number of bytes required by the structure
  446. 40, 0, 0, 0,
  447. // LONG biWidth -- The width of the bitmap, in pixels
  448. biWidth & 0xff, biWidth >> 8 & 0xff, biWidth >> 16 & 0xff, biWidth >> 24 & 0xff,
  449. // LONG biHeight -- The height of the bitmap, in pixels
  450. biHeight & 0xff, biHeight >> 8 & 0xff, biHeight >> 16 & 0xff, biHeight >> 24 & 0xff,
  451. // WORD biPlanes -- The number of planes for the target device. This value must be set to 1
  452. 1, 0,
  453. // WORD biBitCount -- The number of bits-per-pixel, 24 bits-per-pixel -- the bitmap
  454. // has a maximum of 2^24 colors (16777216, Truecolor)
  455. 24, 0,
  456. // DWORD biCompression -- The type of compression, BI_RGB (code 0) -- uncompressed
  457. 0, 0, 0, 0,
  458. // DWORD biSizeImage -- The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps
  459. biSizeImage & 0xff, biSizeImage >> 8 & 0xff, biSizeImage >> 16 & 0xff, biSizeImage >> 24 & 0xff,
  460. // LONG biXPelsPerMeter, unused
  461. 0, 0, 0, 0,
  462. // LONG biYPelsPerMeter, unused
  463. 0, 0, 0, 0,
  464. // DWORD biClrUsed, the number of color indexes of palette, unused
  465. 0, 0, 0, 0,
  466. // DWORD biClrImportant, unused
  467. 0, 0, 0, 0
  468. ];
  469. var iPadding = (4 - ((biWidth * 3) % 4)) % 4;
  470. var aImgData = oData.data;
  471. var strPixelData = '';
  472. var biWidth4 = biWidth << 2;
  473. var y = biHeight;
  474. var fromCharCode = String.fromCharCode;
  475. do {
  476. var iOffsetY = biWidth4 * (y - 1);
  477. var strPixelRow = '';
  478. for (var x = 0; x < biWidth; x++) {
  479. var iOffsetX = x << 2;
  480. strPixelRow += fromCharCode(aImgData[iOffsetY + iOffsetX + 2]) +
  481. fromCharCode(aImgData[iOffsetY + iOffsetX + 1]) +
  482. fromCharCode(aImgData[iOffsetY + iOffsetX]);
  483. }
  484. for (var c = 0; c < iPadding; c++) {
  485. strPixelRow += String.fromCharCode(0);
  486. }
  487. strPixelData += strPixelRow;
  488. } while (--y)
  489. var strEncoded = encodeData(BITMAPFILEHEADER.concat(BITMAPINFOHEADER)) + encodeData(strPixelData);
  490. return strEncoded
  491. }
  492. /**
  493. * 转换为图片base64
  494. * @param canvasId canvas标识
  495. * @param x 将要被提取的图像数据矩形区域的左上角 x 坐标
  496. * @param y 将要被提取的图像数据矩形区域的左上角 y 坐标
  497. * @param width 将要被提取的图像数据矩形区域的宽度
  498. * @param height 将要被提取的图像数据矩形区域的高度
  499. * @param type 转换图片类型
  500. * @param done 完成回调
  501. */
  502. function convertToImage (canvasId, x, y, width, height, type, done) {
  503. if ( done === void 0 ) done = function () {};
  504. if (type === undefined) { type = 'png'; }
  505. type = fixType(type);
  506. if (/bmp/.test(type)) {
  507. getImageData(canvasId, x, y, width, height, function (data) {
  508. var strData = genBitmapImage(data);
  509. isFunction(done) && done(makeURI(strData, 'image/' + type));
  510. });
  511. } else {
  512. console.error('暂不支持生成\'' + type + '\'类型的base64图片');
  513. }
  514. }
  515. var CanvasToBase64 = {
  516. convertToImage: convertToImage,
  517. // convertToPNG: function (width, height, done) {
  518. // return convertToImage(width, height, 'png', done)
  519. // },
  520. // convertToJPEG: function (width, height, done) {
  521. // return convertToImage(width, height, 'jpeg', done)
  522. // },
  523. // convertToGIF: function (width, height, done) {
  524. // return convertToImage(width, height, 'gif', done)
  525. // },
  526. convertToBMP: function (ref, done) {
  527. if ( ref === void 0 ) ref = {};
  528. var canvasId = ref.canvasId;
  529. var x = ref.x;
  530. var y = ref.y;
  531. var width = ref.width;
  532. var height = ref.height;
  533. if ( done === void 0 ) done = function () {};
  534. return convertToImage(canvasId, x, y, width, height, 'bmp', done)
  535. }
  536. };
  537. function methods () {
  538. var self = this;
  539. var id = self.id;
  540. var deviceRadio = self.deviceRadio;
  541. var boundWidth = self.width; // 裁剪框默认宽度,即整个画布宽度
  542. var boundHeight = self.height; // 裁剪框默认高度,即整个画布高度
  543. var ref = self.cut;
  544. var x = ref.x; if ( x === void 0 ) x = 0;
  545. var y = ref.y; if ( y === void 0 ) y = 0;
  546. var width = ref.width; if ( width === void 0 ) width = boundWidth;
  547. var height = ref.height; if ( height === void 0 ) height = boundHeight;
  548. self.updateCanvas = function () {
  549. if (self.croperTarget) {
  550. // 画布绘制图片
  551. self.ctx.drawImage(self.croperTarget, self.imgLeft, self.imgTop, self.scaleWidth, self.scaleHeight);
  552. }
  553. isFunction(self.onBeforeDraw) && self.onBeforeDraw(self.ctx, self);
  554. self.setBoundStyle(); // 设置边界样式
  555. self.ctx.draw();
  556. return self
  557. };
  558. self.pushOrign = function (src) {
  559. self.src = src;
  560. isFunction(self.onBeforeImageLoad) && self.onBeforeImageLoad(self.ctx, self);
  561. wx.getImageInfo({
  562. src: src,
  563. success: function success (res) {
  564. var innerAspectRadio = res.width / res.height;
  565. self.croperTarget = res.path;
  566. if (innerAspectRadio < width / height) {
  567. self.rectX = x;
  568. self.baseWidth = width;
  569. self.baseHeight = width / innerAspectRadio;
  570. self.rectY = y - Math.abs((height - self.baseHeight) / 2);
  571. } else {
  572. self.rectY = y;
  573. self.baseWidth = height * innerAspectRadio;
  574. self.baseHeight = height;
  575. self.rectX = x - Math.abs((width - self.baseWidth) / 2);
  576. }
  577. self.imgLeft = self.rectX;
  578. self.imgTop = self.rectY;
  579. self.scaleWidth = self.baseWidth;
  580. self.scaleHeight = self.baseHeight;
  581. self.updateCanvas();
  582. isFunction(self.onImageLoad) && self.onImageLoad(self.ctx, self);
  583. }
  584. });
  585. self.update();
  586. return self
  587. };
  588. self.getCropperBase64 = function (done) {
  589. if ( done === void 0 ) done = function () {};
  590. CanvasToBase64.convertToBMP({
  591. canvasId: id,
  592. x: x,
  593. y: y,
  594. width: width,
  595. height: height
  596. }, done);
  597. };
  598. self.getCropperImage = function () {
  599. var args = [], len = arguments.length;
  600. while ( len-- ) args[ len ] = arguments[ len ];
  601. var ARG_TYPE = toString.call(args[0]);
  602. var fn = args[args.length - 1];
  603. switch (ARG_TYPE) {
  604. case '[object Object]':
  605. var ref = args[0];
  606. var quality = ref.quality; if ( quality === void 0 ) quality = 10;
  607. if (typeof (quality) !== 'number') {
  608. console.error(("quality:" + quality + " is invalid"));
  609. } else if (quality < 0 || quality > 10) {
  610. console.error("quality should be ranged in 0 ~ 10");
  611. }
  612. wx.canvasToTempFilePath({
  613. canvasId: id,
  614. x: x,
  615. y: y,
  616. width: width,
  617. height: height,
  618. destWidth: width * quality / (deviceRadio * 10),
  619. destHeight: height * quality / (deviceRadio * 10),
  620. success: function success (res) {
  621. isFunction(fn) && fn.call(self, res.tempFilePath);
  622. },
  623. fail: function fail (res) {
  624. isFunction(fn) && fn.call(self, null);
  625. }
  626. }); break
  627. case '[object Function]':
  628. wx.canvasToTempFilePath({
  629. canvasId: id,
  630. x: x,
  631. y: y,
  632. width: width,
  633. height: height,
  634. destWidth: width / deviceRadio,
  635. destHeight: height / deviceRadio,
  636. success: function success (res) {
  637. isFunction(fn) && fn.call(self, res.tempFilePath);
  638. },
  639. fail: function fail (res) {
  640. isFunction(fn) && fn.call(self, null);
  641. }
  642. }); break
  643. }
  644. return self
  645. };
  646. }
  647. /**
  648. * 获取最新缩放值
  649. * @param oldScale 上一次触摸结束后的缩放值
  650. * @param oldDistance 上一次触摸结束后的双指距离
  651. * @param zoom 缩放系数
  652. * @param touch0 第一指touch对象
  653. * @param touch1 第二指touch对象
  654. * @returns {*}
  655. */
  656. var getNewScale = function (oldScale, oldDistance, zoom, touch0, touch1) {
  657. var xMove, yMove, newDistance;
  658. // 计算二指最新距离
  659. xMove = Math.round(touch1.x - touch0.x);
  660. yMove = Math.round(touch1.y - touch0.y);
  661. newDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));
  662. return oldScale + 0.001 * zoom * (newDistance - oldDistance)
  663. };
  664. function update () {
  665. var self = this;
  666. if (!self.src) { return }
  667. self.__oneTouchStart = function (touch) {
  668. self.touchX0 = Math.round(touch.x);
  669. self.touchY0 = Math.round(touch.y);
  670. };
  671. self.__oneTouchMove = function (touch) {
  672. var xMove, yMove;
  673. // 计算单指移动的距离
  674. if (self.touchended) {
  675. return self.updateCanvas()
  676. }
  677. xMove = Math.round(touch.x - self.touchX0);
  678. yMove = Math.round(touch.y - self.touchY0);
  679. var imgLeft = Math.round(self.rectX + xMove);
  680. var imgTop = Math.round(self.rectY + yMove);
  681. self.outsideBound(imgLeft, imgTop);
  682. self.updateCanvas();
  683. };
  684. self.__twoTouchStart = function (touch0, touch1) {
  685. var xMove, yMove, oldDistance;
  686. self.touchX1 = Math.round(self.rectX + self.scaleWidth / 2);
  687. self.touchY1 = Math.round(self.rectY + self.scaleHeight / 2);
  688. // 计算两指距离
  689. xMove = Math.round(touch1.x - touch0.x);
  690. yMove = Math.round(touch1.y - touch0.y);
  691. oldDistance = Math.round(Math.sqrt(xMove * xMove + yMove * yMove));
  692. self.oldDistance = oldDistance;
  693. };
  694. self.__twoTouchMove = function (touch0, touch1) {
  695. var oldScale = self.oldScale;
  696. var oldDistance = self.oldDistance;
  697. var scale = self.scale;
  698. var zoom = self.zoom;
  699. self.newScale = getNewScale(oldScale, oldDistance, zoom, touch0, touch1);
  700. // 设定缩放范围
  701. self.newScale <= 1 && (self.newScale = 1);
  702. self.newScale >= scale && (self.newScale = scale);
  703. self.scaleWidth = Math.round(self.newScale * self.baseWidth);
  704. self.scaleHeight = Math.round(self.newScale * self.baseHeight);
  705. var imgLeft = Math.round(self.touchX1 - self.scaleWidth / 2);
  706. var imgTop = Math.round(self.touchY1 - self.scaleHeight / 2);
  707. self.outsideBound(imgLeft, imgTop);
  708. self.updateCanvas();
  709. };
  710. self.__xtouchEnd = function () {
  711. self.oldScale = self.newScale;
  712. self.rectX = self.imgLeft;
  713. self.rectY = self.imgTop;
  714. };
  715. }
  716. var handle = {
  717. // 图片手势初始监测
  718. touchStart: function touchStart (e) {
  719. var self = this;
  720. var ref = e.touches;
  721. var touch0 = ref[0];
  722. var touch1 = ref[1];
  723. setTouchState(self, true, null, null);
  724. // 计算第一个触摸点的位置,并参照改点进行缩放
  725. self.__oneTouchStart(touch0);
  726. // 两指手势触发
  727. if (e.touches.length >= 2) {
  728. self.__twoTouchStart(touch0, touch1);
  729. }
  730. },
  731. // 图片手势动态缩放
  732. touchMove: function touchMove (e) {
  733. var self = this;
  734. var ref = e.touches;
  735. var touch0 = ref[0];
  736. var touch1 = ref[1];
  737. setTouchState(self, null, true);
  738. // 单指手势时触发
  739. if (e.touches.length === 1) {
  740. self.__oneTouchMove(touch0);
  741. }
  742. // 两指手势触发
  743. if (e.touches.length >= 2) {
  744. self.__twoTouchMove(touch0, touch1);
  745. }
  746. },
  747. touchEnd: function touchEnd (e) {
  748. var self = this;
  749. setTouchState(self, false, false, true);
  750. self.__xtouchEnd();
  751. }
  752. };
  753. function cut () {
  754. var self = this;
  755. var boundWidth = self.width; // 裁剪框默认宽度,即整个画布宽度
  756. var boundHeight = self.height;
  757. // 裁剪框默认高度,即整个画布高度
  758. var ref = self.cut;
  759. var x = ref.x; if ( x === void 0 ) x = 0;
  760. var y = ref.y; if ( y === void 0 ) y = 0;
  761. var width = ref.width; if ( width === void 0 ) width = boundWidth;
  762. var height = ref.height; if ( height === void 0 ) height = boundHeight;
  763. /**
  764. * 设置边界
  765. * @param imgLeft 图片左上角横坐标值
  766. * @param imgTop 图片左上角纵坐标值
  767. */
  768. self.outsideBound = function (imgLeft, imgTop) {
  769. self.imgLeft = imgLeft >= x
  770. ? x
  771. : self.scaleWidth + imgLeft - x <= width
  772. ? x + width - self.scaleWidth
  773. : imgLeft;
  774. self.imgTop = imgTop >= y
  775. ? y
  776. : self.scaleHeight + imgTop - y <= height
  777. ? y + height - self.scaleHeight
  778. : imgTop;
  779. };
  780. /**
  781. * 设置边界样式
  782. * @param color 边界颜色
  783. */
  784. self.setBoundStyle = function (ref) {
  785. if ( ref === void 0 ) ref = {};
  786. var color = ref.color; if ( color === void 0 ) color = '#04b00f';
  787. var mask = ref.mask; if ( mask === void 0 ) mask = 'rgba(0, 0, 0, 0.3)';
  788. var lineWidth = ref.lineWidth; if ( lineWidth === void 0 ) lineWidth = 1;
  789. var boundOption = [
  790. {
  791. start: { x: x - lineWidth, y: y + 10 - lineWidth },
  792. step1: { x: x - lineWidth, y: y - lineWidth },
  793. step2: { x: x + 10 - lineWidth, y: y - lineWidth }
  794. },
  795. {
  796. start: { x: x - lineWidth, y: y + height - 10 + lineWidth },
  797. step1: { x: x - lineWidth, y: y + height + lineWidth },
  798. step2: { x: x + 10 - lineWidth, y: y + height + lineWidth }
  799. },
  800. {
  801. start: { x: x + width - 10 + lineWidth, y: y - lineWidth },
  802. step1: { x: x + width + lineWidth, y: y - lineWidth },
  803. step2: { x: x + width + lineWidth, y: y + 10 - lineWidth }
  804. },
  805. {
  806. start: { x: x + width + lineWidth, y: y + height - 10 + lineWidth },
  807. step1: { x: x + width + lineWidth, y: y + height + lineWidth },
  808. step2: { x: x + width - 10 + lineWidth, y: y + height + lineWidth }
  809. }
  810. ];
  811. // 绘制半透明层
  812. self.ctx.beginPath();
  813. self.ctx.setFillStyle(mask);
  814. self.ctx.fillRect(0, 0, x, boundHeight);
  815. self.ctx.fillRect(x, 0, width, y);
  816. self.ctx.fillRect(x, y + height, width, boundHeight - y - height);
  817. self.ctx.fillRect(x + width, 0, boundWidth - x - width, boundHeight);
  818. self.ctx.fill();
  819. boundOption.forEach(function (op) {
  820. self.ctx.beginPath();
  821. self.ctx.setStrokeStyle(color);
  822. self.ctx.setLineWidth(lineWidth);
  823. self.ctx.moveTo(op.start.x, op.start.y);
  824. self.ctx.lineTo(op.step1.x, op.step1.y);
  825. self.ctx.lineTo(op.step2.x, op.step2.y);
  826. self.ctx.stroke();
  827. });
  828. };
  829. }
  830. var version = "1.2.0";
  831. var WeCropper = function WeCropper (params) {
  832. var self = this;
  833. var _default = {};
  834. validator(self, DEFAULT);
  835. Object.keys(DEFAULT).forEach(function (key) {
  836. _default[key] = DEFAULT[key].default;
  837. });
  838. Object.assign(self, _default, params);
  839. self.prepare();
  840. self.attachPage();
  841. self.createCtx();
  842. self.observer();
  843. self.cutt();
  844. self.methods();
  845. self.init();
  846. self.update();
  847. return self
  848. };
  849. WeCropper.prototype.init = function init () {
  850. var self = this;
  851. var src = self.src;
  852. self.version = version;
  853. typeof self.onReady === 'function' && self.onReady(self.ctx, self);
  854. if (src) {
  855. self.pushOrign(src);
  856. }
  857. setTouchState(self, false, false, false);
  858. self.oldScale = 1;
  859. self.newScale = 1;
  860. return self
  861. };
  862. Object.assign(WeCropper.prototype, handle);
  863. WeCropper.prototype.prepare = prepare;
  864. WeCropper.prototype.observer = observer;
  865. WeCropper.prototype.methods = methods;
  866. WeCropper.prototype.cutt = cut;
  867. WeCropper.prototype.update = update;
  868. return WeCropper;
  869. })));
  870. 复制代码
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/123887
推荐阅读
相关标签
  

闽ICP备14008679号