From e19c63b2d35a7f5368701ccbc6771f4cc1e025cf Mon Sep 17 00:00:00 2001 From: zhang_li <2235006734@qqq.com> Date: Fri, 26 Apr 2024 09:31:51 +0800 Subject: [PATCH 1/2] =?UTF-8?q?app=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/point/index.vue | 58 +++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/src/pages/point/index.vue b/src/pages/point/index.vue index f13c89e6..5e90d8e2 100644 --- a/src/pages/point/index.vue +++ b/src/pages/point/index.vue @@ -3,12 +3,17 @@ 打印 + + + + + + +
+
+
+
+
物品代码
+
replaceItemNumber
+
+
+
物品名称
+
replaceItemName
+
+
+
供应商
+
replaceSupplierCode
+
+
+
包装号
+
replacePackageCode
+
+
+
订单行
+
replaceReferenceOrderRow
+
+
+
+
+ +
+
+
订单号
+
replaceReferenceOrderCode
+
+
+
数量
+
replaceAmount
+
+
+
+
+ + diff --git a/src/pages.json b/src/pages.json index 71d90c9c..af76b466 100644 --- a/src/pages.json +++ b/src/pages.json @@ -2021,25 +2021,13 @@ "navigationBarTitleText": "翻包任务详情", "enablePullDownRefresh": false } - }, - // ,{ - // "path": "pages/point/index", - // "style": { - // "navigationBarTitleText": "打印", - // "enablePullDownRefresh": false - // } - // }, - { - "path": "pages/print/index", + },{ + "path": "pages/point/index", "style": { "navigationBarTitleText": "打印", "enablePullDownRefresh": false } } - - - - ], "globalStyle": { "navigationBarTextStyle": "white", diff --git a/src/pages/point/index.vue b/src/pages/point/index.vue index 5e90d8e2..db4a8f97 100644 --- a/src/pages/point/index.vue +++ b/src/pages/point/index.vue @@ -1,34 +1,77 @@ \ No newline at end of file diff --git a/src/uni_modules/lime-qrcode/changelog.md b/src/uni_modules/lime-qrcode/changelog.md new file mode 100644 index 00000000..c3fdbbf4 --- /dev/null +++ b/src/uni_modules/lime-qrcode/changelog.md @@ -0,0 +1,31 @@ +## 0.1.5(2024-04-14) +- fix: 修复缺少依赖 +## 0.1.4(2024-04-10) +- chore: 更新文档 +## 0.1.3(2024-04-01) +- chore: 兼容uniapp x ios(app-js) +## 0.1.2(2023-12-14) +- fix: uvue 引入 API 自定义包出错 +## 0.1.1(2023-12-11) +- chore: uvue的二维码API独立,需要单独下载 +## 0.1.0(2023-12-07) +- fix: 修复因utssdk目录导致无法运行 +## 0.0.9(2023-12-06) +- feat: 支持uvue +## 0.0.8(2023-12-06) +- feat: 支持uvue +## 0.0.7(2023-12-06) +- feat: 支持uvue +## 0.0.6(2023-12-06) +- feat: 支持uvue +## 0.0.5(2023-07-30) +- fix: 修复再次生成前没有清空,导致图形叠加 +## 0.0.4(2023-07-27) +- fix: 修复相同尺寸无法再次生成 +## 0.0.3(2023-06-09) +- feat: 支持通过`@vue/composition-api`在`vue2`上使用 +- chore: 更新文档 +## 0.0.2(2023-06-08) +- chore: 更新文档 +## 0.0.1(2023-06-08) +- 首次 diff --git a/src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue b/src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue new file mode 100644 index 00000000..abf129ea --- /dev/null +++ b/src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.uvue @@ -0,0 +1,234 @@ + + + \ No newline at end of file diff --git a/src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue b/src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue new file mode 100644 index 00000000..f228583a --- /dev/null +++ b/src/uni_modules/lime-qrcode/components/l-qrcode/l-qrcode.vue @@ -0,0 +1,223 @@ + + + \ No newline at end of file diff --git a/src/uni_modules/lime-qrcode/components/l-qrcode/props.ts b/src/uni_modules/lime-qrcode/components/l-qrcode/props.ts new file mode 100644 index 00000000..fb4d101e --- /dev/null +++ b/src/uni_modules/lime-qrcode/components/l-qrcode/props.ts @@ -0,0 +1,36 @@ +// @ts-nocheck +// import type { PropType } from './vue' +export default { + value: String, + icon: String, + size: { + type: [Number, String], + default: 160 + }, + iconSize: { + type: [Number, String], + default: 40 + }, + marginSize: Number, + color: { + type: String, + default: '#000' + }, + bgColor: { + type: String, + default: 'transparent' + }, + bordered: { + type: Boolean, + default: true + }, + errorLevel: { + type: String as PropType<'L'|'M'|'Q'|'H'>, + default: 'M' // 'L' | 'M' | 'Q' | 'H' + }, + useCanvasToTempFilePath: Boolean + // status: { + // type: String as PropType<'active'|'expired'|'loading'>, + // default: 'active' // active | expired | loading + // } +} \ No newline at end of file diff --git a/src/uni_modules/lime-qrcode/components/l-qrcode/qrcode.js b/src/uni_modules/lime-qrcode/components/l-qrcode/qrcode.js new file mode 100644 index 00000000..b04047b7 --- /dev/null +++ b/src/uni_modules/lime-qrcode/components/l-qrcode/qrcode.js @@ -0,0 +1,6 @@ +function e(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function t(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function n(e,n,r){return n&&t(e.prototype,n),r&&t(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function r(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);t>n;n++)r[n]=e[n];return r}function o(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return i(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?i(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return e.length>r?{done:!1,value:e[r++]}:{done:!0}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,s=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){u=!0,a=e},f:function(){try{s||null==n.return||n.return()}finally{if(u)throw a}}}} +/** + * @license QR Code generator library (TypeScript) + * Copyright (c) Project Nayuki. + * SPDX-License-Identifier: MIT + */var a;!function(t){var r=function(){function r(t,n,i,o){if(e(this,r),this.version=void 0,this.errorCorrectionLevel=void 0,this.size=void 0,this.mask=void 0,this.modules=[],this.isFunction=[],this.version=t,this.errorCorrectionLevel=n,r.MIN_VERSION>t||t>r.MAX_VERSION)throw new RangeError("Version value out of range");if(-1>o||o>7)throw new RangeError("Mask value out of range");this.size=4*t+17;for(var a=[],u=0;this.size>u;u++)a.push(!1);for(var l=0;this.size>l;l++)this.modules.push(a.slice()),this.isFunction.push(a.slice());this.drawFunctionPatterns();var h=this.addEccAndInterleave(i);if(this.drawCodewords(h),-1==o)for(var c=1e9,f=0;8>f;f++){this.applyMask(f),this.drawFormatBits(f);var v=this.getPenaltyScore();c>v&&(o=f,c=v),this.applyMask(f)}s(o>=0&&7>=o),this.mask=o,this.applyMask(o),this.drawFormatBits(o),this.isFunction=[]}return n(r,[{key:"getModule",value:function(e,t){return e>=0&&this.size>e&&t>=0&&this.size>t&&this.modules[t][e]}},{key:"getModules",value:function(){return this.modules}},{key:"drawFunctionPatterns",value:function(){for(var e=0;this.size>e;e++)this.setFunctionModule(6,e,e%2==0),this.setFunctionModule(e,6,e%2==0);this.drawFinderPattern(3,3),this.drawFinderPattern(this.size-4,3),this.drawFinderPattern(3,this.size-4);for(var t=this.getAlignmentPatternPositions(),n=t.length,r=0;n>r;r++)for(var i=0;n>i;i++)0==r&&0==i||0==r&&i==n-1||r==n-1&&0==i||this.drawAlignmentPattern(t[r],t[i]);this.drawFormatBits(0),this.drawVersion()}},{key:"drawFormatBits",value:function(e){for(var t=this.errorCorrectionLevel.formatBits<<3|e,n=t,r=0;10>r;r++)n=n<<1^1335*(n>>>9);var i=21522^(t<<10|n);s(i>>>15==0);for(var o=0;5>=o;o++)this.setFunctionModule(8,o,a(i,o));this.setFunctionModule(8,7,a(i,6)),this.setFunctionModule(8,8,a(i,7)),this.setFunctionModule(7,8,a(i,8));for(var u=9;15>u;u++)this.setFunctionModule(14-u,8,a(i,u));for(var l=0;8>l;l++)this.setFunctionModule(this.size-1-l,8,a(i,l));for(var h=8;15>h;h++)this.setFunctionModule(8,this.size-15+h,a(i,h));this.setFunctionModule(8,this.size-8,!0)}},{key:"drawVersion",value:function(){if(this.version>=7){for(var e=this.version,t=0;12>t;t++)e=e<<1^7973*(e>>>11);var n=this.version<<12|e;s(n>>>18==0);for(var r=0;18>r;r++){var i=a(n,r),o=this.size-11+r%3,u=Math.floor(r/3);this.setFunctionModule(o,u,i),this.setFunctionModule(u,o,i)}}}},{key:"drawFinderPattern",value:function(e,t){for(var n=-4;4>=n;n++)for(var r=-4;4>=r;r++){var i=Math.max(Math.abs(r),Math.abs(n)),o=e+r,a=t+n;o>=0&&this.size>o&&a>=0&&this.size>a&&this.setFunctionModule(o,a,2!=i&&4!=i)}}},{key:"drawAlignmentPattern",value:function(e,t){for(var n=-2;2>=n;n++)for(var r=-2;2>=r;r++)this.setFunctionModule(e+r,t+n,1!=Math.max(Math.abs(r),Math.abs(n)))}},{key:"setFunctionModule",value:function(e,t,n){this.modules[t][e]=n,this.isFunction[t][e]=!0}},{key:"addEccAndInterleave",value:function(e){var t=this.version,n=this.errorCorrectionLevel;if(e.length!=r.getNumDataCodewords(t,n))throw new RangeError("Invalid argument");for(var i=r.NUM_ERROR_CORRECTION_BLOCKS[n.ordinal][t],o=r.ECC_CODEWORDS_PER_BLOCK[n.ordinal][t],a=Math.floor(r.getNumRawDataModules(t)/8),u=i-a%i,l=Math.floor(a/i),h=[],c=r.reedSolomonComputeDivisor(o),f=0,v=0;i>f;f++){var d=e.slice(v,v+l-o+(u>f?0:1));v+=d.length;var m=r.reedSolomonComputeRemainder(d,c);u>f&&d.push(0),h.push(d.concat(m))}for(var g=[],y=function(e){h.forEach((function(t,n){e==l-o&&u>n||g.push(t[e])}))},E=0;h[0].length>E;E++)y(E);return s(g.length==a),g}},{key:"drawCodewords",value:function(e){if(e.length!=Math.floor(r.getNumRawDataModules(this.version)/8))throw new RangeError("Invalid argument");for(var t=0,n=this.size-1;n>=1;n-=2){6==n&&(n=5);for(var i=0;this.size>i;i++)for(var o=0;2>o;o++){var u=n-o,l=0==(n+1&2)?this.size-1-i:i;!this.isFunction[l][u]&&8*e.length>t&&(this.modules[l][u]=a(e[t>>>3],7-(7&t)),t++)}}s(t==8*e.length)}},{key:"applyMask",value:function(e){if(0>e||e>7)throw new RangeError("Mask value out of range");for(var t=0;this.size>t;t++)for(var n=0;this.size>n;n++){var r=void 0;switch(e){case 0:r=(n+t)%2==0;break;case 1:r=t%2==0;break;case 2:r=n%3==0;break;case 3:r=(n+t)%3==0;break;case 4:r=(Math.floor(n/3)+Math.floor(t/2))%2==0;break;case 5:r=n*t%2+n*t%3==0;break;case 6:r=(n*t%2+n*t%3)%2==0;break;case 7:r=((n+t)%2+n*t%3)%2==0;break;default:throw Error("Unreachable")}!this.isFunction[t][n]&&r&&(this.modules[t][n]=!this.modules[t][n])}}},{key:"getPenaltyScore",value:function(){for(var e=0,t=0;this.size>t;t++){for(var n=!1,i=0,a=[0,0,0,0,0,0,0],u=0;this.size>u;u++)this.modules[t][u]==n?5==++i?e+=r.PENALTY_N1:i>5&&e++:(this.finderPenaltyAddHistory(i,a),n||(e+=this.finderPenaltyCountPatterns(a)*r.PENALTY_N3),n=this.modules[t][u],i=1);e+=this.finderPenaltyTerminateAndCount(n,i,a)*r.PENALTY_N3}for(var l=0;this.size>l;l++){for(var h=!1,c=0,f=[0,0,0,0,0,0,0],v=0;this.size>v;v++)this.modules[v][l]==h?5==++c?e+=r.PENALTY_N1:c>5&&e++:(this.finderPenaltyAddHistory(c,f),h||(e+=this.finderPenaltyCountPatterns(f)*r.PENALTY_N3),h=this.modules[v][l],c=1);e+=this.finderPenaltyTerminateAndCount(h,c,f)*r.PENALTY_N3}for(var d=0;this.size-1>d;d++)for(var m=0;this.size-1>m;m++){var g=this.modules[d][m];g==this.modules[d][m+1]&&g==this.modules[d+1][m]&&g==this.modules[d+1][m+1]&&(e+=r.PENALTY_N2)}var y,E=0,w=o(this.modules);try{for(w.s();!(y=w.n()).done;){E=y.value.reduce((function(e,t){return e+(t?1:0)}),E)}}catch(e){w.e(e)}finally{w.f()}var M=this.size*this.size,C=Math.ceil(Math.abs(20*E-10*M)/M)-1;return s(C>=0&&9>=C),s((e+=C*r.PENALTY_N4)>=0&&2568888>=e),e}},{key:"getAlignmentPatternPositions",value:function(){if(1==this.version)return[];for(var e=Math.floor(this.version/7)+2,t=32==this.version?26:2*Math.ceil((4*this.version+4)/(2*e-2)),n=[6],r=this.size-7;e>n.length;r-=t)n.splice(1,0,r);return n}},{key:"finderPenaltyCountPatterns",value:function(e){var t=e[1];s(3*this.size>=t);var n=t>0&&e[2]==t&&e[3]==3*t&&e[4]==t&&e[5]==t;return(!n||4*t>e[0]||t>e[6]?0:1)+(!n||4*t>e[6]||t>e[0]?0:1)}},{key:"finderPenaltyTerminateAndCount",value:function(e,t,n){return e&&(this.finderPenaltyAddHistory(t,n),t=0),this.finderPenaltyAddHistory(t+=this.size,n),this.finderPenaltyCountPatterns(n)}},{key:"finderPenaltyAddHistory",value:function(e,t){0==t[0]&&(e+=this.size),t.pop(),t.unshift(e)}}],[{key:"encodeText",value:function(e,n){var i=t.QrSegment.makeSegments(e);return r.encodeSegments(i,n)}},{key:"encodeBinary",value:function(e,n){var i=t.QrSegment.makeBytes(e);return r.encodeSegments([i],n)}},{key:"encodeSegments",value:function(e,t){var n,a,l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,h=arguments.length>3&&void 0!==arguments[3]?arguments[3]:40,c=arguments.length>4&&void 0!==arguments[4]?arguments[4]:-1,f=5>=arguments.length||void 0===arguments[5]||arguments[5];if(r.MIN_VERSION>l||l>h||h>r.MAX_VERSION||-1>c||c>7)throw new RangeError("Invalid value");for(n=l;;n++){var v=8*r.getNumDataCodewords(n,t),d=u.getTotalBits(e,n);if(v>=d){a=d;break}if(n>=h)throw new RangeError("Data too long")}for(var m=0,g=[r.Ecc.MEDIUM,r.Ecc.QUARTILE,r.Ecc.HIGH];g.length>m;m++){var y=g[m];f&&a<=8*r.getNumDataCodewords(n,y)&&(t=y)}var E,w=[],M=o(e);try{for(M.s();!(E=M.n()).done;){var C=E.value;i(C.mode.modeBits,4,w),i(C.numChars,C.mode.numCharCountBits(n),w);var R,A=o(C.getData());try{for(A.s();!(R=A.n()).done;){var p=R.value;w.push(p)}}catch(e){A.e(e)}finally{A.f()}}}catch(e){M.e(e)}finally{M.f()}s(w.length==a);var P=8*r.getNumDataCodewords(n,t);s(P>=w.length),i(0,Math.min(4,P-w.length),w),i(0,(8-w.length%8)%8,w),s(w.length%8==0);for(var N=236;P>w.length;N^=253)i(N,8,w);for(var k=[];w.length>8*k.length;)k.push(0);return w.forEach((function(e,t){return k[t>>>3]|=e<<7-(7&t)})),new r(n,t,k,c)}},{key:"getNumRawDataModules",value:function(e){if(r.MIN_VERSION>e||e>r.MAX_VERSION)throw new RangeError("Version number out of range");var t=(16*e+128)*e+64;if(e>=2){var n=Math.floor(e/7)+2;t-=(25*n-10)*n-55,7>e||(t-=36)}return s(t>=208&&29648>=t),t}},{key:"getNumDataCodewords",value:function(e,t){return Math.floor(r.getNumRawDataModules(e)/8)-r.ECC_CODEWORDS_PER_BLOCK[t.ordinal][e]*r.NUM_ERROR_CORRECTION_BLOCKS[t.ordinal][e]}},{key:"reedSolomonComputeDivisor",value:function(e){if(1>e||e>255)throw new RangeError("Degree out of range");for(var t=[],n=0;e-1>n;n++)t.push(0);t.push(1);for(var i=1,o=0;e>o;o++){for(var a=0;t.length>a;a++)t[a]=r.reedSolomonMultiply(t[a],i),t.length>a+1&&(t[a]^=t[a+1]);i=r.reedSolomonMultiply(i,2)}return t}},{key:"reedSolomonComputeRemainder",value:function(e,t){var n,i=t.map((function(e){return 0})),a=o(e);try{var s=function(){var e=n.value^i.shift();i.push(0),t.forEach((function(t,n){return i[n]^=r.reedSolomonMultiply(t,e)}))};for(a.s();!(n=a.n()).done;)s()}catch(e){a.e(e)}finally{a.f()}return i}},{key:"reedSolomonMultiply",value:function(e,t){if(e>>>8!=0||t>>>8!=0)throw new RangeError("Byte out of range");for(var n=0,r=7;r>=0;r--)n=n<<1^285*(n>>>7),n^=(t>>>r&1)*e;return s(n>>>8==0),n}}]),r}();function i(e,t,n){if(0>t||t>31||e>>>t!=0)throw new RangeError("Value out of range");for(var r=t-1;r>=0;r--)n.push(e>>>r&1)}function a(e,t){return 0!=(e>>>t&1)}function s(e){if(!e)throw Error("Assertion error")}r.MIN_VERSION=1,r.MAX_VERSION=40,r.PENALTY_N1=3,r.PENALTY_N2=3,r.PENALTY_N3=40,r.PENALTY_N4=10,r.ECC_CODEWORDS_PER_BLOCK=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],r.NUM_ERROR_CORRECTION_BLOCKS=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]],t.QrCode=r;var u=function(){function t(n,r,i){if(e(this,t),this.mode=void 0,this.numChars=void 0,this.bitData=void 0,this.mode=n,this.numChars=r,this.bitData=i,0>r)throw new RangeError("Invalid argument");this.bitData=i.slice()}return n(t,[{key:"getData",value:function(){return this.bitData.slice()}}],[{key:"makeBytes",value:function(e){var n,r=[],a=o(e);try{for(a.s();!(n=a.n()).done;){i(n.value,8,r)}}catch(e){a.e(e)}finally{a.f()}return new t(t.Mode.BYTE,e.length,r)}},{key:"makeNumeric",value:function(e){if(!t.isNumeric(e))throw new RangeError("String contains non-numeric characters");for(var n=[],r=0;e.length>r;){var o=Math.min(e.length-r,3);i(parseInt(e.substring(r,r+o),10),3*o+1,n),r+=o}return new t(t.Mode.NUMERIC,e.length,n)}},{key:"makeAlphanumeric",value:function(e){if(!t.isAlphanumeric(e))throw new RangeError("String contains unencodable characters in alphanumeric mode");var n,r=[];for(n=0;e.length>=n+2;n+=2){var o=45*t.ALPHANUMERIC_CHARSET.indexOf(e.charAt(n));i(o+=t.ALPHANUMERIC_CHARSET.indexOf(e.charAt(n+1)),11,r)}return e.length>n&&i(t.ALPHANUMERIC_CHARSET.indexOf(e.charAt(n)),6,r),new t(t.Mode.ALPHANUMERIC,e.length,r)}},{key:"makeSegments",value:function(e){return""==e?[]:t.isNumeric(e)?[t.makeNumeric(e)]:t.isAlphanumeric(e)?[t.makeAlphanumeric(e)]:[t.makeBytes(t.toUtf8ByteArray(e))]}},{key:"makeEci",value:function(e){var n=[];if(0>e)throw new RangeError("ECI assignment value out of range");if(128>e)i(e,8,n);else if(16384>e)i(2,2,n),i(e,14,n);else{if(e>=1e6)throw new RangeError("ECI assignment value out of range");i(6,3,n),i(e,21,n)}return new t(t.Mode.ECI,0,n)}},{key:"isNumeric",value:function(e){return t.NUMERIC_REGEX.test(e)}},{key:"isAlphanumeric",value:function(e){return t.ALPHANUMERIC_REGEX.test(e)}},{key:"getTotalBits",value:function(e,t){var n,r=0,i=o(e);try{for(i.s();!(n=i.n()).done;){var a=n.value,s=a.mode.numCharCountBits(t);if(a.numChars>=1<n;n++)"%"!=e.charAt(n)?t.push(e.charCodeAt(n)):(t.push(parseInt(e.substring(n+1,n+3),16)),n+=2);return t}}]),t}();u.NUMERIC_REGEX=/^[0-9]*$/,u.ALPHANUMERIC_REGEX=/^[A-Z0-9 $%*+.\/:-]*$/,u.ALPHANUMERIC_CHARSET="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:",t.QrSegment=u}(a||(a={})),function(t){!function(t){var r=n((function t(n,r){e(this,t),this.ordinal=void 0,this.formatBits=void 0,this.ordinal=n,this.formatBits=r}));r.LOW=new r(0,1),r.MEDIUM=new r(1,0),r.QUARTILE=new r(2,3),r.HIGH=new r(3,2),t.Ecc=r}(t.QrCode||(t.QrCode={}))}(a||(a={})),function(t){!function(t){var r=function(){function t(n,r){e(this,t),this.modeBits=void 0,this.numBitsCharCount=void 0,this.modeBits=n,this.numBitsCharCount=r}return n(t,[{key:"numCharCountBits",value:function(e){return this.numBitsCharCount[Math.floor((e+7)/17)]}}]),t}();r.NUMERIC=new r(1,[10,12,14]),r.ALPHANUMERIC=new r(2,[9,11,13]),r.BYTE=new r(4,[8,16,16]),r.KANJI=new r(8,[8,10,12]),r.ECI=new r(7,[0,0,0]),t.Mode=r}(t.QrSegment||(t.QrSegment={}))}(a||(a={}));var s=a,u=["value","size","level","bgColor","fgColor","includeMargin","marginSize","imageSettings"],l={L:s.QrCode.Ecc.LOW,M:s.QrCode.Ecc.MEDIUM,Q:s.QrCode.Ecc.QUARTILE,H:s.QrCode.Ecc.HIGH};function h(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=[];return e.forEach((function(e,r){var i=null;e.forEach((function(o,a){if(!o&&null!==i)return n.push("M".concat(i+t," ").concat(r+t,"h").concat(a-i,"v1H").concat(i+t,"z")),void(i=null);if(a!==e.length-1)o&&null===i&&(i=a);else{if(!o)return;n.push(null===i?"M".concat(a+t,",").concat(r+t," h1v1H").concat(a+t,"z"):"M".concat(i+t,",").concat(r+t," h").concat(a+1-i,"v1H").concat(i+t,"z"))}}))})),n.join("")}function c(e,t){return e.slice().map((function(e,n){return t.y>n||n>=t.y+t.h?e:e.map((function(e,n){return(t.x>n||n>=t.x+t.w)&&e}))}))}var f=function(){function t(n,r){var i=this;for(var o in e(this,t),this.canvas=void 0,this.pixelRatio="undefined"!=typeof window?window.devicePixelRatio:1,this.path2D=!0,this.SUPPORTS_PATH2D=void 0,this.createImage=function(){return new Image},this.createPath2D=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,t="createPath2D";return i.canvas&&t in i.canvas?i.canvas[t](e):new Path2D(e)},this.canvas=n,r)o in this&&(this[o]=r[o]);this.SUPPORTS_PATH2D=function(){try{i.createPath2D()}catch(e){return!1}return!0}()}return n(t,[{key:"render",value:function(e,t){var n=this,i=e.value,o=e.size,a=void 0===o?128:o,f=e.level,v=void 0===f?"L":f,d=e.bgColor,m=void 0===d?"#FFFFFF":d,g=e.fgColor,y=void 0===g?"#000000":g,E=e.includeMargin,w=void 0!==E&&E,M=e.marginSize,C=e.imageSettings,R=(r(e,u),null==C?void 0:C.src),A=this.canvas,p=A.getContext("2d");if(p&&i){var P=s.QrCode.encodeText(i,l[v]).getModules(),N=function(e,t){return null!=t?Math.floor(t):e?4:0}(w,M),k=P.length+2*N,S=function(e,t,n,r){if(null==r)return null;var i=e.length+2*n,o=Math.floor(.1*t),a=i/t,s=(r.width||o)*a,u=(r.height||o)*a,l=null==r.x?e.length/2-s/2:r.x*a,h=null==r.y?e.length/2-u/2:r.y*a,c=null;if(r.excavate){var f=Math.floor(l),v=Math.floor(h);c={x:f,y:v,w:Math.ceil(s+l-f),h:Math.ceil(u+h-v)}}return{x:l,y:h,h:u,w:s,excavation:c}}(P,a,N,C),I=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,r=null!=S&&null!==e&&e.complete&&0!==e.naturalHeight&&0!==e.naturalWidth;r&&null!=S.excavation&&(P=c(P,S.excavation)),t&&t(P);var i=n.pixelRatio;p.clearRect(0,0,A.width,A.height),p.setTransform(1,0,0,1,0,0),A.height=A.width=a*i;var o=a/k*i;if(p.scale(o,o),p.fillStyle=m,p.fillRect(0,0,k,k),p.fillStyle=y,n.SUPPORTS_PATH2D&&n.path2D){var s=n.createPath2D(h(P,N));p.fill(s)}else P.forEach((function(e,t){e.forEach((function(e,n){e&&p.fillRect(n+N,t+N,1,1)}))}));var u=(null==e?void 0:e.path)||e;r&&p.drawImage(u,S.x+N,S.y+N,S.w,S.h),"draw"in p&&p.draw()};if(R){var b=this.createImage(A);b.onload=function(){I(b)},b.onerror=function(e){I(),console.warn(e)},b.src=R}else I()}}}]),t}();export{f as QRCodeCanvas}; diff --git a/src/uni_modules/lime-qrcode/components/l-qrcode/type.ts b/src/uni_modules/lime-qrcode/components/l-qrcode/type.ts new file mode 100644 index 00000000..a11d3796 --- /dev/null +++ b/src/uni_modules/lime-qrcode/components/l-qrcode/type.ts @@ -0,0 +1,47 @@ +export type ImageSettings = { + width: number + height: number + x?: number + y?: number + excavate: boolean +} +export type QRCodePropsTypes = { + value?: string + size?: number + fgColor?: string + level?: string + marginSize: number + includeMargin: boolean + imageSettings?: ImageSettings +} + +export type QRCodeCallback = (cells : boolean[][]) => void + +export type Excavation = { + x: number + y: number + h: number + w: number +} + + + + + +/** + * 成功回调函数定义 + */ +export type TakeSnapshotSuccessCallback = (res: TakeSnapshotSuccess) => void +/** + * 失败回调函数定义 + */ +export type TakeSnapshotFailCallback = (res: TakeSnapshotFail) => void +/** + * 完成回调函数定义 + */ +export type TakeSnapshotCompleteCallback = (res: any) => void + + +export type LQrcodeFailCallback = TakeSnapshotFailCallback +export type LQrcodeCompleteCallback = TakeSnapshotCompleteCallback +export type LQrcodeSuccessCallback = TakeSnapshotSuccessCallback \ No newline at end of file diff --git a/src/uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts b/src/uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts new file mode 100644 index 00000000..10587cde --- /dev/null +++ b/src/uni_modules/lime-qrcode/components/l-qrcode/useCanvas.ts @@ -0,0 +1,78 @@ + +// @ts-nocheck +import type { ComponentInternalInstance } from '@/uni_modules/lime-shared/vue' +import { getRect } from '@/uni_modules/lime-shared/getRect' +import { canIUseCanvas2d } from '@/uni_modules/lime-shared/canIUseCanvas2d' +export const isCanvas2d = canIUseCanvas2d() + +export async function getCanvas(canvasId: string, options: {context: ComponentInternalInstance}) { + let { context } = options + // #ifdef MP || VUE2 + if (context.proxy) context = context.proxy + // #endif + return getRect('#' + canvasId, {context, type: isCanvas2d ? 'fields': 'boundingClientRect'}).then(res => { + if(res.node){ + return res.node + } else { + const ctx = uni.createCanvasContext(canvasId, context) + return { + getContext(type: string) { + if(type == '2d') { + return ctx + } + }, + width: res.width, + height: res.height, + } + // #ifdef H5 + // canvas.value = context.proxy.$el.querySelector('#'+ canvasId) + // #endif + } + }) +} + +// #ifndef H5 || APP-NVUE +class Image { + currentSrc: string | null = null + naturalHeight: number = 0 + naturalWidth: number = 0 + width: number = 0 + height: number = 0 + tagName: string = 'IMG' + path: any = '' + crossOrigin: any = '' + referrerPolicy: any = '' + onload: () => void + onerror: () => void + constructor() {} + set src(src) { + this.currentSrc = src + uni.getImageInfo({ + src, + success: (res) => { + this.path = res.path + this.naturalWidth = this.width = res.width + this.naturalHeight = this.height = res.height + this.onload() + }, + fail: () => { + this.onerror() + } + }) + } + get src() { + return this.currentSrc + } +} +// #endif + +export function createImage(canvas: WechatMiniprogram.Canvas) { + if(canvas && canvas.createImage) { + return canvas.createImage() + } else if(typeof window != 'undefined' && window.Image) { + return new window.Image() + } + // #ifndef H5 || APP-NVUE + return new Image() + // #endif +} \ No newline at end of file diff --git a/src/uni_modules/lime-qrcode/components/l-qrcode/utils.uts b/src/uni_modules/lime-qrcode/components/l-qrcode/utils.uts new file mode 100644 index 00000000..a88a9302 --- /dev/null +++ b/src/uni_modules/lime-qrcode/components/l-qrcode/utils.uts @@ -0,0 +1,35 @@ +export function addUnit(value: any|null):string{ + if(value == null){ + return '' + } + value = `${value}` + return /^(-)?\d+(\\.\d+)?$/.test(value) ? `${value}px` : value +} + +export function unitConvert(value: any|null): number{ + if(typeof value == 'number'){ + return value as number + } + if(typeof value == 'string'){ + value = `${value}` + if(/^(-)?\d+(\\.\d+)?$/.test(value)){ + return parseFloat(value); + } + + const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g; + const results = reg.exec(value); + if (results == null) { + return 0; + } + const unit = results[3]; + const v = parseFloat(value); + if (unit == 'rpx') { + const { windowWidth } = uni.getWindowInfo() + return windowWidth / 750 * v; + } + if (unit == 'px') { + return v; + } + } + return 0; +} \ No newline at end of file diff --git a/src/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.uvue b/src/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.uvue new file mode 100644 index 00000000..93e10d2e --- /dev/null +++ b/src/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.uvue @@ -0,0 +1,140 @@ + + + \ No newline at end of file diff --git a/src/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.vue b/src/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.vue new file mode 100644 index 00000000..113ef87c --- /dev/null +++ b/src/uni_modules/lime-qrcode/components/lime-qrcode/lime-qrcode.vue @@ -0,0 +1,79 @@ + + + diff --git a/src/uni_modules/lime-qrcode/hybrid/html/index.html b/src/uni_modules/lime-qrcode/hybrid/html/index.html new file mode 100644 index 00000000..bf4def9c --- /dev/null +++ b/src/uni_modules/lime-qrcode/hybrid/html/index.html @@ -0,0 +1,77 @@ + + + + + + + lime-qrcode + + + + + + + + + \ No newline at end of file diff --git a/src/uni_modules/lime-qrcode/hybrid/html/qrcode.min.js b/src/uni_modules/lime-qrcode/hybrid/html/qrcode.min.js new file mode 100644 index 00000000..a0c79ce9 --- /dev/null +++ b/src/uni_modules/lime-qrcode/hybrid/html/qrcode.min.js @@ -0,0 +1,6 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).lime={})}(this,(function(e){"use strict";function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function n(e,t){for(var n=0;t.length>n;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function r(e,t,r){return t&&n(e.prototype,t),r&&n(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}function i(e,t){if(null==e)return{};var n,r,i=function(e,t){if(null==e)return{};var n,r,i={},o=Object.keys(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;o.length>r;r++)0>t.indexOf(n=o[r])&&Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}function o(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);t>n;n++)r[n]=e[n];return r}function a(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return o(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?o(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,i=function(){};return{s:i,n:function(){return e.length>r?{done:!1,value:e[r++]}:{done:!0}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,s=!0,u=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return s=e.done,e},e:function(e){u=!0,a=e},f:function(){try{s||null==n.return||n.return()}finally{if(u)throw a}}}} +/** + * @license QR Code generator library (TypeScript) + * Copyright (c) Project Nayuki. + * SPDX-License-Identifier: MIT + */var s;!function(e){var n=function(){function n(e,r,i,o){if(t(this,n),this.version=void 0,this.errorCorrectionLevel=void 0,this.size=void 0,this.mask=void 0,this.modules=[],this.isFunction=[],this.version=e,this.errorCorrectionLevel=r,n.MIN_VERSION>e||e>n.MAX_VERSION)throw new RangeError("Version value out of range");if(-1>o||o>7)throw new RangeError("Mask value out of range");this.size=4*e+17;for(var a=[],u=0;this.size>u;u++)a.push(!1);for(var l=0;this.size>l;l++)this.modules.push(a.slice()),this.isFunction.push(a.slice());this.drawFunctionPatterns();var h=this.addEccAndInterleave(i);if(this.drawCodewords(h),-1==o)for(var c=1e9,f=0;8>f;f++){this.applyMask(f),this.drawFormatBits(f);var d=this.getPenaltyScore();c>d&&(o=f,c=d),this.applyMask(f)}s(o>=0&&7>=o),this.mask=o,this.applyMask(o),this.drawFormatBits(o),this.isFunction=[]}return r(n,[{key:"getModule",value:function(e,t){return e>=0&&this.size>e&&t>=0&&this.size>t&&this.modules[t][e]}},{key:"getModules",value:function(){return this.modules}},{key:"drawFunctionPatterns",value:function(){for(var e=0;this.size>e;e++)this.setFunctionModule(6,e,e%2==0),this.setFunctionModule(e,6,e%2==0);this.drawFinderPattern(3,3),this.drawFinderPattern(this.size-4,3),this.drawFinderPattern(3,this.size-4);for(var t=this.getAlignmentPatternPositions(),n=t.length,r=0;n>r;r++)for(var i=0;n>i;i++)0==r&&0==i||0==r&&i==n-1||r==n-1&&0==i||this.drawAlignmentPattern(t[r],t[i]);this.drawFormatBits(0),this.drawVersion()}},{key:"drawFormatBits",value:function(e){for(var t=this.errorCorrectionLevel.formatBits<<3|e,n=t,r=0;10>r;r++)n=n<<1^1335*(n>>>9);var i=21522^(t<<10|n);s(i>>>15==0);for(var a=0;5>=a;a++)this.setFunctionModule(8,a,o(i,a));this.setFunctionModule(8,7,o(i,6)),this.setFunctionModule(8,8,o(i,7)),this.setFunctionModule(7,8,o(i,8));for(var u=9;15>u;u++)this.setFunctionModule(14-u,8,o(i,u));for(var l=0;8>l;l++)this.setFunctionModule(this.size-1-l,8,o(i,l));for(var h=8;15>h;h++)this.setFunctionModule(8,this.size-15+h,o(i,h));this.setFunctionModule(8,this.size-8,!0)}},{key:"drawVersion",value:function(){if(this.version>=7){for(var e=this.version,t=0;12>t;t++)e=e<<1^7973*(e>>>11);var n=this.version<<12|e;s(n>>>18==0);for(var r=0;18>r;r++){var i=o(n,r),a=this.size-11+r%3,u=Math.floor(r/3);this.setFunctionModule(a,u,i),this.setFunctionModule(u,a,i)}}}},{key:"drawFinderPattern",value:function(e,t){for(var n=-4;4>=n;n++)for(var r=-4;4>=r;r++){var i=Math.max(Math.abs(r),Math.abs(n)),o=e+r,a=t+n;o>=0&&this.size>o&&a>=0&&this.size>a&&this.setFunctionModule(o,a,2!=i&&4!=i)}}},{key:"drawAlignmentPattern",value:function(e,t){for(var n=-2;2>=n;n++)for(var r=-2;2>=r;r++)this.setFunctionModule(e+r,t+n,1!=Math.max(Math.abs(r),Math.abs(n)))}},{key:"setFunctionModule",value:function(e,t,n){this.modules[t][e]=n,this.isFunction[t][e]=!0}},{key:"addEccAndInterleave",value:function(e){var t=this.version,r=this.errorCorrectionLevel;if(e.length!=n.getNumDataCodewords(t,r))throw new RangeError("Invalid argument");for(var i=n.NUM_ERROR_CORRECTION_BLOCKS[r.ordinal][t],o=n.ECC_CODEWORDS_PER_BLOCK[r.ordinal][t],a=Math.floor(n.getNumRawDataModules(t)/8),u=i-a%i,l=Math.floor(a/i),h=[],c=n.reedSolomonComputeDivisor(o),f=0,d=0;i>f;f++){var v=e.slice(d,d+l-o+(u>f?0:1));d+=v.length;var m=n.reedSolomonComputeRemainder(v,c);u>f&&v.push(0),h.push(v.concat(m))}for(var g=[],y=function(e){h.forEach((function(t,n){e==l-o&&u>n||g.push(t[e])}))},E=0;h[0].length>E;E++)y(E);return s(g.length==a),g}},{key:"drawCodewords",value:function(e){if(e.length!=Math.floor(n.getNumRawDataModules(this.version)/8))throw new RangeError("Invalid argument");for(var t=0,r=this.size-1;r>=1;r-=2){6==r&&(r=5);for(var i=0;this.size>i;i++)for(var a=0;2>a;a++){var u=r-a,l=0==(r+1&2)?this.size-1-i:i;!this.isFunction[l][u]&&8*e.length>t&&(this.modules[l][u]=o(e[t>>>3],7-(7&t)),t++)}}s(t==8*e.length)}},{key:"applyMask",value:function(e){if(0>e||e>7)throw new RangeError("Mask value out of range");for(var t=0;this.size>t;t++)for(var n=0;this.size>n;n++){var r=void 0;switch(e){case 0:r=(n+t)%2==0;break;case 1:r=t%2==0;break;case 2:r=n%3==0;break;case 3:r=(n+t)%3==0;break;case 4:r=(Math.floor(n/3)+Math.floor(t/2))%2==0;break;case 5:r=n*t%2+n*t%3==0;break;case 6:r=(n*t%2+n*t%3)%2==0;break;case 7:r=((n+t)%2+n*t%3)%2==0;break;default:throw Error("Unreachable")}!this.isFunction[t][n]&&r&&(this.modules[t][n]=!this.modules[t][n])}}},{key:"getPenaltyScore",value:function(){for(var e=0,t=0;this.size>t;t++){for(var r=!1,i=0,o=[0,0,0,0,0,0,0],u=0;this.size>u;u++)this.modules[t][u]==r?5==++i?e+=n.PENALTY_N1:i>5&&e++:(this.finderPenaltyAddHistory(i,o),r||(e+=this.finderPenaltyCountPatterns(o)*n.PENALTY_N3),r=this.modules[t][u],i=1);e+=this.finderPenaltyTerminateAndCount(r,i,o)*n.PENALTY_N3}for(var l=0;this.size>l;l++){for(var h=!1,c=0,f=[0,0,0,0,0,0,0],d=0;this.size>d;d++)this.modules[d][l]==h?5==++c?e+=n.PENALTY_N1:c>5&&e++:(this.finderPenaltyAddHistory(c,f),h||(e+=this.finderPenaltyCountPatterns(f)*n.PENALTY_N3),h=this.modules[d][l],c=1);e+=this.finderPenaltyTerminateAndCount(h,c,f)*n.PENALTY_N3}for(var v=0;this.size-1>v;v++)for(var m=0;this.size-1>m;m++){var g=this.modules[v][m];g==this.modules[v][m+1]&&g==this.modules[v+1][m]&&g==this.modules[v+1][m+1]&&(e+=n.PENALTY_N2)}var y,E=0,w=a(this.modules);try{for(w.s();!(y=w.n()).done;){E=y.value.reduce((function(e,t){return e+(t?1:0)}),E)}}catch(e){w.e(e)}finally{w.f()}var M=this.size*this.size,C=Math.ceil(Math.abs(20*E-10*M)/M)-1;return s(C>=0&&9>=C),s((e+=C*n.PENALTY_N4)>=0&&2568888>=e),e}},{key:"getAlignmentPatternPositions",value:function(){if(1==this.version)return[];for(var e=Math.floor(this.version/7)+2,t=32==this.version?26:2*Math.ceil((4*this.version+4)/(2*e-2)),n=[6],r=this.size-7;e>n.length;r-=t)n.splice(1,0,r);return n}},{key:"finderPenaltyCountPatterns",value:function(e){var t=e[1];s(3*this.size>=t);var n=t>0&&e[2]==t&&e[3]==3*t&&e[4]==t&&e[5]==t;return(!n||4*t>e[0]||t>e[6]?0:1)+(!n||4*t>e[6]||t>e[0]?0:1)}},{key:"finderPenaltyTerminateAndCount",value:function(e,t,n){return e&&(this.finderPenaltyAddHistory(t,n),t=0),this.finderPenaltyAddHistory(t+=this.size,n),this.finderPenaltyCountPatterns(n)}},{key:"finderPenaltyAddHistory",value:function(e,t){0==t[0]&&(e+=this.size),t.pop(),t.unshift(e)}}],[{key:"encodeText",value:function(t,r){var i=e.QrSegment.makeSegments(t);return n.encodeSegments(i,r)}},{key:"encodeBinary",value:function(t,r){var i=e.QrSegment.makeBytes(t);return n.encodeSegments([i],r)}},{key:"encodeSegments",value:function(e,t){var r,o,l=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1,h=arguments.length>3&&void 0!==arguments[3]?arguments[3]:40,c=arguments.length>4&&void 0!==arguments[4]?arguments[4]:-1,f=5>=arguments.length||void 0===arguments[5]||arguments[5];if(n.MIN_VERSION>l||l>h||h>n.MAX_VERSION||-1>c||c>7)throw new RangeError("Invalid value");for(r=l;;r++){var d=8*n.getNumDataCodewords(r,t),v=u.getTotalBits(e,r);if(d>=v){o=v;break}if(r>=h)throw new RangeError("Data too long")}for(var m=0,g=[n.Ecc.MEDIUM,n.Ecc.QUARTILE,n.Ecc.HIGH];g.length>m;m++){var y=g[m];f&&o<=8*n.getNumDataCodewords(r,y)&&(t=y)}var E,w=[],M=a(e);try{for(M.s();!(E=M.n()).done;){var C=E.value;i(C.mode.modeBits,4,w),i(C.numChars,C.mode.numCharCountBits(r),w);var R,p=a(C.getData());try{for(p.s();!(R=p.n()).done;){var A=R.value;w.push(A)}}catch(e){p.e(e)}finally{p.f()}}}catch(e){M.e(e)}finally{M.f()}s(w.length==o);var P=8*n.getNumDataCodewords(r,t);s(P>=w.length),i(0,Math.min(4,P-w.length),w),i(0,(8-w.length%8)%8,w),s(w.length%8==0);for(var N=236;P>w.length;N^=253)i(N,8,w);for(var k=[];w.length>8*k.length;)k.push(0);return w.forEach((function(e,t){return k[t>>>3]|=e<<7-(7&t)})),new n(r,t,k,c)}},{key:"getNumRawDataModules",value:function(e){if(n.MIN_VERSION>e||e>n.MAX_VERSION)throw new RangeError("Version number out of range");var t=(16*e+128)*e+64;if(e>=2){var r=Math.floor(e/7)+2;t-=(25*r-10)*r-55,7>e||(t-=36)}return s(t>=208&&29648>=t),t}},{key:"getNumDataCodewords",value:function(e,t){return Math.floor(n.getNumRawDataModules(e)/8)-n.ECC_CODEWORDS_PER_BLOCK[t.ordinal][e]*n.NUM_ERROR_CORRECTION_BLOCKS[t.ordinal][e]}},{key:"reedSolomonComputeDivisor",value:function(e){if(1>e||e>255)throw new RangeError("Degree out of range");for(var t=[],r=0;e-1>r;r++)t.push(0);t.push(1);for(var i=1,o=0;e>o;o++){for(var a=0;t.length>a;a++)t[a]=n.reedSolomonMultiply(t[a],i),t.length>a+1&&(t[a]^=t[a+1]);i=n.reedSolomonMultiply(i,2)}return t}},{key:"reedSolomonComputeRemainder",value:function(e,t){var r,i=t.map((function(e){return 0})),o=a(e);try{var s=function(){var e=r.value^i.shift();i.push(0),t.forEach((function(t,r){return i[r]^=n.reedSolomonMultiply(t,e)}))};for(o.s();!(r=o.n()).done;)s()}catch(e){o.e(e)}finally{o.f()}return i}},{key:"reedSolomonMultiply",value:function(e,t){if(e>>>8!=0||t>>>8!=0)throw new RangeError("Byte out of range");for(var n=0,r=7;r>=0;r--)n=n<<1^285*(n>>>7),n^=(t>>>r&1)*e;return s(n>>>8==0),n}}]),n}();function i(e,t,n){if(0>t||t>31||e>>>t!=0)throw new RangeError("Value out of range");for(var r=t-1;r>=0;r--)n.push(e>>>r&1)}function o(e,t){return 0!=(e>>>t&1)}function s(e){if(!e)throw Error("Assertion error")}n.MIN_VERSION=1,n.MAX_VERSION=40,n.PENALTY_N1=3,n.PENALTY_N2=3,n.PENALTY_N3=40,n.PENALTY_N4=10,n.ECC_CODEWORDS_PER_BLOCK=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],n.NUM_ERROR_CORRECTION_BLOCKS=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]],e.QrCode=n;var u=function(){function e(n,r,i){if(t(this,e),this.mode=void 0,this.numChars=void 0,this.bitData=void 0,this.mode=n,this.numChars=r,this.bitData=i,0>r)throw new RangeError("Invalid argument");this.bitData=i.slice()}return r(e,[{key:"getData",value:function(){return this.bitData.slice()}}],[{key:"makeBytes",value:function(t){var n,r=[],o=a(t);try{for(o.s();!(n=o.n()).done;){i(n.value,8,r)}}catch(e){o.e(e)}finally{o.f()}return new e(e.Mode.BYTE,t.length,r)}},{key:"makeNumeric",value:function(t){if(!e.isNumeric(t))throw new RangeError("String contains non-numeric characters");for(var n=[],r=0;t.length>r;){var o=Math.min(t.length-r,3);i(parseInt(t.substring(r,r+o),10),3*o+1,n),r+=o}return new e(e.Mode.NUMERIC,t.length,n)}},{key:"makeAlphanumeric",value:function(t){if(!e.isAlphanumeric(t))throw new RangeError("String contains unencodable characters in alphanumeric mode");var n,r=[];for(n=0;t.length>=n+2;n+=2){var o=45*e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n));i(o+=e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n+1)),11,r)}return t.length>n&&i(e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(n)),6,r),new e(e.Mode.ALPHANUMERIC,t.length,r)}},{key:"makeSegments",value:function(t){return""==t?[]:e.isNumeric(t)?[e.makeNumeric(t)]:e.isAlphanumeric(t)?[e.makeAlphanumeric(t)]:[e.makeBytes(e.toUtf8ByteArray(t))]}},{key:"makeEci",value:function(t){var n=[];if(0>t)throw new RangeError("ECI assignment value out of range");if(128>t)i(t,8,n);else if(16384>t)i(2,2,n),i(t,14,n);else{if(t>=1e6)throw new RangeError("ECI assignment value out of range");i(6,3,n),i(t,21,n)}return new e(e.Mode.ECI,0,n)}},{key:"isNumeric",value:function(t){return e.NUMERIC_REGEX.test(t)}},{key:"isAlphanumeric",value:function(t){return e.ALPHANUMERIC_REGEX.test(t)}},{key:"getTotalBits",value:function(e,t){var n,r=0,i=a(e);try{for(i.s();!(n=i.n()).done;){var o=n.value,s=o.mode.numCharCountBits(t);if(o.numChars>=1<n;n++)"%"!=e.charAt(n)?t.push(e.charCodeAt(n)):(t.push(parseInt(e.substring(n+1,n+3),16)),n+=2);return t}}]),e}();u.NUMERIC_REGEX=/^[0-9]*$/,u.ALPHANUMERIC_REGEX=/^[A-Z0-9 $%*+.\/:-]*$/,u.ALPHANUMERIC_CHARSET="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:",e.QrSegment=u}(s||(s={})),function(e){!function(e){var n=r((function e(n,r){t(this,e),this.ordinal=void 0,this.formatBits=void 0,this.ordinal=n,this.formatBits=r}));n.LOW=new n(0,1),n.MEDIUM=new n(1,0),n.QUARTILE=new n(2,3),n.HIGH=new n(3,2),e.Ecc=n}(e.QrCode||(e.QrCode={}))}(s||(s={})),function(e){!function(e){var n=function(){function e(n,r){t(this,e),this.modeBits=void 0,this.numBitsCharCount=void 0,this.modeBits=n,this.numBitsCharCount=r}return r(e,[{key:"numCharCountBits",value:function(e){return this.numBitsCharCount[Math.floor((e+7)/17)]}}]),e}();n.NUMERIC=new n(1,[10,12,14]),n.ALPHANUMERIC=new n(2,[9,11,13]),n.BYTE=new n(4,[8,16,16]),n.KANJI=new n(8,[8,10,12]),n.ECI=new n(7,[0,0,0]),e.Mode=n}(e.QrSegment||(e.QrSegment={}))}(s||(s={}));var u=s,l=["value","size","level","bgColor","fgColor","includeMargin","marginSize","imageSettings"],h={L:u.QrCode.Ecc.LOW,M:u.QrCode.Ecc.MEDIUM,Q:u.QrCode.Ecc.QUARTILE,H:u.QrCode.Ecc.HIGH};function c(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=[];return e.forEach((function(e,r){var i=null;e.forEach((function(o,a){if(!o&&null!==i)return n.push("M".concat(i+t," ").concat(r+t,"h").concat(a-i,"v1H").concat(i+t,"z")),void(i=null);if(a!==e.length-1)o&&null===i&&(i=a);else{if(!o)return;n.push(null===i?"M".concat(a+t,",").concat(r+t," h1v1H").concat(a+t,"z"):"M".concat(i+t,",").concat(r+t," h").concat(a+1-i,"v1H").concat(i+t,"z"))}}))})),n.join("")}function f(e,t){return e.slice().map((function(e,n){return t.y>n||n>=t.y+t.h?e:e.map((function(e,n){return(t.x>n||n>=t.x+t.w)&&e}))}))}var d=function(){function e(n,r){var i=this;for(var o in t(this,e),this.canvas=void 0,this.pixelRatio="undefined"!=typeof window?window.devicePixelRatio:1,this.path2D=!0,this.SUPPORTS_PATH2D=void 0,this.createImage=function(){return new Image},this.createPath2D=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0,t="createPath2D";return i.canvas&&t in i.canvas?i.canvas[t](e):new Path2D(e)},this.canvas=n,r)o in this&&(this[o]=r[o]);this.SUPPORTS_PATH2D=function(){try{i.createPath2D()}catch(e){return!1}return!0}()}return r(e,[{key:"render",value:function(e,t){var n=this,r=e.value,o=e.size,a=void 0===o?128:o,s=e.level,d=void 0===s?"L":s,v=e.bgColor,m=void 0===v?"#FFFFFF":v,g=e.fgColor,y=void 0===g?"#000000":g,E=e.includeMargin,w=void 0!==E&&E,M=e.marginSize,C=e.imageSettings,R=(i(e,l),null==C?void 0:C.src),p=this.canvas,A=p.getContext("2d");if(A&&r){var P=u.QrCode.encodeText(r,h[d]).getModules(),N=function(e,t){return null!=t?Math.floor(t):e?4:0}(w,M),k=P.length+2*N,S=function(e,t,n,r){if(null==r)return null;var i=e.length+2*n,o=Math.floor(.1*t),a=i/t,s=(r.width||o)*a,u=(r.height||o)*a,l=null==r.x?e.length/2-s/2:r.x*a,h=null==r.y?e.length/2-u/2:r.y*a,c=null;if(r.excavate){var f=Math.floor(l),d=Math.floor(h);c={x:f,y:d,w:Math.ceil(s+l-f),h:Math.ceil(u+h-d)}}return{x:l,y:h,h:u,w:s,excavation:c}}(P,a,N,C),I=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,r=null!=S&&null!==e&&e.complete&&0!==e.naturalHeight&&0!==e.naturalWidth;r&&null!=S.excavation&&(P=f(P,S.excavation)),t&&t(P);var i=n.pixelRatio;A.clearRect(0,0,p.width,p.height),A.setTransform(1,0,0,1,0,0),p.height=p.width=a*i;var o=a/k*i;if(A.scale(o,o),A.fillStyle=m,A.fillRect(0,0,k,k),A.fillStyle=y,n.SUPPORTS_PATH2D&&n.path2D){var s=n.createPath2D(c(P,N));A.fill(s)}else P.forEach((function(e,t){e.forEach((function(e,n){e&&A.fillRect(n+N,t+N,1,1)}))}));var u=(null==e?void 0:e.path)||e;r&&A.drawImage(u,S.x+N,S.y+N,S.w,S.h),"draw"in A&&A.draw()};if(R){var b=this.createImage(p);b.onload=function(){I(b)},b.onerror=function(e){I(),console.warn(e)},b.src=R}else I()}}}]),e}();e.QRCodeCanvas=d,Object.defineProperty(e,"__esModule",{value:!0})})); diff --git a/src/uni_modules/lime-qrcode/hybrid/html/uni.webview.1.5.3.js b/src/uni_modules/lime-qrcode/hybrid/html/uni.webview.1.5.3.js new file mode 100644 index 00000000..d6524fa8 --- /dev/null +++ b/src/uni_modules/lime-qrcode/hybrid/html/uni.webview.1.5.3.js @@ -0,0 +1 @@ +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e=e||self).uni=n()}(this,(function(){"use strict";try{var e={};Object.defineProperty(e,"passive",{get:function(){!0}}),window.addEventListener("test-passive",null,e)}catch(e){}var n=Object.prototype.hasOwnProperty;function i(e,i){return n.call(e,i)}var t=[];function o(){return window.__dcloud_weex_postMessage||window.__dcloud_weex_}var r=function(e,n){var i={options:{timestamp:+new Date},name:e,arg:n};if(o()){if("postMessage"===e){var r={data:[n]};return window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessage(r):window.__dcloud_weex_.postMessage(JSON.stringify(r))}var a={type:"WEB_INVOKE_APPSERVICE",args:{data:i,webviewIds:t}};window.__dcloud_weex_postMessage?window.__dcloud_weex_postMessageToService(a):window.__dcloud_weex_.postMessageToService(JSON.stringify(a))}if(!window.plus)return window.parent.postMessage({type:"WEB_INVOKE_APPSERVICE",data:i,pageId:""},"*");if(0===t.length){var d=plus.webview.currentWebview();if(!d)throw new Error("plus.webview.currentWebview() is undefined");var s=d.parent(),w="";w=s?s.id:d.id,t.push(w)}if(plus.webview.getWebviewById("__uniapp__service"))plus.webview.postMessageToUniNView({type:"WEB_INVOKE_APPSERVICE",args:{data:i,webviewIds:t}},"__uniapp__service");else{var u=JSON.stringify(i);plus.webview.getLaunchWebview().evalJS('UniPlusBridge.subscribeHandler("'.concat("WEB_INVOKE_APPSERVICE",'",').concat(u,",").concat(JSON.stringify(t),");"))}},a={navigateTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("navigateTo",{url:encodeURI(n)})},navigateBack:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.delta;r("navigateBack",{delta:parseInt(n)||1})},switchTab:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("switchTab",{url:encodeURI(n)})},reLaunch:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("reLaunch",{url:encodeURI(n)})},redirectTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("redirectTo",{url:encodeURI(n)})},getEnv:function(e){o()?e({nvue:!0}):window.plus?e({plus:!0}):e({h5:!0})},postMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};r("postMessage",e.data||{})}},d=/uni-app/i.test(navigator.userAgent),s=/Html5Plus/i.test(navigator.userAgent),w=/complete|loaded|interactive/;var u=window.my&&navigator.userAgent.indexOf("AlipayClient")>-1;var g=window.swan&&window.swan.webView&&/swan/i.test(navigator.userAgent);var c=window.qq&&window.qq.miniProgram&&/QQ/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var v=window.tt&&window.tt.miniProgram&&/toutiaomicroapp/i.test(navigator.userAgent);var m=window.wx&&window.wx.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var p=window.qa&&/quickapp/i.test(navigator.userAgent);var f=window.ks&&window.ks.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var l=window.tt&&window.tt.miniProgram&&/Lark|Feishu/i.test(navigator.userAgent);var _=window.jd&&window.jd.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);for(var E,b=function(){window.UniAppJSBridge=!0,document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady",{bubbles:!0,cancelable:!0}))},h=[function(e){if(d||s)return window.__dcloud_weex_postMessage||window.__dcloud_weex_?document.addEventListener("DOMContentLoaded",e):window.plus&&w.test(document.readyState)?setTimeout(e,0):document.addEventListener("plusready",e),a},function(e){if(m)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.wx.miniProgram},function(e){if(c)return window.QQJSBridge&&window.QQJSBridge.invoke?setTimeout(e,0):document.addEventListener("QQJSBridgeReady",e),window.qq.miniProgram},function(e){if(u){document.addEventListener("DOMContentLoaded",e);var n=window.my;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(g)return document.addEventListener("DOMContentLoaded",e),window.swan.webView},function(e){if(v)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(p){window.QaJSBridge&&window.QaJSBridge.invoke?setTimeout(e,0):document.addEventListener("QaJSBridgeReady",e);var n=window.qa;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(f)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.ks.miniProgram},function(e){if(l)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(_)return window.JDJSBridgeReady&&window.JDJSBridgeReady.invoke?setTimeout(e,0):document.addEventListener("JDJSBridgeReady",e),window.jd.miniProgram},function(e){return document.addEventListener("DOMContentLoaded",e),a}],y=0;y +``` + + +#### ICON +- 带 Icon 的二维码 + +```html + +``` + +#### 颜色 +- 通过设置 `color` 自定义二维码颜色,通过设置 `bgColor` 自定义背景颜色。 + +```html + + +``` + +#### 纠错比例 +- 通过设置 `errorLevel` 调整不同的容错等级。 + +```html + +``` + +#### 生成图片 +- 1、通过调用插件的`canvasToTempFilePath`方法生成图片。 + +```html + + + +``` +```js +// vue3 +const qrcodeRef = ref(null) +const onClick = () => { + if(!qrcodeRef.value) return + qrcodeRef.value.canvasToTempFilePath({ + success(res) { + image.value = res.tempFilePath + console.log('success:::', res) + }, + fail(err) { + console.log('err:::', err) + } + }) +} + +// vue2 +const el = this.$refs['qrcodeRef'] +el.canvasToTempFilePath({ + success:(res)=>{ + this.image = res.tempFilePath + }, + fail(err) { + console.log('err:::', err) + } +}) + +// uvue +const el:LQrcodeComponentPublicInstance = this.$refs['qrcodeRef'] as LQrcodeComponentPublicInstance +el.canvasToTempFilePath({ + success:(res: TakeSnapshotSuccess)=>{ + this.image = res.tempFilePath + }, + fail(err: TakeSnapshotFail) { + console.log('err:::', err) + } +}) +``` + +- 2、通过设置`useCanvasToTempFilePath`在`success`事件里接收图片地址 + +```html + + +``` +```js +const image = ref(null) +const success = (img) => { + image.value = img +} +``` + +### 关于vue2的使用方式 +- 插件使用了`composition-api`, 如果你希望在vue2中使用请按官方的教程[vue-composition-api](https://uniapp.dcloud.net.cn/tutorial/vue-composition-api.html)配置 +- 关键代码是: 在main.js中 在vue2部分加上这一段即可 + +```js +// main.js vue2 +import Vue from 'vue' +import VueCompositionAPI from '@vue/composition-api' +Vue.use(VueCompositionAPI) +``` +另外插件也用到了TS,vue2可能会遇过官方的TS版本过低的问题,找到HX目录下的`compile-typescript`目录 +```cmd +// \HBuilderX\plugins\compile-typescript +yarn add typescript -D +- or - +npm install typescript -D +``` + +### 查看示例 +- 导入后直接使用这个标签查看演示效果 + +```html +// 代码位于 uni_modules/lime-qrcode/compoents/lime-qrcode + +``` + +### 插件标签 +- 默认 l-qrcode 为 component +- 默认 lime-qrcode 为 demo + + + +## API + +### Props + +| 参数 | 说明 | 类型 | 默认值 | +| --------------------------| ------------------------------------------------------------ | ---------------- | ------------ | +| value | 扫描后的文本 | string | `-` | +| icon | 二维码中图片的地址 | string | `-` | +| size | 二维码大小 | number,string | `160` | +| iconSize | 二维码中图片的大小 | number,string | `40` | +| color | 二维码颜色 | string | `-` | +| bgColor | 二维码背景颜色 | string | `-` | +| errorLevel | 二维码纠错等级 | `'L' | 'M' | 'Q' | 'H' ` | `M` | +| marginSize | 边距码大小,默认为0码点 | number | `0` | + +### 常见问题 +- icon 是网络地址时,H5和Nvue需要解决跨域问题,小程序需要配置download + +## 打赏 + +如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。 +![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/alipay.png) +![](https://testingcf.jsdelivr.net/gh/liangei/image@1.9/wpay.png) \ No newline at end of file diff --git a/src/uni_modules/lime-shared/addUnit/index.ts b/src/uni_modules/lime-shared/addUnit/index.ts new file mode 100644 index 00000000..2468a3aa --- /dev/null +++ b/src/uni_modules/lime-shared/addUnit/index.ts @@ -0,0 +1,25 @@ +// @ts-nocheck +import {isNumeric} from '../isNumeric' +import {isDef} from '../isDef' +/** + * 给一个值添加单位(像素 px) + * @param value 要添加单位的值,可以是字符串或数字 + * @returns 添加了单位的值,如果值为 undefined 则返回 undefined + */ +export function addUnit(value?: string | number): string | undefined { + if (!isDef(value)) { + return undefined; + } + + value = String(value); // 将值转换为字符串 + + // 如果值是数字,则在后面添加单位 "px",否则保持原始值 + return isNumeric(value) ? `${value}px` : value; +} + + +// console.log(addUnit(100)); // 输出: "100px" +// console.log(addUnit("200")); // 输出: "200px" +// console.log(addUnit("300px")); // 输出: "300px"(已经包含单位) +// console.log(addUnit()); // 输出: undefined(值为 undefined) +// console.log(addUnit(null)); // 输出: undefined(值为 null) \ No newline at end of file diff --git a/src/uni_modules/lime-shared/arrayBufferToFile/index.ts b/src/uni_modules/lime-shared/arrayBufferToFile/index.ts new file mode 100644 index 00000000..9760b203 --- /dev/null +++ b/src/uni_modules/lime-shared/arrayBufferToFile/index.ts @@ -0,0 +1,63 @@ +// @ts-nocheck +import {platform} from '../platform' +/** + * buffer转路径 + * @param {Object} buffer + */ +// @ts-nocheck +export function arrayBufferToFile(buffer: ArrayBuffer | Blob, name?: string, format?:string):Promise<(File|string)> { + return new Promise((resolve, reject) => { + // #ifdef MP + const fs = uni.getFileSystemManager() + //自定义文件名 + if (!name && !format) { + reject(new Error('ERROR_NAME_PARSE')) + } + const fileName = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`; + let pre = platform() + const filePath = `${pre.env.USER_DATA_PATH}/${fileName}` + fs.writeFile({ + filePath, + data: buffer, + success() { + resolve(filePath) + }, + fail(err) { + console.error(err) + reject(err) + } + }) + // #endif + + // #ifdef H5 + const file = new File([buffer], name, { + type: format, + }); + resolve(file) + // #endif + + // #ifdef APP-PLUS + const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now()) + const base64 = uni.arrayBufferToBase64(buffer) + bitmap.loadBase64Data(base64, () => { + if (!name && !format) { + reject(new Error('ERROR_NAME_PARSE')) + } + const fileNmae = `${name || new Date().getTime()}.${format.replace(/(.+)?\//,'')}`; + const filePath = `_doc/uniapp_temp/${fileNmae}` + bitmap.save(filePath, {}, + () => { + bitmap.clear() + resolve(filePath) + }, + (error) => { + bitmap.clear() + reject(error) + }) + }, (error) => { + bitmap.clear() + reject(error) + }) + // #endif + }) +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/base64ToArrayBuffer/index.ts b/src/uni_modules/lime-shared/base64ToArrayBuffer/index.ts new file mode 100644 index 00000000..f83b6408 --- /dev/null +++ b/src/uni_modules/lime-shared/base64ToArrayBuffer/index.ts @@ -0,0 +1,13 @@ +// @ts-nocheck +// 未完成 +export function base64ToArrayBuffer(base64 : string) { + const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || []; + if (!format) { + new Error('ERROR_BASE64SRC_PARSE') + } + if(uni.base64ToArrayBuffer) { + return uni.base64ToArrayBuffer(bodyData) + } else { + + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/base64ToPath/index.ts b/src/uni_modules/lime-shared/base64ToPath/index.ts new file mode 100644 index 00000000..1f14d0ab --- /dev/null +++ b/src/uni_modules/lime-shared/base64ToPath/index.ts @@ -0,0 +1,76 @@ +// @ts-nocheck +import {platform} from '../platform' +/** + * base64转路径 + * @param {Object} base64 + */ +export function base64ToPath(base64: string, filename?: string):Promise { + const [, format] = /^data:image\/(\w+);base64,/.exec(base64) || []; + console.log('format', format) + return new Promise((resolve, reject) => { + // #ifdef MP + const fs = uni.getFileSystemManager() + //自定义文件名 + if (!filename && !format) { + reject(new Error('ERROR_BASE64SRC_PARSE')) + } + // const time = new Date().getTime(); + const name = filename || `${new Date().getTime()}.${format}`; + let pre = platform() + const filePath = `${pre.env.USER_DATA_PATH}/${name}` + fs.writeFile({ + filePath, + data: base64.split(',')[1], + encoding: 'base64', + success() { + resolve(filePath) + }, + fail(err) { + console.error(err) + reject(err) + } + }) + // #endif + + // #ifdef H5 + // mime类型 + let mimeString = base64.split(',')[0].split(':')[1].split(';')[0]; + //base64 解码 + let byteString = atob(base64.split(',')[1]); + //创建缓冲数组 + let arrayBuffer = new ArrayBuffer(byteString.length); + //创建视图 + let intArray = new Uint8Array(arrayBuffer); + for (let i = 0; i < byteString.length; i++) { + intArray[i] = byteString.charCodeAt(i); + } + resolve(URL.createObjectURL(new Blob([intArray], { + type: mimeString + }))) + // #endif + + // #ifdef APP-PLUS + const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now()) + bitmap.loadBase64Data(base64, () => { + if (!filename && !format) { + reject(new Error('ERROR_BASE64SRC_PARSE')) + } + // const time = new Date().getTime(); + const name = filename || `${new Date().getTime()}.${format}`; + const filePath = `_doc/uniapp_temp/${name}` + bitmap.save(filePath, {}, + () => { + bitmap.clear() + resolve(filePath) + }, + (error) => { + bitmap.clear() + reject(error) + }) + }, (error) => { + bitmap.clear() + reject(error) + }) + // #endif + }) +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/camelCase/index.ts b/src/uni_modules/lime-shared/camelCase/index.ts new file mode 100644 index 00000000..78a81c81 --- /dev/null +++ b/src/uni_modules/lime-shared/camelCase/index.ts @@ -0,0 +1,21 @@ +/** + * 将字符串转换为 camelCase 或 PascalCase 风格的命名约定 + * @param str 要转换的字符串 + * @param isPascalCase 指示是否转换为 PascalCase 的布尔值,默认为 false + * @returns 转换后的字符串 + */ +export function camelCase(str: string, isPascalCase: boolean = false): string { + // 将字符串分割成单词数组 + let words: string[] = str.split(/[\s_-]+/); + + // 将数组中的每个单词首字母大写(除了第一个单词) + let camelCased: string[] = words.map((word, index) => { + if (index === 0 && !isPascalCase) { + return word.toLowerCase(); // 第一个单词全小写 + } + return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); + }); + + // 将数组中的单词拼接成一个字符串 + return camelCased.join(''); +}; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/canIUseCanvas2d/index.ts b/src/uni_modules/lime-shared/canIUseCanvas2d/index.ts new file mode 100644 index 00000000..22e44162 --- /dev/null +++ b/src/uni_modules/lime-shared/canIUseCanvas2d/index.ts @@ -0,0 +1,58 @@ +// @ts-nocheck +// #ifdef MP-ALIPAY +interface My { + SDKVersion: string +} +declare var my: My +// #endif + +function compareVersion(v1:string, v2:string) { + let a1 = v1.split('.'); + let a2 = v2.split('.'); + const len = Math.max(a1.length, a2.length); + + while (a1.length < len) { + a1.push('0'); + } + while (a2.length < len) { + a2.push('0'); + } + + for (let i = 0; i < len; i++) { + const num1 = parseInt(a1[i], 10); + const num2 = parseInt(a2[i], 10); + + if (num1 > num2) { + return 1; + } + if (num1 < num2) { + return -1; + } + } + + return 0; +} + +function gte(version: string) { + let {SDKVersion} = uni.getSystemInfoSync(); + // #ifdef MP-ALIPAY + SDKVersion = my.SDKVersion + // #endif + return compareVersion(SDKVersion, version) >= 0; +} + +/** 环境是否支持canvas 2d */ +export function canIUseCanvas2d() { + // #ifdef MP-WEIXIN + return gte('2.9.0'); + // #endif + // #ifdef MP-ALIPAY + return gte('2.7.0'); + // #endif + // #ifdef MP-TOUTIAO + return gte('1.78.0'); + // #endif + // #ifndef MP-WEIXIN || MP-ALIPAY || MP-TOUTIAO + return false + // #endif +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/changelog.md b/src/uni_modules/lime-shared/changelog.md new file mode 100644 index 00000000..62c3f5b0 --- /dev/null +++ b/src/uni_modules/lime-shared/changelog.md @@ -0,0 +1,30 @@ +## 0.1.4(2023-09-05) +- feat: 增加 Hooks `useIntersectionObserver` +- feat: 增加 `floatAdd` +- feat: 因为本人插件兼容 vue2 需要使用 `composition-api`,故增加vue文件代码插件的条件编译 +## 0.1.3(2023-08-13) +- feat: 增加 `camelCase` +## 0.1.2(2023-07-17) +- feat: 增加 `getClassStr` +## 0.1.1(2023-07-06) +- feat: 增加 `isNumeric`, 区别于 `isNumber` +## 0.1.0(2023-06-30) +- fix: `clamp`忘记导出了 +## 0.0.9(2023-06-27) +- feat: 增加`arrayBufferToFile` +## 0.0.8(2023-06-19) +- feat: 增加`createAnimation`、`clamp` +## 0.0.7(2023-06-08) +- chore: 更新注释 +## 0.0.6(2023-06-08) +- chore: 增加`createImage`为`lime-watermark`和`lime-qrcode`提供依赖 +## 0.0.5(2023-06-03) +- chore: 更新注释 +## 0.0.4(2023-05-22) +- feat: 增加`range`,`exif`,`selectComponent` +## 0.0.3(2023-05-08) +- feat: 增加`fillZero`,`debounce`,`throttle`,`random` +## 0.0.2(2023-05-05) +- chore: 更新文档 +## 0.0.1(2023-05-05) +- 无 diff --git a/src/uni_modules/lime-shared/clamp/index.ts b/src/uni_modules/lime-shared/clamp/index.ts new file mode 100644 index 00000000..b62565ca --- /dev/null +++ b/src/uni_modules/lime-shared/clamp/index.ts @@ -0,0 +1,16 @@ +// @ts-nocheck +/** + * 将一个值限制在指定的范围内 + * @param min 最小值 + * @param max 最大值 + * @param val 要限制的值 + * @returns 限制后的值 + */ +export function clamp(min: number, max: number, val: number): number { + return Math.max(min, Math.min(max, val)); +} + + +// console.log(clamp(0, 10, 5)); // 输出: 5(在范围内,不做更改) +// console.log(clamp(0, 10, -5)); // 输出: 0(小于最小值,被限制为最小值) +// console.log(clamp(0, 10, 15)); // 输出: 10(大于最大值,被限制为最大值) \ No newline at end of file diff --git a/src/uni_modules/lime-shared/cloneDeep/index.ts b/src/uni_modules/lime-shared/cloneDeep/index.ts new file mode 100644 index 00000000..ded334de --- /dev/null +++ b/src/uni_modules/lime-shared/cloneDeep/index.ts @@ -0,0 +1,103 @@ +// @ts-nocheck +/** + * 深度克隆一个对象或数组 + * @param obj 要克隆的对象或数组 + * @returns 克隆后的对象或数组 + */ +export function cloneDeep(obj: any): T { + // 如果传入的对象为空,返回空 + if (obj === null) { + return null as unknown as T; + } + + // 如果传入的对象是 Set 类型,则将其转换为数组,并通过新的 Set 构造函数创建一个新的 Set 对象 + if (obj instanceof Set) { + return new Set([...obj]) as unknown as T; + } + + // 如果传入的对象是 Map 类型,则将其转换为数组,并通过新的 Map 构造函数创建一个新的 Map 对象 + if (obj instanceof Map) { + return new Map([...obj]) as unknown as T; + } + + // 如果传入的对象是 WeakMap 类型,则直接用传入的 WeakMap 对象进行赋值 + if (obj instanceof WeakMap) { + let weakMap = new WeakMap(); + weakMap = obj; + return weakMap as unknown as T; + } + + // 如果传入的对象是 WeakSet 类型,则直接用传入的 WeakSet 对象进行赋值 + if (obj instanceof WeakSet) { + let weakSet = new WeakSet(); + weakSet = obj; + return weakSet as unknown as T; + } + + // 如果传入的对象是 RegExp 类型,则通过新的 RegExp 构造函数创建一个新的 RegExp 对象 + if (obj instanceof RegExp) { + return new RegExp(obj) as unknown as T; + } + + // 如果传入的对象是 undefined 类型,则返回 undefined + if (typeof obj === 'undefined') { + return undefined as unknown as T; + } + + // 如果传入的对象是数组,则递归调用 cloneDeep 函数对数组中的每个元素进行克隆 + if (Array.isArray(obj)) { + return obj.map(cloneDeep) as unknown as T; + } + + // 如果传入的对象是 Date 类型,则通过新的 Date 构造函数创建一个新的 Date 对象 + if (obj instanceof Date) { + return new Date(obj.getTime()) as unknown as T; + } + + // 如果传入的对象是普通对象,则使用递归调用 cloneDeep 函数对对象的每个属性进行克隆 + if (typeof obj === 'object') { + const newObj: any = {}; + for (const [key, value] of Object.entries(obj)) { + newObj[key] = cloneDeep(value); + } + const symbolKeys = Object.getOwnPropertySymbols(obj); + for (const key of symbolKeys) { + newObj[key] = cloneDeep(obj[key]); + } + return newObj; + } + + // 如果传入的对象是基本数据类型(如字符串、数字等),则直接返回 + return obj; +} + +// 示例使用 + +// // 克隆一个对象 +// const obj = { name: 'John', age: 30 }; +// const clonedObj = cloneDeep(obj); + +// console.log(clonedObj); // 输出: { name: 'John', age: 30 } +// console.log(clonedObj === obj); // 输出: false (副本与原对象是独立的) + +// // 克隆一个数组 +// const arr = [1, 2, 3]; +// const clonedArr = cloneDeep(arr); + +// console.log(clonedArr); // 输出: [1, 2, 3] +// console.log(clonedArr === arr); // 输出: false (副本与原数组是独立的) + +// // 克隆一个包含嵌套对象的对象 +// const person = { +// name: 'Alice', +// age: 25, +// address: { +// city: 'New York', +// country: 'USA', +// }, +// }; +// const clonedPerson = cloneDeep(person); + +// console.log(clonedPerson); // 输出: { name: 'Alice', age: 25, address: { city: 'New York', country: 'USA' } } +// console.log(clonedPerson === person); // 输出: false (副本与原对象是独立的) +// console.log(clonedPerson.address === person.address); // 输出: false (嵌套对象的副本也是独立的) \ No newline at end of file diff --git a/src/uni_modules/lime-shared/closest/index.ts b/src/uni_modules/lime-shared/closest/index.ts new file mode 100644 index 00000000..95804cda --- /dev/null +++ b/src/uni_modules/lime-shared/closest/index.ts @@ -0,0 +1,22 @@ +// @ts-nocheck + +/** + * 在给定数组中找到最接近目标数字的元素。 + * @param arr 要搜索的数字数组。 + * @param target 目标数字。 + * @returns 最接近目标数字的数组元素。 + */ +export function closest(arr: number[], target: number) { + return arr.reduce((pre, cur) => + Math.abs(pre - target) < Math.abs(cur - target) ? pre : cur + ); +} + +// 示例 +// // 定义一个数字数组 +// const numbers = [1, 3, 5, 7, 9]; + +// // 在数组中找到最接近目标数字 6 的元素 +// const closestNumber = closest(numbers, 6); + +// console.log(closestNumber); // 输出结果: 5 \ No newline at end of file diff --git a/src/uni_modules/lime-shared/createAnimation/index.ts b/src/uni_modules/lime-shared/createAnimation/index.ts new file mode 100644 index 00000000..5b6e2da3 --- /dev/null +++ b/src/uni_modules/lime-shared/createAnimation/index.ts @@ -0,0 +1,149 @@ +// @ts-nocheck +// nvue 需要在节点上设置ref或在export里传入 +// const animation = createAnimation({ +// ref: this.$refs['xxx'], +// duration: 0, +// timingFunction: 'linear' +// }) +// animation.opacity(1).translate(x, y).step({duration}) +// animation.export(ref) + +// 抹平nvue 与 uni.createAnimation的使用差距 +// 但是nvue动画太慢~~~无语 + + + + +// #ifdef APP-NVUE +const nvueAnimation = uni.requireNativePlugin('animation') + +type AnimationTypes = 'matrix' | 'matrix3d' | 'rotate' | 'rotate3d' | 'rotateX' | 'rotateY' | 'rotateZ' | 'scale' | 'scale3d' | 'scaleX' | 'scaleY' | 'scaleZ' | 'skew' | 'skewX' | 'skewY' | 'translate' | 'translate3d' | 'translateX' | 'translateY' | 'translateZ' + | 'opacity' | 'backgroundColor' | 'width' | 'height' | 'left' | 'right' | 'top' | 'bottom' + +interface Styles { + [key : string] : any +} + +interface StepConfig { + duration?: number + timingFunction?: string + delay?: number + needLayout?: boolean + transformOrigin?: string +} +interface StepAnimate { + styles?: Styles + config?: StepConfig +} +interface StepAnimates { + [key: number]: StepAnimate +} +interface CreateAnimationOptions extends UniApp.CreateAnimationOptions { + ref?: string +} + +type Callback = (time: number) => void +const animateTypes1 : AnimationTypes[] = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d', + 'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY', + 'translateZ' +] +const animateTypes2 : AnimationTypes[] = ['opacity', 'backgroundColor'] +const animateTypes3 : AnimationTypes[] = ['width', 'height', 'left', 'right', 'top', 'bottom'] + +class LimeAnimation { + ref : any + context : any + options : UniApp.CreateAnimationOptions + // stack : any[] = [] + next : number = 0 + currentStepAnimates : StepAnimates = {} + duration : number = 0 + constructor(options : CreateAnimationOptions) { + const {ref} = options + this.ref = ref + this.options = options + } + addAnimate(type : AnimationTypes, args: (string | number)[]) { + let aniObj = this.currentStepAnimates[this.next] + let stepAnimate:StepAnimate = {} + if (!aniObj) { + stepAnimate = {styles: {}, config: {}} + } else { + stepAnimate = aniObj + } + + if (animateTypes1.includes(type)) { + if (!stepAnimate.styles.transform) { + stepAnimate.styles.transform = '' + } + let unit = '' + if (type === 'rotate') { + unit = 'deg' + } + stepAnimate.styles.transform += `${type}(${args.map((v: number) => v + unit).join(',')}) ` + } else { + stepAnimate.styles[type] = `${args.join(',')}` + } + this.currentStepAnimates[this.next] = stepAnimate + } + animateRun(styles: Styles = {}, config:StepConfig = {}, ref: any) { + const el = ref || this.ref + if (!el) return + return new Promise((resolve) => { + const time = +new Date() + nvueAnimation.transition(el, { + styles, + ...config + }, () => { + resolve(+new Date() - time) + }) + }) + } + nextAnimate(animates: StepAnimates, step: number = 0, ref: any, cb: Callback) { + let obj = animates[step] + if (obj) { + let { styles, config } = obj + // this.duration += config.duration + this.animateRun(styles, config, ref).then((time: number) => { + step += 1 + this.duration += time + this.nextAnimate(animates, step, ref, cb) + }) + } else { + this.currentStepAnimates = {} + cb && cb(this.duration) + } + } + step(config:StepConfig = {}) { + this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config) + this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin + this.next++ + return this + } + export(ref: any, cb?: Callback) { + ref = ref || this.ref + if(!ref) return + this.duration = 0 + this.next = 0 + this.nextAnimate(this.currentStepAnimates, 0, ref, cb) + return null + } +} + + +animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => { + LimeAnimation.prototype[type] = function(...args: (string | number)[]) { + this.addAnimate(type, args) + return this + } +}) +// #endif +export function createAnimation(options : CreateAnimationOptions) { + // #ifndef APP-NVUE + // 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误 + return uni.createAnimation({ ...options }) + // #endif + // #ifdef APP-NVUE + return new LimeAnimation(options) + // #endif +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/createImage/index.ts b/src/uni_modules/lime-shared/createImage/index.ts new file mode 100644 index 00000000..674b6f55 --- /dev/null +++ b/src/uni_modules/lime-shared/createImage/index.ts @@ -0,0 +1,61 @@ +// @ts-nocheck +import {isBrowser} from '../isBrowser' +class Image { + currentSrc: string | null = null + naturalHeight: number = 0 + naturalWidth: number = 0 + width: number = 0 + height: number = 0 + tagName: string = 'IMG' + path: string = '' + crossOrigin: string = '' + referrerPolicy: string = '' + onload: () => void = () => {} + onerror: () => void = () => {} + complete: boolean = false + constructor() {} + set src(src: string) { + console.log('src', src) + if(!src) { + return this.onerror() + } + src = src.replace(/^@\//,'/') + this.currentSrc = src + uni.getImageInfo({ + src, + success: (res) => { + const localReg = /^\.|^\/(?=[^\/])/; + // #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO + res.path = localReg.test(src) ? `/${res.path}` : res.path; + // #endif + this.complete = true + this.path = res.path + this.naturalWidth = this.width = res.width + this.naturalHeight = this.height = res.height + this.onload() + }, + fail: () => { + this.onerror() + } + }) + } + get src() { + return this.currentSrc + } +} +interface UniImage extends WechatMiniprogram.Image { + complete?: boolean + naturalHeight?: number + naturalWidth?: number +} +/** 创建用于 canvas 的 img */ +export function createImage(canvas?: any): HTMLImageElement | UniImage { + if(canvas && canvas.createImage) { + return (canvas as WechatMiniprogram.Canvas).createImage() + } else if(this.tagName == 'canvas' && !('toBlob' in this) || canvas && !('toBlob' in canvas)){ + return new Image() + } else if(isBrowser) { + return new window.Image() + } + return new Image() +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/debounce/index.ts b/src/uni_modules/lime-shared/debounce/index.ts new file mode 100644 index 00000000..58751450 --- /dev/null +++ b/src/uni_modules/lime-shared/debounce/index.ts @@ -0,0 +1,38 @@ +// @ts-nocheck +type Timeout = ReturnType | null; +/** + * 防抖函数,通过延迟一定时间来限制函数的执行频率。 + * @param fn 要防抖的函数。 + * @param wait 触发防抖的等待时间,单位为毫秒。 + * @returns 防抖函数。 + */ +export function debounce(fn: (...args: any[]) => void, wait = 300) { + let timer: Timeout = null; // 用于存储 setTimeout 的标识符的变量 + + return function (this: any, ...args: any[]) { + if (timer) clearTimeout(timer); // 如果上一个 setTimeout 存在,则清除它 + + // 设置一个新的 setTimeout,在指定的等待时间后调用防抖函数 + timer = setTimeout(() => { + fn.apply(this, args); // 使用提供的参数调用原始函数 + }, wait); + }; +}; + + + +// 示例 +// 定义一个函数 +// function saveData(data: string) { +// // 模拟保存数据的操作 +// console.log(`Saving data: ${data}`); +// } + +// // 创建一个防抖函数,延迟 500 毫秒后调用 saveData 函数 +// const debouncedSaveData = debounce(saveData, 500); + +// // 连续调用防抖函数 +// debouncedSaveData('Data 1'); // 不会立即调用 saveData 函数 +// debouncedSaveData('Data 2'); // 不会立即调用 saveData 函数 + +// 在 500 毫秒后,只会调用一次 saveData 函数,输出结果为 "Saving data: Data 2" \ No newline at end of file diff --git a/src/uni_modules/lime-shared/exif/index.ts b/src/uni_modules/lime-shared/exif/index.ts new file mode 100644 index 00000000..b67e2334 --- /dev/null +++ b/src/uni_modules/lime-shared/exif/index.ts @@ -0,0 +1,1056 @@ +// @ts-nocheck +import { base64ToArrayBuffer } from '../base64ToArrayBuffer'; +import { pathToBase64 } from '../pathToBase64'; +import { isBase64 } from '../isBase64'; +import { isString } from '../isString'; + +interface File { + exifdata : any + iptcdata : any + xmpdata : any + src : string +} +class EXIF { + isXmpEnabled = false + debug = false + Tags = { + // version tags + 0x9000: "ExifVersion", // EXIF version + 0xA000: "FlashpixVersion", // Flashpix format version + + // colorspace tags + 0xA001: "ColorSpace", // Color space information tag + + // image configuration + 0xA002: "PixelXDimension", // Valid width of meaningful image + 0xA003: "PixelYDimension", // Valid height of meaningful image + 0x9101: "ComponentsConfiguration", // Information about channels + 0x9102: "CompressedBitsPerPixel", // Compressed bits per pixel + + // user information + 0x927C: "MakerNote", // Any desired information written by the manufacturer + 0x9286: "UserComment", // Comments by user + + // related file + 0xA004: "RelatedSoundFile", // Name of related sound file + + // date and time + 0x9003: "DateTimeOriginal", // Date and time when the original image was generated + 0x9004: "DateTimeDigitized", // Date and time when the image was stored digitally + 0x9290: "SubsecTime", // Fractions of seconds for DateTime + 0x9291: "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal + 0x9292: "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized + + // picture-taking conditions + 0x829A: "ExposureTime", // Exposure time (in seconds) + 0x829D: "FNumber", // F number + 0x8822: "ExposureProgram", // Exposure program + 0x8824: "SpectralSensitivity", // Spectral sensitivity + 0x8827: "ISOSpeedRatings", // ISO speed rating + 0x8828: "OECF", // Optoelectric conversion factor + 0x9201: "ShutterSpeedValue", // Shutter speed + 0x9202: "ApertureValue", // Lens aperture + 0x9203: "BrightnessValue", // Value of brightness + 0x9204: "ExposureBias", // Exposure bias + 0x9205: "MaxApertureValue", // Smallest F number of lens + 0x9206: "SubjectDistance", // Distance to subject in meters + 0x9207: "MeteringMode", // Metering mode + 0x9208: "LightSource", // Kind of light source + 0x9209: "Flash", // Flash status + 0x9214: "SubjectArea", // Location and area of main subject + 0x920A: "FocalLength", // Focal length of the lens in mm + 0xA20B: "FlashEnergy", // Strobe energy in BCPS + 0xA20C: "SpatialFrequencyResponse", // + 0xA20E: "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit + 0xA20F: "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit + 0xA210: "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution + 0xA214: "SubjectLocation", // Location of subject in image + 0xA215: "ExposureIndex", // Exposure index selected on camera + 0xA217: "SensingMethod", // Image sensor type + 0xA300: "FileSource", // Image source (3 == DSC) + 0xA301: "SceneType", // Scene type (1 == directly photographed) + 0xA302: "CFAPattern", // Color filter array geometric pattern + 0xA401: "CustomRendered", // Special processing + 0xA402: "ExposureMode", // Exposure mode + 0xA403: "WhiteBalance", // 1 = auto white balance, 2 = manual + 0xA404: "DigitalZoomRation", // Digital zoom ratio + 0xA405: "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm) + 0xA406: "SceneCaptureType", // Type of scene + 0xA407: "GainControl", // Degree of overall image gain adjustment + 0xA408: "Contrast", // Direction of contrast processing applied by camera + 0xA409: "Saturation", // Direction of saturation processing applied by camera + 0xA40A: "Sharpness", // Direction of sharpness processing applied by camera + 0xA40B: "DeviceSettingDescription", // + 0xA40C: "SubjectDistanceRange", // Distance to subject + + // other tags + 0xA005: "InteroperabilityIFDPointer", + 0xA420: "ImageUniqueID" // Identifier assigned uniquely to each image + } + TiffTags = { + 0x0100: "ImageWidth", + 0x0101: "ImageHeight", + 0x8769: "ExifIFDPointer", + 0x8825: "GPSInfoIFDPointer", + 0xA005: "InteroperabilityIFDPointer", + 0x0102: "BitsPerSample", + 0x0103: "Compression", + 0x0106: "PhotometricInterpretation", + 0x0112: "Orientation", + 0x0115: "SamplesPerPixel", + 0x011C: "PlanarConfiguration", + 0x0212: "YCbCrSubSampling", + 0x0213: "YCbCrPositioning", + 0x011A: "XResolution", + 0x011B: "YResolution", + 0x0128: "ResolutionUnit", + 0x0111: "StripOffsets", + 0x0116: "RowsPerStrip", + 0x0117: "StripByteCounts", + 0x0201: "JPEGInterchangeFormat", + 0x0202: "JPEGInterchangeFormatLength", + 0x012D: "TransferFunction", + 0x013E: "WhitePoint", + 0x013F: "PrimaryChromaticities", + 0x0211: "YCbCrCoefficients", + 0x0214: "ReferenceBlackWhite", + 0x0132: "DateTime", + 0x010E: "ImageDescription", + 0x010F: "Make", + 0x0110: "Model", + 0x0131: "Software", + 0x013B: "Artist", + 0x8298: "Copyright" + } + GPSTags = { + 0x0000: "GPSVersionID", + 0x0001: "GPSLatitudeRef", + 0x0002: "GPSLatitude", + 0x0003: "GPSLongitudeRef", + 0x0004: "GPSLongitude", + 0x0005: "GPSAltitudeRef", + 0x0006: "GPSAltitude", + 0x0007: "GPSTimeStamp", + 0x0008: "GPSSatellites", + 0x0009: "GPSStatus", + 0x000A: "GPSMeasureMode", + 0x000B: "GPSDOP", + 0x000C: "GPSSpeedRef", + 0x000D: "GPSSpeed", + 0x000E: "GPSTrackRef", + 0x000F: "GPSTrack", + 0x0010: "GPSImgDirectionRef", + 0x0011: "GPSImgDirection", + 0x0012: "GPSMapDatum", + 0x0013: "GPSDestLatitudeRef", + 0x0014: "GPSDestLatitude", + 0x0015: "GPSDestLongitudeRef", + 0x0016: "GPSDestLongitude", + 0x0017: "GPSDestBearingRef", + 0x0018: "GPSDestBearing", + 0x0019: "GPSDestDistanceRef", + 0x001A: "GPSDestDistance", + 0x001B: "GPSProcessingMethod", + 0x001C: "GPSAreaInformation", + 0x001D: "GPSDateStamp", + 0x001E: "GPSDifferential" + } + // EXIF 2.3 Spec + IFD1Tags = { + 0x0100: "ImageWidth", + 0x0101: "ImageHeight", + 0x0102: "BitsPerSample", + 0x0103: "Compression", + 0x0106: "PhotometricInterpretation", + 0x0111: "StripOffsets", + 0x0112: "Orientation", + 0x0115: "SamplesPerPixel", + 0x0116: "RowsPerStrip", + 0x0117: "StripByteCounts", + 0x011A: "XResolution", + 0x011B: "YResolution", + 0x011C: "PlanarConfiguration", + 0x0128: "ResolutionUnit", + 0x0201: "JpegIFOffset", // When image format is JPEG, this value show offset to JPEG data stored.(aka "ThumbnailOffset" or "JPEGInterchangeFormat") + 0x0202: "JpegIFByteCount", // When image format is JPEG, this value shows data size of JPEG image (aka "ThumbnailLength" or "JPEGInterchangeFormatLength") + 0x0211: "YCbCrCoefficients", + 0x0212: "YCbCrSubSampling", + 0x0213: "YCbCrPositioning", + 0x0214: "ReferenceBlackWhite" + } + StringValues = { + ExposureProgram: { + 0: "Not defined", + 1: "Manual", + 2: "Normal program", + 3: "Aperture priority", + 4: "Shutter priority", + 5: "Creative program", + 6: "Action program", + 7: "Portrait mode", + 8: "Landscape mode" + }, + MeteringMode: { + 0: "Unknown", + 1: "Average", + 2: "CenterWeightedAverage", + 3: "Spot", + 4: "MultiSpot", + 5: "Pattern", + 6: "Partial", + 255: "Other" + }, + LightSource: { + 0: "Unknown", + 1: "Daylight", + 2: "Fluorescent", + 3: "Tungsten (incandescent light)", + 4: "Flash", + 9: "Fine weather", + 10: "Cloudy weather", + 11: "Shade", + 12: "Daylight fluorescent (D 5700 - 7100K)", + 13: "Day white fluorescent (N 4600 - 5400K)", + 14: "Cool white fluorescent (W 3900 - 4500K)", + 15: "White fluorescent (WW 3200 - 3700K)", + 17: "Standard light A", + 18: "Standard light B", + 19: "Standard light C", + 20: "D55", + 21: "D65", + 22: "D75", + 23: "D50", + 24: "ISO studio tungsten", + 255: "Other" + }, + Flash: { + 0x0000: "Flash did not fire", + 0x0001: "Flash fired", + 0x0005: "Strobe return light not detected", + 0x0007: "Strobe return light detected", + 0x0009: "Flash fired, compulsory flash mode", + 0x000D: "Flash fired, compulsory flash mode, return light not detected", + 0x000F: "Flash fired, compulsory flash mode, return light detected", + 0x0010: "Flash did not fire, compulsory flash mode", + 0x0018: "Flash did not fire, auto mode", + 0x0019: "Flash fired, auto mode", + 0x001D: "Flash fired, auto mode, return light not detected", + 0x001F: "Flash fired, auto mode, return light detected", + 0x0020: "No flash function", + 0x0041: "Flash fired, red-eye reduction mode", + 0x0045: "Flash fired, red-eye reduction mode, return light not detected", + 0x0047: "Flash fired, red-eye reduction mode, return light detected", + 0x0049: "Flash fired, compulsory flash mode, red-eye reduction mode", + 0x004D: "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected", + 0x004F: "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected", + 0x0059: "Flash fired, auto mode, red-eye reduction mode", + 0x005D: "Flash fired, auto mode, return light not detected, red-eye reduction mode", + 0x005F: "Flash fired, auto mode, return light detected, red-eye reduction mode" + }, + SensingMethod: { + 1: "Not defined", + 2: "One-chip color area sensor", + 3: "Two-chip color area sensor", + 4: "Three-chip color area sensor", + 5: "Color sequential area sensor", + 7: "Trilinear sensor", + 8: "Color sequential linear sensor" + }, + SceneCaptureType: { + 0: "Standard", + 1: "Landscape", + 2: "Portrait", + 3: "Night scene" + }, + SceneType: { + 1: "Directly photographed" + }, + CustomRendered: { + 0: "Normal process", + 1: "Custom process" + }, + WhiteBalance: { + 0: "Auto white balance", + 1: "Manual white balance" + }, + GainControl: { + 0: "None", + 1: "Low gain up", + 2: "High gain up", + 3: "Low gain down", + 4: "High gain down" + }, + Contrast: { + 0: "Normal", + 1: "Soft", + 2: "Hard" + }, + Saturation: { + 0: "Normal", + 1: "Low saturation", + 2: "High saturation" + }, + Sharpness: { + 0: "Normal", + 1: "Soft", + 2: "Hard" + }, + SubjectDistanceRange: { + 0: "Unknown", + 1: "Macro", + 2: "Close view", + 3: "Distant view" + }, + FileSource: { + 3: "DSC" + }, + + Components: { + 0: "", + 1: "Y", + 2: "Cb", + 3: "Cr", + 4: "R", + 5: "G", + 6: "B" + } + } + enableXmp() { + this.isXmpEnabled = true + } + disableXmp() { + this.isXmpEnabled = false; + } + /** + * 获取图片数据 + * @param img 图片地址 + * @param callback 回调 返回图片数据 + * */ + getData(img : any, callback : Function) { + // if (((self.Image && img instanceof self.Image) || (self.HTMLImageElement && img instanceof self.HTMLImageElement)) && !img.complete) + // return false; + let file : File = { + src: '', + exifdata: null, + iptcdata: null, + xmpdata: null, + } + if (isBase64(img)) { + file.src = img + } else if (img.path) { + file.src = img.path + } else if (isString(img)) { + file.src = img + } else { + return false; + } + + + if (!imageHasData(file)) { + getImageData(file, callback); + } else { + if (callback) { + callback.call(file); + } + } + return true; + } + /** + * 获取图片tag + * @param img 图片数据 + * @param tag tag 类型 + * */ + getTag(img : File, tag : string) { + if (!imageHasData(img)) return; + return img.exifdata[tag]; + } + getIptcTag(img : File, tag : string) { + if (!imageHasData(img)) return; + return img.iptcdata[tag]; + } + getAllTags(img : File) { + if (!imageHasData(img)) return {}; + let a, + data = img.exifdata, + tags = {}; + for (a in data) { + if (data.hasOwnProperty(a)) { + tags[a] = data[a]; + } + } + return tags; + } + getAllIptcTags(img : File) { + if (!imageHasData(img)) return {}; + let a, + data = img.iptcdata, + tags = {}; + for (a in data) { + if (data.hasOwnProperty(a)) { + tags[a] = data[a]; + } + } + return tags; + } + pretty(img : File) { + if (!imageHasData(img)) return ""; + let a, + data = img.exifdata, + strPretty = ""; + for (a in data) { + if (data.hasOwnProperty(a)) { + if (typeof data[a] == "object") { + if (data[a] instanceof Number) { + strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a] + .denominator + "]\r\n"; + } else { + strPretty += a + " : [" + data[a].length + " values]\r\n"; + } + } else { + strPretty += a + " : " + data[a] + "\r\n"; + } + } + } + return strPretty; + } + readFromBinaryFile(file: ArrayBuffer) { + return findEXIFinJPEG(file); + } +} + +export const exif = new EXIF() +// export function getData(img, callback) { +// const exif = new EXIF() +// exif.getData(img, callback) +// } + +// export default {getData} +const ExifTags = exif.Tags +const TiffTags = exif.TiffTags +const IFD1Tags = exif.IFD1Tags +const GPSTags = exif.GPSTags +const StringValues = exif.StringValues + + +function imageHasData(img : File) : boolean { + return !!(img.exifdata); +} + +function objectURLToBlob(url : string, callback : Function) { + try { + const http = new XMLHttpRequest(); + http.open("GET", url, true); + http.responseType = "blob"; + http.onload = function (e) { + if (this.status == 200 || this.status === 0) { + callback(this.response); + } + }; + http.send(); + } catch (e) { + console.warn(e) + } +} + + +function getImageData(img : File, callback : Function) { + function handleBinaryFile(binFile: ArrayBuffer) { + const data = findEXIFinJPEG(binFile); + img.exifdata = data ?? {}; + const iptcdata = findIPTCinJPEG(binFile); + img.iptcdata = iptcdata ?? {}; + if (exif.isXmpEnabled) { + const xmpdata = findXMPinJPEG(binFile); + img.xmpdata = xmpdata ?? {}; + } + if (callback) { + callback.call(img); + } + } + + if (img.src) { + if (/^data\:/i.test(img.src)) { // Data URI + // var arrayBuffer = base64ToArrayBuffer(img.src); + handleBinaryFile(base64ToArrayBuffer(img.src)); + + } else if (/^blob\:/i.test(img.src) && typeof FileReader !== 'undefined') { // Object URL + var fileReader = new FileReader(); + fileReader.onload = function (e) { + handleBinaryFile(e.target.result); + }; + objectURLToBlob(img.src, function (blob : Blob) { + fileReader.readAsArrayBuffer(blob); + }); + } else if (typeof XMLHttpRequest !== 'undefined') { + var http = new XMLHttpRequest(); + http.onload = function () { + if (this.status == 200 || this.status === 0) { + handleBinaryFile(http.response); + } else { + throw "Could not load image"; + } + http = null; + }; + http.open("GET", img.src, true); + http.responseType = "arraybuffer"; + http.send(null); + } else { + pathToBase64(img.src).then(res => { + handleBinaryFile(base64ToArrayBuffer(res)); + }) + } + } else if (typeof FileReader !== 'undefined' && self.FileReader && (img instanceof self.Blob || img instanceof self.File)) { + var fileReader = new FileReader(); + fileReader.onload = function (e : any) { + if (exif.debug) console.log("Got file of length " + e.target.result.byteLength); + handleBinaryFile(e.target.result); + }; + + fileReader.readAsArrayBuffer(img); + } +} + +function findEXIFinJPEG(file: ArrayBuffer) { + const dataView = new DataView(file); + + if (exif.debug) console.log("Got file of length " + file.byteLength); + if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { + if (exif.debug) console.log("Not a valid JPEG"); + return false; // not a valid jpeg + } + + let offset = 2, + length = file.byteLength, + marker; + + while (offset < length) { + if (dataView.getUint8(offset) != 0xFF) { + if (exif.debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8( + offset)); + return false; // not a valid marker, something is wrong + } + + marker = dataView.getUint8(offset + 1); + if (exif.debug) console.log(marker); + + // we could implement handling for other markers here, + // but we're only looking for 0xFFE1 for EXIF data + + if (marker == 225) { + if (exif.debug) console.log("Found 0xFFE1 marker"); + + return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2); + + // offset += 2 + file.getShortAt(offset+2, true); + + } else { + offset += 2 + dataView.getUint16(offset + 2); + } + + } + +} + +function findIPTCinJPEG(file: ArrayBuffer) { + const dataView = new DataView(file); + + if (exif.debug) console.log("Got file of length " + file.byteLength); + if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { + if (exif.debug) console.log("Not a valid JPEG"); + return false; // not a valid jpeg + } + + let offset = 2, + length = file.byteLength; + + + const isFieldSegmentStart = function (dataView, offset: number) { + return ( + dataView.getUint8(offset) === 0x38 && + dataView.getUint8(offset + 1) === 0x42 && + dataView.getUint8(offset + 2) === 0x49 && + dataView.getUint8(offset + 3) === 0x4D && + dataView.getUint8(offset + 4) === 0x04 && + dataView.getUint8(offset + 5) === 0x04 + ); + }; + + while (offset < length) { + + if (isFieldSegmentStart(dataView, offset)) { + + // Get the length of the name header (which is padded to an even number of bytes) + var nameHeaderLength = dataView.getUint8(offset + 7); + if (nameHeaderLength % 2 !== 0) nameHeaderLength += 1; + // Check for pre photoshop 6 format + if (nameHeaderLength === 0) { + // Always 4 + nameHeaderLength = 4; + } + + var startOffset = offset + 8 + nameHeaderLength; + var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength); + + return readIPTCData(file, startOffset, sectionLength); + + break; + + } + + + // Not the marker, continue searching + offset++; + + } + +} + +const IptcFieldMap = { + 0x78: 'caption', + 0x6E: 'credit', + 0x19: 'keywords', + 0x37: 'dateCreated', + 0x50: 'byline', + 0x55: 'bylineTitle', + 0x7A: 'captionWriter', + 0x69: 'headline', + 0x74: 'copyright', + 0x0F: 'category' +}; + +function readIPTCData(file: ArrayBuffer, startOffset: number, sectionLength: number) { + const dataView = new DataView(file); + let data = {}; + let fieldValue, fieldName, dataSize, segmentType, segmentSize; + let segmentStartPos = startOffset; + while (segmentStartPos < startOffset + sectionLength) { + if (dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos + 1) === 0x02) { + segmentType = dataView.getUint8(segmentStartPos + 2); + if (segmentType in IptcFieldMap) { + dataSize = dataView.getInt16(segmentStartPos + 3); + segmentSize = dataSize + 5; + fieldName = IptcFieldMap[segmentType]; + fieldValue = getStringFromDB(dataView, segmentStartPos + 5, dataSize); + // Check if we already stored a value with this name + if (data.hasOwnProperty(fieldName)) { + // Value already stored with this name, create multivalue field + if (data[fieldName] instanceof Array) { + data[fieldName].push(fieldValue); + } else { + data[fieldName] = [data[fieldName], fieldValue]; + } + } else { + data[fieldName] = fieldValue; + } + } + + } + segmentStartPos++; + } + return data; +} + +function readTags(file: DataView, tiffStart: number, dirStart: number, strings: any[], bigEnd: number) { + let entries = file.getUint16(dirStart, !bigEnd), + tags = {}, + entryOffset, tag; + + for (let i = 0; i < entries; i++) { + entryOffset = dirStart + i * 12 + 2; + tag = strings[file.getUint16(entryOffset, !bigEnd)]; + if (!tag && exif.debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd)); + tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd); + } + return tags; +} + +function readTagValue(file: DataView, entryOffset: number, tiffStart: number, dirStart: number, bigEnd: number) { + let type = file.getUint16(entryOffset + 2, !bigEnd), + numValues = file.getUint32(entryOffset + 4, !bigEnd), + valueOffset = file.getUint32(entryOffset + 8, !bigEnd) + tiffStart, + offset, + vals, val, n, + numerator, denominator; + + switch (type) { + case 1: // byte, 8-bit unsigned int + case 7: // undefined, 8-bit byte, value depending on field + if (numValues == 1) { + return file.getUint8(entryOffset + 8, !bigEnd); + } else { + offset = numValues > 4 ? valueOffset : (entryOffset + 8); + vals = []; + for (n = 0; n < numValues; n++) { + vals[n] = file.getUint8(offset + n); + } + return vals; + } + + case 2: // ascii, 8-bit byte + offset = numValues > 4 ? valueOffset : (entryOffset + 8); + return getStringFromDB(file, offset, numValues - 1); + + case 3: // short, 16 bit int + if (numValues == 1) { + return file.getUint16(entryOffset + 8, !bigEnd); + } else { + offset = numValues > 2 ? valueOffset : (entryOffset + 8); + vals = []; + for (n = 0; n < numValues; n++) { + vals[n] = file.getUint16(offset + 2 * n, !bigEnd); + } + return vals; + } + + case 4: // long, 32 bit int + if (numValues == 1) { + return file.getUint32(entryOffset + 8, !bigEnd); + } else { + vals = []; + for (n = 0; n < numValues; n++) { + vals[n] = file.getUint32(valueOffset + 4 * n, !bigEnd); + } + return vals; + } + + case 5: // rational = two long values, first is numerator, second is denominator + if (numValues == 1) { + numerator = file.getUint32(valueOffset, !bigEnd); + denominator = file.getUint32(valueOffset + 4, !bigEnd); + val = new Number(numerator / denominator); + val.numerator = numerator; + val.denominator = denominator; + return val; + } else { + vals = []; + for (n = 0; n < numValues; n++) { + numerator = file.getUint32(valueOffset + 8 * n, !bigEnd); + denominator = file.getUint32(valueOffset + 4 + 8 * n, !bigEnd); + vals[n] = new Number(numerator / denominator); + vals[n].numerator = numerator; + vals[n].denominator = denominator; + } + return vals; + } + + case 9: // slong, 32 bit signed int + if (numValues == 1) { + return file.getInt32(entryOffset + 8, !bigEnd); + } else { + vals = []; + for (n = 0; n < numValues; n++) { + vals[n] = file.getInt32(valueOffset + 4 * n, !bigEnd); + } + return vals; + } + + case 10: // signed rational, two slongs, first is numerator, second is denominator + if (numValues == 1) { + return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset + 4, !bigEnd); + } else { + vals = []; + for (n = 0; n < numValues; n++) { + vals[n] = file.getInt32(valueOffset + 8 * n, !bigEnd) / file.getInt32(valueOffset + 4 + 8 * + n, !bigEnd); + } + return vals; + } + } +} +/** + * Given an IFD (Image File Directory) start offset + * returns an offset to next IFD or 0 if it's the last IFD. + */ +function getNextIFDOffset(dataView: DataView, dirStart: number, bigEnd: number) { + //the first 2bytes means the number of directory entries contains in this IFD + var entries = dataView.getUint16(dirStart, !bigEnd); + + // After last directory entry, there is a 4bytes of data, + // it means an offset to next IFD. + // If its value is '0x00000000', it means this is the last IFD and there is no linked IFD. + + return dataView.getUint32(dirStart + 2 + entries * 12, !bigEnd); // each entry is 12 bytes long +} + +function readThumbnailImage(dataView: DataView, tiffStart: number, firstIFDOffset: number, bigEnd: number) { + // get the IFD1 offset + const IFD1OffsetPointer = getNextIFDOffset(dataView, tiffStart + firstIFDOffset, bigEnd); + + if (!IFD1OffsetPointer) { + // console.log('******** IFD1Offset is empty, image thumb not found ********'); + return {}; + } else if (IFD1OffsetPointer > dataView.byteLength) { // this should not happen + // console.log('******** IFD1Offset is outside the bounds of the DataView ********'); + return {}; + } + // console.log('******* thumbnail IFD offset (IFD1) is: %s', IFD1OffsetPointer); + + let thumbTags : any = readTags(dataView, tiffStart, tiffStart + IFD1OffsetPointer, IFD1Tags, bigEnd) + + // EXIF 2.3 specification for JPEG format thumbnail + + // If the value of Compression(0x0103) Tag in IFD1 is '6', thumbnail image format is JPEG. + // Most of Exif image uses JPEG format for thumbnail. In that case, you can get offset of thumbnail + // by JpegIFOffset(0x0201) Tag in IFD1, size of thumbnail by JpegIFByteCount(0x0202) Tag. + // Data format is ordinary JPEG format, starts from 0xFFD8 and ends by 0xFFD9. It seems that + // JPEG format and 160x120pixels of size are recommended thumbnail format for Exif2.1 or later. + + if (thumbTags['Compression'] && typeof Blob !== 'undefined') { + // console.log('Thumbnail image found!'); + + switch (thumbTags['Compression']) { + case 6: + // console.log('Thumbnail image format is JPEG'); + if (thumbTags.JpegIFOffset && thumbTags.JpegIFByteCount) { + // extract the thumbnail + var tOffset = tiffStart + thumbTags.JpegIFOffset; + var tLength = thumbTags.JpegIFByteCount; + thumbTags['blob'] = new Blob([new Uint8Array(dataView.buffer, tOffset, tLength)], { + type: 'image/jpeg' + }); + } + break; + + case 1: + console.log("Thumbnail image format is TIFF, which is not implemented."); + break; + default: + console.log("Unknown thumbnail image format '%s'", thumbTags['Compression']); + } + } else if (thumbTags['PhotometricInterpretation'] == 2) { + console.log("Thumbnail image format is RGB, which is not implemented."); + } + return thumbTags; +} + +function getStringFromDB(buffer: DataView, start: number, length: number) { + let outstr = ""; + for (let n = start; n < start + length; n++) { + outstr += String.fromCharCode(buffer.getUint8(n)); + } + return outstr; +} + +function readEXIFData(file: DataView, start: number) { + if (getStringFromDB(file, start, 4) != "Exif") { + if (exif.debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4)); + return false; + } + + let bigEnd, + tags, tag, + exifData, gpsData, + tiffOffset = start + 6; + + // test for TIFF validity and endianness + if (file.getUint16(tiffOffset) == 0x4949) { + bigEnd = false; + } else if (file.getUint16(tiffOffset) == 0x4D4D) { + bigEnd = true; + } else { + if (exif.debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)"); + return false; + } + + if (file.getUint16(tiffOffset + 2, !bigEnd) != 0x002A) { + if (exif.debug) console.log("Not valid TIFF data! (no 0x002A)"); + return false; + } + + const firstIFDOffset = file.getUint32(tiffOffset + 4, !bigEnd); + + if (firstIFDOffset < 0x00000008) { + if (exif.debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset + 4, + !bigEnd)); + return false; + } + + tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd); + + if (tags.ExifIFDPointer) { + exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd); + for (tag in exifData) { + switch (tag) { + case "LightSource": + case "Flash": + case "MeteringMode": + case "ExposureProgram": + case "SensingMethod": + case "SceneCaptureType": + case "SceneType": + case "CustomRendered": + case "WhiteBalance": + case "GainControl": + case "Contrast": + case "Saturation": + case "Sharpness": + case "SubjectDistanceRange": + case "FileSource": + exifData[tag] = StringValues[tag][exifData[tag]]; + break; + + case "ExifVersion": + case "FlashpixVersion": + exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], + exifData[tag][3]); + break; + + case "ComponentsConfiguration": + exifData[tag] = + StringValues.Components[exifData[tag][0]] + + StringValues.Components[exifData[tag][1]] + + StringValues.Components[exifData[tag][2]] + + StringValues.Components[exifData[tag][3]]; + break; + } + tags[tag] = exifData[tag]; + } + } + + if (tags.GPSInfoIFDPointer) { + gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd); + for (tag in gpsData) { + switch (tag) { + case "GPSVersionID": + gpsData[tag] = gpsData[tag][0] + + "." + gpsData[tag][1] + + "." + gpsData[tag][2] + + "." + gpsData[tag][3]; + break; + } + tags[tag] = gpsData[tag]; + } + } + + // extract thumbnail + tags['thumbnail'] = readThumbnailImage(file, tiffOffset, firstIFDOffset, bigEnd); + + return tags; +} + +function findXMPinJPEG(file: ArrayBuffer) { + + if (!('DOMParser' in self)) { + // console.warn('XML parsing not supported without DOMParser'); + return; + } + const dataView = new DataView(file); + + if (exif.debug) console.log("Got file of length " + file.byteLength); + if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) { + if (exif.debug) console.log("Not a valid JPEG"); + return false; // not a valid jpeg + } + + let offset = 2, + length = file.byteLength, + dom = new DOMParser(); + + while (offset < (length - 4)) { + if (getStringFromDB(dataView, offset, 4) == "http") { + const startOffset = offset - 1; + const sectionLength = dataView.getUint16(offset - 2) - 1; + let xmpString = getStringFromDB(dataView, startOffset, sectionLength) + const xmpEndIndex = xmpString.indexOf('xmpmeta>') + 8; + xmpString = xmpString.substring(xmpString.indexOf(' 0) { + json['@attributes'] = {}; + for (var j = 0; j < xml.attributes.length; j++) { + var attribute = xml.attributes.item(j); + json['@attributes'][attribute.nodeName] = attribute.nodeValue; + } + } + } else if (xml.nodeType == 3) { // text node + return xml.nodeValue; + } + + // deal with children + if (xml.hasChildNodes()) { + for (var i = 0; i < xml.childNodes.length; i++) { + var child = xml.childNodes.item(i); + var nodeName = child.nodeName; + if (json[nodeName] == null) { + json[nodeName] = xml2json(child); + } else { + if (json[nodeName].push == null) { + var old = json[nodeName]; + json[nodeName] = []; + json[nodeName].push(old); + } + json[nodeName].push(xml2json(child)); + } + } + } + + return json; +} + +function xml2Object(xml: any) { + try { + var obj = {}; + if (xml.children.length > 0) { + for (var i = 0; i < xml.children.length; i++) { + var item = xml.children.item(i); + var attributes = item.attributes; + for (var idx in attributes) { + var itemAtt = attributes[idx]; + var dataKey = itemAtt.nodeName; + var dataValue = itemAtt.nodeValue; + + if (dataKey !== undefined) { + obj[dataKey] = dataValue; + } + } + var nodeName = item.nodeName; + + if (typeof (obj[nodeName]) == "undefined") { + obj[nodeName] = xml2json(item); + } else { + if (typeof (obj[nodeName].push) == "undefined") { + var old = obj[nodeName]; + + obj[nodeName] = []; + obj[nodeName].push(old); + } + obj[nodeName].push(xml2json(item)); + } + } + } else { + obj = xml.textContent; + } + return obj; + } catch (e) { + console.log(e.message); + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/fillZero/index.ts b/src/uni_modules/lime-shared/fillZero/index.ts new file mode 100644 index 00000000..9952c45a --- /dev/null +++ b/src/uni_modules/lime-shared/fillZero/index.ts @@ -0,0 +1,11 @@ +// @ts-nocheck +/** + * 在数字前填充零,返回字符串形式的结果 + * @param number 要填充零的数字 + * @param length 填充零后的字符串长度,默认为2 + * @returns 填充零后的字符串 + */ +export function fillZero(number: number, length: number = 2): string { + // 将数字转换为字符串,然后使用 padStart 方法填充零到指定长度 + return `${number}`.padStart(length, '0'); +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/floatAdd/index.ts b/src/uni_modules/lime-shared/floatAdd/index.ts new file mode 100644 index 00000000..aba4774d --- /dev/null +++ b/src/uni_modules/lime-shared/floatAdd/index.ts @@ -0,0 +1,36 @@ +import {isNumber} from '../isNumber' +/** + * 返回两个浮点数相加的结果 + * @param num1 第一个浮点数 + * @param num2 第二个浮点数 + * @returns 两个浮点数的相加结果 + */ +export function floatAdd(num1: number, num2: number): number { + // 检查 num1 和 num2 是否为数字类型 + if (!(isNumber(num1) || isNumber(num2))) { + console.warn('Please pass in the number type'); + return NaN; + } + + let r1: number, r2: number, m: number; + + try { + // 获取 num1 小数点后的位数 + r1 = num1.toString().split('.')[1].length; + } catch (error) { + r1 = 0; + } + + try { + // 获取 num2 小数点后的位数 + r2 = num2.toString().split('.')[1].length; + } catch (error) { + r2 = 0; + } + + // 计算需要扩大的倍数 + m = Math.pow(10, Math.max(r1, r2)); + + // 返回相加结果 + return (num1 * m + num2 * m) / m; +} diff --git a/src/uni_modules/lime-shared/getClassStr/index.ts b/src/uni_modules/lime-shared/getClassStr/index.ts new file mode 100644 index 00000000..c3c62ac2 --- /dev/null +++ b/src/uni_modules/lime-shared/getClassStr/index.ts @@ -0,0 +1,27 @@ +// @ts-nocheck +/** + * 获取对象的类名字符串 + * @param obj - 需要处理的对象 + * @returns 由对象属性作为类名组成的字符串 + */ +export function getClassStr(obj: T): string { + let classNames: string[] = []; + + // 遍历对象的属性 + for (let key in obj) { + // 检查属性确实属于对象自身且其值为true + if ((obj as any).hasOwnProperty(key) && obj[key]) { + // 将属性名添加到类名数组中 + classNames.push(key); + } + } + + // 将类名数组用空格连接成字符串并返回 + return classNames.join(' '); +} + + +// 示例 +// const obj = { foo: true, bar: false, baz: true }; +// const classNameStr = getClassStr(obj); +// console.log(classNameStr); // 输出: "foo baz" \ No newline at end of file diff --git a/src/uni_modules/lime-shared/getCurrentPage/index.ts b/src/uni_modules/lime-shared/getCurrentPage/index.ts new file mode 100644 index 00000000..79ecac83 --- /dev/null +++ b/src/uni_modules/lime-shared/getCurrentPage/index.ts @@ -0,0 +1,6 @@ +// @ts-nocheck +/** 获取当前页 */ +export const getCurrentPage = () => { + const pages = getCurrentPages(); + return pages[pages.length - 1] //as T & WechatMiniprogram.Page.TrivialInstance; +}; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/getLocalFilePath/index.ts b/src/uni_modules/lime-shared/getLocalFilePath/index.ts new file mode 100644 index 00000000..8d6a68ca --- /dev/null +++ b/src/uni_modules/lime-shared/getLocalFilePath/index.ts @@ -0,0 +1,14 @@ +// @ts-nocheck +export const getLocalFilePath = (path: string) => { + if(typeof plus == 'undefined') return path + if(/^(_www|_doc|_documents|_downloads|file:\/\/|\/storage\/emulated\/0\/)/.test(path)) return path + if (/^\//.test(path)) { + const localFilePath = plus.io.convertAbsoluteFileSystem(path) + if (localFilePath !== path) { + return localFilePath + } else { + path = path.slice(1) + } + } + return '_www/' + path +} diff --git a/src/uni_modules/lime-shared/getRect/index.ts b/src/uni_modules/lime-shared/getRect/index.ts new file mode 100644 index 00000000..9763ee20 --- /dev/null +++ b/src/uni_modules/lime-shared/getRect/index.ts @@ -0,0 +1,86 @@ +// @ts-nocheck + +// #ifdef APP-NVUE +// 当编译环境是 APP-NVUE 时,引入 uni.requireNativePlugin('dom'),具体插件用途未知 +const dom = uni.requireNativePlugin('dom') +// #endif + +interface RectOptions { + /** + * 上下文 + */ + context ?: any // ComponentInternalInstance 类型,用于指定上下文 + + /** + * 是否需要获取所有节点,nvue 环境下不支持 + */ + needAll ?: boolean, + + /** + * 节点引用对象,类型为 UniNamespace.NodesRef + */ + nodes ?: UniNamespace.NodesRef + + /** + * 节点引用对象的键,类型为 UniNamespace.NodesRef 中的某个键 + */ + type ?: keyof UniNamespace.NodesRef +} + +/** + * 获取节点信息 + * @param selector 选择器字符串 + * @param options RectOptions 对象,用于配置选项 + * @returns 包含节点信息的 Promise 对象 + */ +export function getRect(selector : string, options : RectOptions = {}) { + // #ifndef APP-NVUE + const typeDefault = 'boundingClientRect' + let { context, needAll, type = typeDefault } = options + // #endif + + // #ifdef MP || VUE2 + if (context.proxy) context = context.proxy + // #endif + + return new Promise((resolve, reject) => { + // #ifndef APP-NVUE + const dom = uni.createSelectorQuery().in(context)[needAll ? 'selectAll' : 'select'](selector); + const result = (rect: UniNamespace.NodeInfo) => { + if (rect) { + resolve(rect) + } else { + reject('no rect') + } + } + if (type == typeDefault) { + dom[type](result).exec() + } else { + dom[type]({ + node: true, + size: true, + rect: true + }, result).exec() + } + // #endif + // #ifdef APP-NVUE + let { context } = options + if (/#|\./.test(selector) && context.refs) { + selector = selector.replace(/#|\./, '') + if (context.refs[selector]) { + selector = context.refs[selector] + if(Array.isArray(selector)) { + selector = selector[0] + } + } + } + dom.getComponentRect(selector, (res) => { + if (res.size) { + resolve(res.size) + } else { + reject('no rect') + } + }) + // #endif + }); +}; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/getStyleStr/index.ts b/src/uni_modules/lime-shared/getStyleStr/index.ts new file mode 100644 index 00000000..33decd5b --- /dev/null +++ b/src/uni_modules/lime-shared/getStyleStr/index.ts @@ -0,0 +1,30 @@ +// @ts-nocheck +interface CSSProperties { + [key: string]: string | number +} +/** + * 将字符串转换为带有连字符分隔的小写形式 + * @param key - 要转换的字符串 + * @returns 转换后的字符串 + */ +export function toLowercaseSeparator(key: string) { + return key.replace(/([A-Z])/g, '-$1').toLowerCase(); +} + +/** + * 获取样式对象对应的样式字符串 + * @param style - CSS样式对象 + * @returns 由非空有效样式属性键值对组成的字符串 + */ +export function getStyleStr(style: CSSProperties): string { + return Object.keys(style) + .filter(key => style[key] !== undefined && style[key] !== null && style[key] !== '') + .map((key: string) => `${toLowercaseSeparator(key)}: ${style[key]};`) + .join(' '); +} + +// 示例 +// const style = { color: 'red', fontSize: '16px', backgroundColor: '', border: null }; +// const styleStr = getStyleStr(style); +// console.log(styleStr); +// 输出: "color: red; font-size: 16px;" \ No newline at end of file diff --git a/src/uni_modules/lime-shared/hasOwn/index.ts b/src/uni_modules/lime-shared/hasOwn/index.ts new file mode 100644 index 00000000..73178790 --- /dev/null +++ b/src/uni_modules/lime-shared/hasOwn/index.ts @@ -0,0 +1,30 @@ +// @ts-nocheck +const hasOwnProperty = Object.prototype.hasOwnProperty +/** + * 检查对象或数组是否具有指定的属性或键 + * @param obj 要检查的对象或数组 + * @param key 指定的属性或键 + * @returns 如果对象或数组具有指定的属性或键,则返回true;否则返回false + */ +export function hasOwn(obj: Object | Array, key: string): boolean { + return hasOwnProperty.call(obj, key); +} + +// 示例 +// const obj = { name: 'John', age: 30 }; + +// if (hasOwn(obj, 'name')) { +// console.log("对象具有 'name' 属性"); +// } else { +// console.log("对象不具有 'name' 属性"); +// } +// // 输出: 对象具有 'name' 属性 + +// const arr = [1, 2, 3]; + +// if (hasOwn(arr, 'length')) { +// console.log("数组具有 'length' 属性"); +// } else { +// console.log("数组不具有 'length' 属性"); +// } +// 输出: 数组具有 'length' 属性 \ No newline at end of file diff --git a/src/uni_modules/lime-shared/index.ts b/src/uni_modules/lime-shared/index.ts new file mode 100644 index 00000000..8ffb34d6 --- /dev/null +++ b/src/uni_modules/lime-shared/index.ts @@ -0,0 +1,43 @@ +// @ts-nocheck +// validator +export * from './isString' +export * from './isNumber' +export * from './isNumeric' +export * from './isDef' +export * from './isFunction' +export * from './isObject' +export * from './isPromise' +export * from './isBase64' + +export * from './hasOwn' + +// 单位转换 +export * from './addUnit' +export * from './unitConvert' +export * from './toNumber' + +export * from './random' +export * from './range' +export * from './fillZero' + +// image +export * from './base64ToPath' +export * from './pathToBase64' +export * from './exif' + +// canvas +export * from './canIUseCanvas2d' + +// page +export * from './getCurrentPage' + +// dom +export * from './getRect' +export * from './selectComponent' +export * from './createAnimation' + +// delay +export * from './sleep' +export * from './debounce' +export * from './throttle' + diff --git a/src/uni_modules/lime-shared/isBase64/index.ts b/src/uni_modules/lime-shared/isBase64/index.ts new file mode 100644 index 00000000..44d0f6d7 --- /dev/null +++ b/src/uni_modules/lime-shared/isBase64/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +/** + * 判断给定的路径是否为Base64编码的图像路径 + * @param path 图像路径 + * @returns 如果路径是Base64编码,则返回true;否则返回false + */ +export const isBase64 = (path: string): boolean => { + return /^data:image\/(\w+);base64/.test(path); +}; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isBrowser/index.ts b/src/uni_modules/lime-shared/isBrowser/index.ts new file mode 100644 index 00000000..241c7f7f --- /dev/null +++ b/src/uni_modules/lime-shared/isBrowser/index.ts @@ -0,0 +1,2 @@ +// @ts-nocheck +export const isBrowser = typeof window !== 'undefined'; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isDef/index.ts b/src/uni_modules/lime-shared/isDef/index.ts new file mode 100644 index 00000000..029407ef --- /dev/null +++ b/src/uni_modules/lime-shared/isDef/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +/** + * 检查一个值是否已定义(不为 undefined)且不为 null + * @param value 要检查的值 + * @returns 如果值已定义且不为 null,则返回 true;否则返回 false + */ +export function isDef(value: unknown): boolean { + return value !== undefined && value !== null; +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isFunction/index.ts b/src/uni_modules/lime-shared/isFunction/index.ts new file mode 100644 index 00000000..981442d9 --- /dev/null +++ b/src/uni_modules/lime-shared/isFunction/index.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +/** + * 检查一个值是否为函数类型 + * @param val 要检查的值 + * @returns 如果值的类型是函数类型,则返回 true;否则返回 false + */ +export const isFunction = (val: unknown): val is Function => + typeof val === 'function'; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isNumber/index.ts b/src/uni_modules/lime-shared/isNumber/index.ts new file mode 100644 index 00000000..2feff2fd --- /dev/null +++ b/src/uni_modules/lime-shared/isNumber/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +/** + * 检查一个值是否为数字类型 + * @param value 要检查的值,可以是 number 类型或 string 类型的数字 + * @returns 如果值是数字类型且不是 NaN,则返回 true;否则返回 false + */ +export function isNumber(value: number | string): boolean { + return typeof value === 'number' && !isNaN(value); +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isNumeric/index.ts b/src/uni_modules/lime-shared/isNumeric/index.ts new file mode 100644 index 00000000..5ee60ba3 --- /dev/null +++ b/src/uni_modules/lime-shared/isNumeric/index.ts @@ -0,0 +1,9 @@ +// @ts-nocheck +/** + * 检查一个值是否为数字类型或表示数字的字符串 + * @param value 要检查的值,可以是 string 类型或 number 类型 + * @returns 如果值是数字类型或表示数字的字符串,则返回 true;否则返回 false + */ +export function isNumeric(value: string | number): boolean { + return /^(-)?\d+(\.\d+)?$/.test(value); +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isObject/index.ts b/src/uni_modules/lime-shared/isObject/index.ts new file mode 100644 index 00000000..17234c9c --- /dev/null +++ b/src/uni_modules/lime-shared/isObject/index.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +/** + * 检查一个值是否为对象类型 + * @param val 要检查的值 + * @returns 如果值的类型是对象类型,则返回 true;否则返回 false + */ +export const isObject = (val : unknown) : val is Record => + val !== null && typeof val === 'object'; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isPromise/index.ts b/src/uni_modules/lime-shared/isPromise/index.ts new file mode 100644 index 00000000..63847cd1 --- /dev/null +++ b/src/uni_modules/lime-shared/isPromise/index.ts @@ -0,0 +1,13 @@ +// @ts-nocheck +import {isFunction} from '../isFunction' +import {isObject} from '../isObject' +/** + * 检查一个值是否为 Promise 类型 + * @param val 要检查的值 + * @returns 如果值的类型是 Promise 类型,则返回 true;否则返回 false + */ +export const isPromise = (val: unknown): val is Promise => { + // 使用 isObject 函数判断值是否为对象类型 + // 使用 isFunction 函数判断值是否具有 then 方法和 catch 方法 + return isObject(val) && isFunction(val.then) && isFunction(val.catch); +}; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/isString/index.ts b/src/uni_modules/lime-shared/isString/index.ts new file mode 100644 index 00000000..a130eb28 --- /dev/null +++ b/src/uni_modules/lime-shared/isString/index.ts @@ -0,0 +1,7 @@ +// @ts-nocheck +/** + * 检查一个值是否为字符串类型 + * @param str 要检查的值 + * @returns 如果值的类型是字符串类型,则返回 true;否则返回 false + */ +export const isString = (str: unknown): str is string => typeof str === 'string'; \ No newline at end of file diff --git a/src/uni_modules/lime-shared/kebabCase/index.ts b/src/uni_modules/lime-shared/kebabCase/index.ts new file mode 100644 index 00000000..fa0e4b2d --- /dev/null +++ b/src/uni_modules/lime-shared/kebabCase/index.ts @@ -0,0 +1,17 @@ +// export function toLowercaseSeparator(key: string) { +// return key.replace(/([A-Z])/g, '-$1').toLowerCase(); +// } + +/** + * 将字符串转换为指定连接符的命名约定 + * @param str 要转换的字符串 + * @param separator 指定的连接符,默认为 "-" + * @returns 转换后的字符串 + */ +export function kebabCase(str: string, separator: string = "-"): string { + return str + .replace(/[A-Z]/g, match => `${separator}${match.toLowerCase()}`) // 将大写字母替换为连接符加小写字母 + .replace(/[\s_-]+/g, separator) // 将空格、下划线和短横线替换为指定连接符 + .replace(new RegExp(`^${separator}|${separator}$`, "g"), "") // 删除开头和结尾的连接符 + .toLowerCase(); // 将结果转换为全小写 +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/package.json b/src/uni_modules/lime-shared/package.json new file mode 100644 index 00000000..2e000c98 --- /dev/null +++ b/src/uni_modules/lime-shared/package.json @@ -0,0 +1,83 @@ +{ + "id": "lime-shared", + "displayName": "lime-shared", + "version": "0.1.4", + "description": "本人插件的几个公共函数,获取当前页,图片的base64转临时路径,图片的exif信息等", + "keywords": [ + "lime-shared", + "exif", + "selectComponent" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "type": "sdk-js", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "y" + }, + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y", + "钉钉": "y", + "快手": "y", + "飞书": "y", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/pathToBase64/index.ts b/src/uni_modules/lime-shared/pathToBase64/index.ts new file mode 100644 index 00000000..8167f882 --- /dev/null +++ b/src/uni_modules/lime-shared/pathToBase64/index.ts @@ -0,0 +1,121 @@ +// @ts-nocheck + +// #ifdef APP-PLUS +import { getLocalFilePath } from '../getLocalFilePath' +// #endif +function isImage(extension : string) { + const imageExtensions = ["jpg", "jpeg", "png", "gif", "bmp", "svg"]; + return imageExtensions.includes(extension.toLowerCase()); +} +// #ifdef H5 +function getSVGFromURL(url: string) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.responseType = 'text'; + + xhr.onload = function () { + if (xhr.status === 200) { + const svg = xhr.responseText; + resolve(svg); + } else { + reject(new Error(xhr.statusText)); + } + }; + + xhr.onerror = function () { + reject(new Error('Network error')); + }; + + xhr.send(); + }); +} +// #endif +/** + * 路径转base64 + * @param {Object} string + */ +export function pathToBase64(path : string) : Promise { + if (/^data:/.test(path)) return path + let extension = path.substring(path.lastIndexOf('.') + 1); + const isImageFile = isImage(extension) + let prefix = '' + if (isImageFile) { + prefix = 'image/'; + if(extension == 'svg') { + extension += '+xml' + } + } else if (extension === 'pdf') { + prefix = 'application/pdf'; + } else if (extension === 'txt') { + prefix = 'text/plain'; + } else { + // 添加更多文件类型的判断 + // 如果不是图片、PDF、文本等类型,可以设定默认的前缀或采取其他处理 + prefix = 'application/octet-stream'; + } + return new Promise((resolve, reject) => { + // #ifdef H5 + if (isImageFile) { + if(extension == 'svg') { + getSVGFromURL(path).then(svg => { + const base64 = btoa(svg); + resolve(`data:image/svg+xml;base64,${base64}`); + }) + } else { + let image = new Image(); + image.setAttribute("crossOrigin", 'Anonymous'); + image.onload = function () { + let canvas = document.createElement('canvas'); + canvas.width = this.naturalWidth; + canvas.height = this.naturalHeight; + canvas.getContext('2d').drawImage(image, 0, 0); + let result = canvas.toDataURL(`${prefix}${extension}`) + resolve(result); + canvas.height = canvas.width = 0 + } + image.src = path + '?v=' + Math.random() + image.onerror = (error) => { + reject(error); + }; + } + + } else { + reject('not image'); + } + + // #endif + + // #ifdef MP + if (uni.canIUse('getFileSystemManager')) { + uni.getFileSystemManager().readFile({ + filePath: path, + encoding: 'base64', + success: (res) => { + resolve(`data:${prefix}${extension};base64,${res.data}`) + }, + fail: (error) => { + console.error({ error, path }) + reject(error) + } + }) + } + // #endif + + // #ifdef APP-PLUS + plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => { + entry.file((file : any) => { + const fileReader = new plus.io.FileReader() + fileReader.onload = (data) => { + resolve(data.target.result) + } + fileReader.onerror = (error) => { + console.error({ error, path }) + reject(error) + } + fileReader.readAsDataURL(file) + }, reject) + }, reject) + // #endif + }) +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/piexif/index.ts b/src/uni_modules/lime-shared/piexif/index.ts new file mode 100644 index 00000000..6a52b10e --- /dev/null +++ b/src/uni_modules/lime-shared/piexif/index.ts @@ -0,0 +1,2320 @@ +// @ts-nocheck +// 源于piexifjs +import { cloneDeep } from '../cloneDeep' +import { isString } from '../isString' +const TAGS = { + 'Image': { + 11: { + 'name': 'ProcessingSoftware', + 'type': 'Ascii' + }, + 254: { + 'name': 'NewSubfileType', + 'type': 'Long' + }, + 255: { + 'name': 'SubfileType', + 'type': 'Short' + }, + 256: { + 'name': 'ImageWidth', + 'type': 'Long' + }, + 257: { + 'name': 'ImageLength', + 'type': 'Long' + }, + 258: { + 'name': 'BitsPerSample', + 'type': 'Short' + }, + 259: { + 'name': 'Compression', + 'type': 'Short' + }, + 262: { + 'name': 'PhotometricInterpretation', + 'type': 'Short' + }, + 263: { + 'name': 'Threshholding', + 'type': 'Short' + }, + 264: { + 'name': 'CellWidth', + 'type': 'Short' + }, + 265: { + 'name': 'CellLength', + 'type': 'Short' + }, + 266: { + 'name': 'FillOrder', + 'type': 'Short' + }, + 269: { + 'name': 'DocumentName', + 'type': 'Ascii' + }, + 270: { + 'name': 'ImageDescription', + 'type': 'Ascii' + }, + 271: { + 'name': 'Make', + 'type': 'Ascii' + }, + 272: { + 'name': 'Model', + 'type': 'Ascii' + }, + 273: { + 'name': 'StripOffsets', + 'type': 'Long' + }, + 274: { + 'name': 'Orientation', + 'type': 'Short' + }, + 277: { + 'name': 'SamplesPerPixel', + 'type': 'Short' + }, + 278: { + 'name': 'RowsPerStrip', + 'type': 'Long' + }, + 279: { + 'name': 'StripByteCounts', + 'type': 'Long' + }, + 282: { + 'name': 'XResolution', + 'type': 'Rational' + }, + 283: { + 'name': 'YResolution', + 'type': 'Rational' + }, + 284: { + 'name': 'PlanarConfiguration', + 'type': 'Short' + }, + 290: { + 'name': 'GrayResponseUnit', + 'type': 'Short' + }, + 291: { + 'name': 'GrayResponseCurve', + 'type': 'Short' + }, + 292: { + 'name': 'T4Options', + 'type': 'Long' + }, + 293: { + 'name': 'T6Options', + 'type': 'Long' + }, + 296: { + 'name': 'ResolutionUnit', + 'type': 'Short' + }, + 301: { + 'name': 'TransferFunction', + 'type': 'Short' + }, + 305: { + 'name': 'Software', + 'type': 'Ascii' + }, + 306: { + 'name': 'DateTime', + 'type': 'Ascii' + }, + 315: { + 'name': 'Artist', + 'type': 'Ascii' + }, + 316: { + 'name': 'HostComputer', + 'type': 'Ascii' + }, + 317: { + 'name': 'Predictor', + 'type': 'Short' + }, + 318: { + 'name': 'WhitePoint', + 'type': 'Rational' + }, + 319: { + 'name': 'PrimaryChromaticities', + 'type': 'Rational' + }, + 320: { + 'name': 'ColorMap', + 'type': 'Short' + }, + 321: { + 'name': 'HalftoneHints', + 'type': 'Short' + }, + 322: { + 'name': 'TileWidth', + 'type': 'Short' + }, + 323: { + 'name': 'TileLength', + 'type': 'Short' + }, + 324: { + 'name': 'TileOffsets', + 'type': 'Short' + }, + 325: { + 'name': 'TileByteCounts', + 'type': 'Short' + }, + 330: { + 'name': 'SubIFDs', + 'type': 'Long' + }, + 332: { + 'name': 'InkSet', + 'type': 'Short' + }, + 333: { + 'name': 'InkNames', + 'type': 'Ascii' + }, + 334: { + 'name': 'NumberOfInks', + 'type': 'Short' + }, + 336: { + 'name': 'DotRange', + 'type': 'Byte' + }, + 337: { + 'name': 'TargetPrinter', + 'type': 'Ascii' + }, + 338: { + 'name': 'ExtraSamples', + 'type': 'Short' + }, + 339: { + 'name': 'SampleFormat', + 'type': 'Short' + }, + 340: { + 'name': 'SMinSampleValue', + 'type': 'Short' + }, + 341: { + 'name': 'SMaxSampleValue', + 'type': 'Short' + }, + 342: { + 'name': 'TransferRange', + 'type': 'Short' + }, + 343: { + 'name': 'ClipPath', + 'type': 'Byte' + }, + 344: { + 'name': 'XClipPathUnits', + 'type': 'Long' + }, + 345: { + 'name': 'YClipPathUnits', + 'type': 'Long' + }, + 346: { + 'name': 'Indexed', + 'type': 'Short' + }, + 347: { + 'name': 'JPEGTables', + 'type': 'Undefined' + }, + 351: { + 'name': 'OPIProxy', + 'type': 'Short' + }, + 512: { + 'name': 'JPEGProc', + 'type': 'Long' + }, + 513: { + 'name': 'JPEGInterchangeFormat', + 'type': 'Long' + }, + 514: { + 'name': 'JPEGInterchangeFormatLength', + 'type': 'Long' + }, + 515: { + 'name': 'JPEGRestartInterval', + 'type': 'Short' + }, + 517: { + 'name': 'JPEGLosslessPredictors', + 'type': 'Short' + }, + 518: { + 'name': 'JPEGPointTransforms', + 'type': 'Short' + }, + 519: { + 'name': 'JPEGQTables', + 'type': 'Long' + }, + 520: { + 'name': 'JPEGDCTables', + 'type': 'Long' + }, + 521: { + 'name': 'JPEGACTables', + 'type': 'Long' + }, + 529: { + 'name': 'YCbCrCoefficients', + 'type': 'Rational' + }, + 530: { + 'name': 'YCbCrSubSampling', + 'type': 'Short' + }, + 531: { + 'name': 'YCbCrPositioning', + 'type': 'Short' + }, + 532: { + 'name': 'ReferenceBlackWhite', + 'type': 'Rational' + }, + 700: { + 'name': 'XMLPacket', + 'type': 'Byte' + }, + 18246: { + 'name': 'Rating', + 'type': 'Short' + }, + 18249: { + 'name': 'RatingPercent', + 'type': 'Short' + }, + 32781: { + 'name': 'ImageID', + 'type': 'Ascii' + }, + 33421: { + 'name': 'CFARepeatPatternDim', + 'type': 'Short' + }, + 33422: { + 'name': 'CFAPattern', + 'type': 'Byte' + }, + 33423: { + 'name': 'BatteryLevel', + 'type': 'Rational' + }, + 33432: { + 'name': 'Copyright', + 'type': 'Ascii' + }, + 33434: { + 'name': 'ExposureTime', + 'type': 'Rational' + }, + 34377: { + 'name': 'ImageResources', + 'type': 'Byte' + }, + 34665: { + 'name': 'ExifTag', + 'type': 'Long' + }, + 34675: { + 'name': 'InterColorProfile', + 'type': 'Undefined' + }, + 34853: { + 'name': 'GPSTag', + 'type': 'Long' + }, + 34857: { + 'name': 'Interlace', + 'type': 'Short' + }, + 34858: { + 'name': 'TimeZoneOffset', + 'type': 'Long' + }, + 34859: { + 'name': 'SelfTimerMode', + 'type': 'Short' + }, + 37387: { + 'name': 'FlashEnergy', + 'type': 'Rational' + }, + 37388: { + 'name': 'SpatialFrequencyResponse', + 'type': 'Undefined' + }, + 37389: { + 'name': 'Noise', + 'type': 'Undefined' + }, + 37390: { + 'name': 'FocalPlaneXResolution', + 'type': 'Rational' + }, + 37391: { + 'name': 'FocalPlaneYResolution', + 'type': 'Rational' + }, + 37392: { + 'name': 'FocalPlaneResolutionUnit', + 'type': 'Short' + }, + 37393: { + 'name': 'ImageNumber', + 'type': 'Long' + }, + 37394: { + 'name': 'SecurityClassification', + 'type': 'Ascii' + }, + 37395: { + 'name': 'ImageHistory', + 'type': 'Ascii' + }, + 37397: { + 'name': 'ExposureIndex', + 'type': 'Rational' + }, + 37398: { + 'name': 'TIFFEPStandardID', + 'type': 'Byte' + }, + 37399: { + 'name': 'SensingMethod', + 'type': 'Short' + }, + 40091: { + 'name': 'XPTitle', + 'type': 'Byte' + }, + 40092: { + 'name': 'XPComment', + 'type': 'Byte' + }, + 40093: { + 'name': 'XPAuthor', + 'type': 'Byte' + }, + 40094: { + 'name': 'XPKeywords', + 'type': 'Byte' + }, + 40095: { + 'name': 'XPSubject', + 'type': 'Byte' + }, + 50341: { + 'name': 'PrintImageMatching', + 'type': 'Undefined' + }, + 50706: { + 'name': 'DNGVersion', + 'type': 'Byte' + }, + 50707: { + 'name': 'DNGBackwardVersion', + 'type': 'Byte' + }, + 50708: { + 'name': 'UniqueCameraModel', + 'type': 'Ascii' + }, + 50709: { + 'name': 'LocalizedCameraModel', + 'type': 'Byte' + }, + 50710: { + 'name': 'CFAPlaneColor', + 'type': 'Byte' + }, + 50711: { + 'name': 'CFALayout', + 'type': 'Short' + }, + 50712: { + 'name': 'LinearizationTable', + 'type': 'Short' + }, + 50713: { + 'name': 'BlackLevelRepeatDim', + 'type': 'Short' + }, + 50714: { + 'name': 'BlackLevel', + 'type': 'Rational' + }, + 50715: { + 'name': 'BlackLevelDeltaH', + 'type': 'SRational' + }, + 50716: { + 'name': 'BlackLevelDeltaV', + 'type': 'SRational' + }, + 50717: { + 'name': 'WhiteLevel', + 'type': 'Short' + }, + 50718: { + 'name': 'DefaultScale', + 'type': 'Rational' + }, + 50719: { + 'name': 'DefaultCropOrigin', + 'type': 'Short' + }, + 50720: { + 'name': 'DefaultCropSize', + 'type': 'Short' + }, + 50721: { + 'name': 'ColorMatrix1', + 'type': 'SRational' + }, + 50722: { + 'name': 'ColorMatrix2', + 'type': 'SRational' + }, + 50723: { + 'name': 'CameraCalibration1', + 'type': 'SRational' + }, + 50724: { + 'name': 'CameraCalibration2', + 'type': 'SRational' + }, + 50725: { + 'name': 'ReductionMatrix1', + 'type': 'SRational' + }, + 50726: { + 'name': 'ReductionMatrix2', + 'type': 'SRational' + }, + 50727: { + 'name': 'AnalogBalance', + 'type': 'Rational' + }, + 50728: { + 'name': 'AsShotNeutral', + 'type': 'Short' + }, + 50729: { + 'name': 'AsShotWhiteXY', + 'type': 'Rational' + }, + 50730: { + 'name': 'BaselineExposure', + 'type': 'SRational' + }, + 50731: { + 'name': 'BaselineNoise', + 'type': 'Rational' + }, + 50732: { + 'name': 'BaselineSharpness', + 'type': 'Rational' + }, + 50733: { + 'name': 'BayerGreenSplit', + 'type': 'Long' + }, + 50734: { + 'name': 'LinearResponseLimit', + 'type': 'Rational' + }, + 50735: { + 'name': 'CameraSerialNumber', + 'type': 'Ascii' + }, + 50736: { + 'name': 'LensInfo', + 'type': 'Rational' + }, + 50737: { + 'name': 'ChromaBlurRadius', + 'type': 'Rational' + }, + 50738: { + 'name': 'AntiAliasStrength', + 'type': 'Rational' + }, + 50739: { + 'name': 'ShadowScale', + 'type': 'SRational' + }, + 50740: { + 'name': 'DNGPrivateData', + 'type': 'Byte' + }, + 50741: { + 'name': 'MakerNoteSafety', + 'type': 'Short' + }, + 50778: { + 'name': 'CalibrationIlluminant1', + 'type': 'Short' + }, + 50779: { + 'name': 'CalibrationIlluminant2', + 'type': 'Short' + }, + 50780: { + 'name': 'BestQualityScale', + 'type': 'Rational' + }, + 50781: { + 'name': 'RawDataUniqueID', + 'type': 'Byte' + }, + 50827: { + 'name': 'OriginalRawFileName', + 'type': 'Byte' + }, + 50828: { + 'name': 'OriginalRawFileData', + 'type': 'Undefined' + }, + 50829: { + 'name': 'ActiveArea', + 'type': 'Short' + }, + 50830: { + 'name': 'MaskedAreas', + 'type': 'Short' + }, + 50831: { + 'name': 'AsShotICCProfile', + 'type': 'Undefined' + }, + 50832: { + 'name': 'AsShotPreProfileMatrix', + 'type': 'SRational' + }, + 50833: { + 'name': 'CurrentICCProfile', + 'type': 'Undefined' + }, + 50834: { + 'name': 'CurrentPreProfileMatrix', + 'type': 'SRational' + }, + 50879: { + 'name': 'ColorimetricReference', + 'type': 'Short' + }, + 50931: { + 'name': 'CameraCalibrationSignature', + 'type': 'Byte' + }, + 50932: { + 'name': 'ProfileCalibrationSignature', + 'type': 'Byte' + }, + 50934: { + 'name': 'AsShotProfileName', + 'type': 'Byte' + }, + 50935: { + 'name': 'NoiseReductionApplied', + 'type': 'Rational' + }, + 50936: { + 'name': 'ProfileName', + 'type': 'Byte' + }, + 50937: { + 'name': 'ProfileHueSatMapDims', + 'type': 'Long' + }, + 50938: { + 'name': 'ProfileHueSatMapData1', + 'type': 'Float' + }, + 50939: { + 'name': 'ProfileHueSatMapData2', + 'type': 'Float' + }, + 50940: { + 'name': 'ProfileToneCurve', + 'type': 'Float' + }, + 50941: { + 'name': 'ProfileEmbedPolicy', + 'type': 'Long' + }, + 50942: { + 'name': 'ProfileCopyright', + 'type': 'Byte' + }, + 50964: { + 'name': 'ForwardMatrix1', + 'type': 'SRational' + }, + 50965: { + 'name': 'ForwardMatrix2', + 'type': 'SRational' + }, + 50966: { + 'name': 'PreviewApplicationName', + 'type': 'Byte' + }, + 50967: { + 'name': 'PreviewApplicationVersion', + 'type': 'Byte' + }, + 50968: { + 'name': 'PreviewSettingsName', + 'type': 'Byte' + }, + 50969: { + 'name': 'PreviewSettingsDigest', + 'type': 'Byte' + }, + 50970: { + 'name': 'PreviewColorSpace', + 'type': 'Long' + }, + 50971: { + 'name': 'PreviewDateTime', + 'type': 'Ascii' + }, + 50972: { + 'name': 'RawImageDigest', + 'type': 'Undefined' + }, + 50973: { + 'name': 'OriginalRawFileDigest', + 'type': 'Undefined' + }, + 50974: { + 'name': 'SubTileBlockSize', + 'type': 'Long' + }, + 50975: { + 'name': 'RowInterleaveFactor', + 'type': 'Long' + }, + 50981: { + 'name': 'ProfileLookTableDims', + 'type': 'Long' + }, + 50982: { + 'name': 'ProfileLookTableData', + 'type': 'Float' + }, + 51008: { + 'name': 'OpcodeList1', + 'type': 'Undefined' + }, + 51009: { + 'name': 'OpcodeList2', + 'type': 'Undefined' + }, + 51022: { + 'name': 'OpcodeList3', + 'type': 'Undefined' + } + }, + 'Exif': { + 33434: { + 'name': 'ExposureTime', + 'type': 'Rational' + }, + 33437: { + 'name': 'FNumber', + 'type': 'Rational' + }, + 34850: { + 'name': 'ExposureProgram', + 'type': 'Short' + }, + 34852: { + 'name': 'SpectralSensitivity', + 'type': 'Ascii' + }, + 34855: { + 'name': 'ISOSpeedRatings', + 'type': 'Short' + }, + 34856: { + 'name': 'OECF', + 'type': 'Undefined' + }, + 34864: { + 'name': 'SensitivityType', + 'type': 'Short' + }, + 34865: { + 'name': 'StandardOutputSensitivity', + 'type': 'Long' + }, + 34866: { + 'name': 'RecommendedExposureIndex', + 'type': 'Long' + }, + 34867: { + 'name': 'ISOSpeed', + 'type': 'Long' + }, + 34868: { + 'name': 'ISOSpeedLatitudeyyy', + 'type': 'Long' + }, + 34869: { + 'name': 'ISOSpeedLatitudezzz', + 'type': 'Long' + }, + 36864: { + 'name': 'ExifVersion', + 'type': 'Undefined' + }, + 36867: { + 'name': 'DateTimeOriginal', + 'type': 'Ascii' + }, + 36868: { + 'name': 'DateTimeDigitized', + 'type': 'Ascii' + }, + 37121: { + 'name': 'ComponentsConfiguration', + 'type': 'Undefined' + }, + 37122: { + 'name': 'CompressedBitsPerPixel', + 'type': 'Rational' + }, + 37377: { + 'name': 'ShutterSpeedValue', + 'type': 'SRational' + }, + 37378: { + 'name': 'ApertureValue', + 'type': 'Rational' + }, + 37379: { + 'name': 'BrightnessValue', + 'type': 'SRational' + }, + 37380: { + 'name': 'ExposureBiasValue', + 'type': 'SRational' + }, + 37381: { + 'name': 'MaxApertureValue', + 'type': 'Rational' + }, + 37382: { + 'name': 'SubjectDistance', + 'type': 'Rational' + }, + 37383: { + 'name': 'MeteringMode', + 'type': 'Short' + }, + 37384: { + 'name': 'LightSource', + 'type': 'Short' + }, + 37385: { + 'name': 'Flash', + 'type': 'Short' + }, + 37386: { + 'name': 'FocalLength', + 'type': 'Rational' + }, + 37396: { + 'name': 'SubjectArea', + 'type': 'Short' + }, + 37500: { + 'name': 'MakerNote', + 'type': 'Undefined' + }, + 37510: { + 'name': 'UserComment', + 'type': 'Ascii' + }, + 37520: { + 'name': 'SubSecTime', + 'type': 'Ascii' + }, + 37521: { + 'name': 'SubSecTimeOriginal', + 'type': 'Ascii' + }, + 37522: { + 'name': 'SubSecTimeDigitized', + 'type': 'Ascii' + }, + 40960: { + 'name': 'FlashpixVersion', + 'type': 'Undefined' + }, + 40961: { + 'name': 'ColorSpace', + 'type': 'Short' + }, + 40962: { + 'name': 'PixelXDimension', + 'type': 'Long' + }, + 40963: { + 'name': 'PixelYDimension', + 'type': 'Long' + }, + 40964: { + 'name': 'RelatedSoundFile', + 'type': 'Ascii' + }, + 40965: { + 'name': 'InteroperabilityTag', + 'type': 'Long' + }, + 41483: { + 'name': 'FlashEnergy', + 'type': 'Rational' + }, + 41484: { + 'name': 'SpatialFrequencyResponse', + 'type': 'Undefined' + }, + 41486: { + 'name': 'FocalPlaneXResolution', + 'type': 'Rational' + }, + 41487: { + 'name': 'FocalPlaneYResolution', + 'type': 'Rational' + }, + 41488: { + 'name': 'FocalPlaneResolutionUnit', + 'type': 'Short' + }, + 41492: { + 'name': 'SubjectLocation', + 'type': 'Short' + }, + 41493: { + 'name': 'ExposureIndex', + 'type': 'Rational' + }, + 41495: { + 'name': 'SensingMethod', + 'type': 'Short' + }, + 41728: { + 'name': 'FileSource', + 'type': 'Undefined' + }, + 41729: { + 'name': 'SceneType', + 'type': 'Undefined' + }, + 41730: { + 'name': 'CFAPattern', + 'type': 'Undefined' + }, + 41985: { + 'name': 'CustomRendered', + 'type': 'Short' + }, + 41986: { + 'name': 'ExposureMode', + 'type': 'Short' + }, + 41987: { + 'name': 'WhiteBalance', + 'type': 'Short' + }, + 41988: { + 'name': 'DigitalZoomRatio', + 'type': 'Rational' + }, + 41989: { + 'name': 'FocalLengthIn35mmFilm', + 'type': 'Short' + }, + 41990: { + 'name': 'SceneCaptureType', + 'type': 'Short' + }, + 41991: { + 'name': 'GainControl', + 'type': 'Short' + }, + 41992: { + 'name': 'Contrast', + 'type': 'Short' + }, + 41993: { + 'name': 'Saturation', + 'type': 'Short' + }, + 41994: { + 'name': 'Sharpness', + 'type': 'Short' + }, + 41995: { + 'name': 'DeviceSettingDescription', + 'type': 'Undefined' + }, + 41996: { + 'name': 'SubjectDistanceRange', + 'type': 'Short' + }, + 42016: { + 'name': 'ImageUniqueID', + 'type': 'Ascii' + }, + 42032: { + 'name': 'CameraOwnerName', + 'type': 'Ascii' + }, + 42033: { + 'name': 'BodySerialNumber', + 'type': 'Ascii' + }, + 42034: { + 'name': 'LensSpecification', + 'type': 'Rational' + }, + 42035: { + 'name': 'LensMake', + 'type': 'Ascii' + }, + 42036: { + 'name': 'LensModel', + 'type': 'Ascii' + }, + 42037: { + 'name': 'LensSerialNumber', + 'type': 'Ascii' + }, + 42240: { + 'name': 'Gamma', + 'type': 'Rational' + } + }, + 'GPS': { + 0: { + 'name': 'GPSVersionID', + 'type': 'Byte' + }, + 1: { + 'name': 'GPSLatitudeRef', + 'type': 'Ascii' + }, + 2: { + 'name': 'GPSLatitude', + 'type': 'Rational' + }, + 3: { + 'name': 'GPSLongitudeRef', + 'type': 'Ascii' + }, + 4: { + 'name': 'GPSLongitude', + 'type': 'Rational' + }, + 5: { + 'name': 'GPSAltitudeRef', + 'type': 'Byte' + }, + 6: { + 'name': 'GPSAltitude', + 'type': 'Rational' + }, + 7: { + 'name': 'GPSTimeStamp', + 'type': 'Rational' + }, + 8: { + 'name': 'GPSSatellites', + 'type': 'Ascii' + }, + 9: { + 'name': 'GPSStatus', + 'type': 'Ascii' + }, + 10: { + 'name': 'GPSMeasureMode', + 'type': 'Ascii' + }, + 11: { + 'name': 'GPSDOP', + 'type': 'Rational' + }, + 12: { + 'name': 'GPSSpeedRef', + 'type': 'Ascii' + }, + 13: { + 'name': 'GPSSpeed', + 'type': 'Rational' + }, + 14: { + 'name': 'GPSTrackRef', + 'type': 'Ascii' + }, + 15: { + 'name': 'GPSTrack', + 'type': 'Rational' + }, + 16: { + 'name': 'GPSImgDirectionRef', + 'type': 'Ascii' + }, + 17: { + 'name': 'GPSImgDirection', + 'type': 'Rational' + }, + 18: { + 'name': 'GPSMapDatum', + 'type': 'Ascii' + }, + 19: { + 'name': 'GPSDestLatitudeRef', + 'type': 'Ascii' + }, + 20: { + 'name': 'GPSDestLatitude', + 'type': 'Rational' + }, + 21: { + 'name': 'GPSDestLongitudeRef', + 'type': 'Ascii' + }, + 22: { + 'name': 'GPSDestLongitude', + 'type': 'Rational' + }, + 23: { + 'name': 'GPSDestBearingRef', + 'type': 'Ascii' + }, + 24: { + 'name': 'GPSDestBearing', + 'type': 'Rational' + }, + 25: { + 'name': 'GPSDestDistanceRef', + 'type': 'Ascii' + }, + 26: { + 'name': 'GPSDestDistance', + 'type': 'Rational' + }, + 27: { + 'name': 'GPSProcessingMethod', + 'type': 'Undefined' + }, + 28: { + 'name': 'GPSAreaInformation', + 'type': 'Undefined' + }, + 29: { + 'name': 'GPSDateStamp', + 'type': 'Ascii' + }, + 30: { + 'name': 'GPSDifferential', + 'type': 'Short' + }, + 31: { + 'name': 'GPSHPositioningError', + 'type': 'Rational' + } + }, + 'Interop': { + 1: { + 'name': 'InteroperabilityIndex', + 'type': 'Ascii' + } + }, +}; +const TYPES = { + "Byte": 1, + "Ascii": 2, + "Short": 3, + "Long": 4, + "Rational": 5, + "Undefined": 7, + "SLong": 9, + "SRational": 10 +}; +TAGS["0th"] = TAGS["Image"]; +TAGS["1st"] = TAGS["Image"]; +class Piexif { + version = "1.0.4" + TAGS = TAGS + ImageIFD = { + ProcessingSoftware: 11, + NewSubfileType: 254, + SubfileType: 255, + ImageWidth: 256, + ImageLength: 257, + BitsPerSample: 258, + Compression: 259, + PhotometricInterpretation: 262, + Threshholding: 263, + CellWidth: 264, + CellLength: 265, + FillOrder: 266, + DocumentName: 269, + ImageDescription: 270, + Make: 271, + Model: 272, + StripOffsets: 273, + Orientation: 274, + SamplesPerPixel: 277, + RowsPerStrip: 278, + StripByteCounts: 279, + XResolution: 282, + YResolution: 283, + PlanarConfiguration: 284, + GrayResponseUnit: 290, + GrayResponseCurve: 291, + T4Options: 292, + T6Options: 293, + ResolutionUnit: 296, + TransferFunction: 301, + Software: 305, + DateTime: 306, + Artist: 315, + HostComputer: 316, + Predictor: 317, + WhitePoint: 318, + PrimaryChromaticities: 319, + ColorMap: 320, + HalftoneHints: 321, + TileWidth: 322, + TileLength: 323, + TileOffsets: 324, + TileByteCounts: 325, + SubIFDs: 330, + InkSet: 332, + InkNames: 333, + NumberOfInks: 334, + DotRange: 336, + TargetPrinter: 337, + ExtraSamples: 338, + SampleFormat: 339, + SMinSampleValue: 340, + SMaxSampleValue: 341, + TransferRange: 342, + ClipPath: 343, + XClipPathUnits: 344, + YClipPathUnits: 345, + Indexed: 346, + JPEGTables: 347, + OPIProxy: 351, + JPEGProc: 512, + JPEGInterchangeFormat: 513, + JPEGInterchangeFormatLength: 514, + JPEGRestartInterval: 515, + JPEGLosslessPredictors: 517, + JPEGPointTransforms: 518, + JPEGQTables: 519, + JPEGDCTables: 520, + JPEGACTables: 521, + YCbCrCoefficients: 529, + YCbCrSubSampling: 530, + YCbCrPositioning: 531, + ReferenceBlackWhite: 532, + XMLPacket: 700, + Rating: 18246, + RatingPercent: 18249, + ImageID: 32781, + CFARepeatPatternDim: 33421, + CFAPattern: 33422, + BatteryLevel: 33423, + Copyright: 33432, + ExposureTime: 33434, + ImageResources: 34377, + ExifTag: 34665, + InterColorProfile: 34675, + GPSTag: 34853, + Interlace: 34857, + TimeZoneOffset: 34858, + SelfTimerMode: 34859, + FlashEnergy: 37387, + SpatialFrequencyResponse: 37388, + Noise: 37389, + FocalPlaneXResolution: 37390, + FocalPlaneYResolution: 37391, + FocalPlaneResolutionUnit: 37392, + ImageNumber: 37393, + SecurityClassification: 37394, + ImageHistory: 37395, + ExposureIndex: 37397, + TIFFEPStandardID: 37398, + SensingMethod: 37399, + XPTitle: 40091, + XPComment: 40092, + XPAuthor: 40093, + XPKeywords: 40094, + XPSubject: 40095, + PrintImageMatching: 50341, + DNGVersion: 50706, + DNGBackwardVersion: 50707, + UniqueCameraModel: 50708, + LocalizedCameraModel: 50709, + CFAPlaneColor: 50710, + CFALayout: 50711, + LinearizationTable: 50712, + BlackLevelRepeatDim: 50713, + BlackLevel: 50714, + BlackLevelDeltaH: 50715, + BlackLevelDeltaV: 50716, + WhiteLevel: 50717, + DefaultScale: 50718, + DefaultCropOrigin: 50719, + DefaultCropSize: 50720, + ColorMatrix1: 50721, + ColorMatrix2: 50722, + CameraCalibration1: 50723, + CameraCalibration2: 50724, + ReductionMatrix1: 50725, + ReductionMatrix2: 50726, + AnalogBalance: 50727, + AsShotNeutral: 50728, + AsShotWhiteXY: 50729, + BaselineExposure: 50730, + BaselineNoise: 50731, + BaselineSharpness: 50732, + BayerGreenSplit: 50733, + LinearResponseLimit: 50734, + CameraSerialNumber: 50735, + LensInfo: 50736, + ChromaBlurRadius: 50737, + AntiAliasStrength: 50738, + ShadowScale: 50739, + DNGPrivateData: 50740, + MakerNoteSafety: 50741, + CalibrationIlluminant1: 50778, + CalibrationIlluminant2: 50779, + BestQualityScale: 50780, + RawDataUniqueID: 50781, + OriginalRawFileName: 50827, + OriginalRawFileData: 50828, + ActiveArea: 50829, + MaskedAreas: 50830, + AsShotICCProfile: 50831, + AsShotPreProfileMatrix: 50832, + CurrentICCProfile: 50833, + CurrentPreProfileMatrix: 50834, + ColorimetricReference: 50879, + CameraCalibrationSignature: 50931, + ProfileCalibrationSignature: 50932, + AsShotProfileName: 50934, + NoiseReductionApplied: 50935, + ProfileName: 50936, + ProfileHueSatMapDims: 50937, + ProfileHueSatMapData1: 50938, + ProfileHueSatMapData2: 50939, + ProfileToneCurve: 50940, + ProfileEmbedPolicy: 50941, + ProfileCopyright: 50942, + ForwardMatrix1: 50964, + ForwardMatrix2: 50965, + PreviewApplicationName: 50966, + PreviewApplicationVersion: 50967, + PreviewSettingsName: 50968, + PreviewSettingsDigest: 50969, + PreviewColorSpace: 50970, + PreviewDateTime: 50971, + RawImageDigest: 50972, + OriginalRawFileDigest: 50973, + SubTileBlockSize: 50974, + RowInterleaveFactor: 50975, + ProfileLookTableDims: 50981, + ProfileLookTableData: 50982, + OpcodeList1: 51008, + OpcodeList2: 51009, + OpcodeList3: 51022, + NoiseProfile: 51041, + } + ExifIFD = { + ExposureTime: 33434, + FNumber: 33437, + ExposureProgram: 34850, + SpectralSensitivity: 34852, + ISOSpeedRatings: 34855, + OECF: 34856, + SensitivityType: 34864, + StandardOutputSensitivity: 34865, + RecommendedExposureIndex: 34866, + ISOSpeed: 34867, + ISOSpeedLatitudeyyy: 34868, + ISOSpeedLatitudezzz: 34869, + ExifVersion: 36864, + DateTimeOriginal: 36867, + DateTimeDigitized: 36868, + ComponentsConfiguration: 37121, + CompressedBitsPerPixel: 37122, + ShutterSpeedValue: 37377, + ApertureValue: 37378, + BrightnessValue: 37379, + ExposureBiasValue: 37380, + MaxApertureValue: 37381, + SubjectDistance: 37382, + MeteringMode: 37383, + LightSource: 37384, + Flash: 37385, + FocalLength: 37386, + SubjectArea: 37396, + MakerNote: 37500, + UserComment: 37510, + SubSecTime: 37520, + SubSecTimeOriginal: 37521, + SubSecTimeDigitized: 37522, + FlashpixVersion: 40960, + ColorSpace: 40961, + PixelXDimension: 40962, + PixelYDimension: 40963, + RelatedSoundFile: 40964, + InteroperabilityTag: 40965, + FlashEnergy: 41483, + SpatialFrequencyResponse: 41484, + FocalPlaneXResolution: 41486, + FocalPlaneYResolution: 41487, + FocalPlaneResolutionUnit: 41488, + SubjectLocation: 41492, + ExposureIndex: 41493, + SensingMethod: 41495, + FileSource: 41728, + SceneType: 41729, + CFAPattern: 41730, + CustomRendered: 41985, + ExposureMode: 41986, + WhiteBalance: 41987, + DigitalZoomRatio: 41988, + FocalLengthIn35mmFilm: 41989, + SceneCaptureType: 41990, + GainControl: 41991, + Contrast: 41992, + Saturation: 41993, + Sharpness: 41994, + DeviceSettingDescription: 41995, + SubjectDistanceRange: 41996, + ImageUniqueID: 42016, + CameraOwnerName: 42032, + BodySerialNumber: 42033, + LensSpecification: 42034, + LensMake: 42035, + LensModel: 42036, + LensSerialNumber: 42037, + Gamma: 42240, + } + GPSIFD = { + GPSVersionID: 0, + GPSLatitudeRef: 1, + GPSLatitude: 2, + GPSLongitudeRef: 3, + GPSLongitude: 4, + GPSAltitudeRef: 5, + GPSAltitude: 6, + GPSTimeStamp: 7, + GPSSatellites: 8, + GPSStatus: 9, + GPSMeasureMode: 10, + GPSDOP: 11, + GPSSpeedRef: 12, + GPSSpeed: 13, + GPSTrackRef: 14, + GPSTrack: 15, + GPSImgDirectionRef: 16, + GPSImgDirection: 17, + GPSMapDatum: 18, + GPSDestLatitudeRef: 19, + GPSDestLatitude: 20, + GPSDestLongitudeRef: 21, + GPSDestLongitude: 22, + GPSDestBearingRef: 23, + GPSDestBearing: 24, + GPSDestDistanceRef: 25, + GPSDestDistance: 26, + GPSProcessingMethod: 27, + GPSAreaInformation: 28, + GPSDateStamp: 29, + GPSDifferential: 30, + GPSHPositioningError: 31, + } + InteropIFD = { + InteroperabilityIndex: 1, + } + GPSHelper = { + degToDmsRational(degFloat : number) { + const degAbs = Math.abs(degFloat); + const minFloat = degAbs % 1 * 60; + const secFloat = minFloat % 1 * 60; + const deg = Math.floor(degAbs); + const min = Math.floor(minFloat); + const sec = Math.round(secFloat * 100); + + return [ + [deg, 1], + [min, 1], + [sec, 100] + ]; + }, + dmsRationalToDeg(dmsArray : number[][], ref : string) { + const sign = (ref === 'S' || ref === 'W') ? -1.0 : 1.0; + const deg = dmsArray[0][0] / dmsArray[0][1] + + dmsArray[1][0] / dmsArray[1][1] / 60.0 + + dmsArray[2][0] / dmsArray[2][1] / 3600.0; + + return deg * sign; + }, + } + remove(jpeg) { + let b64 = false; + if (jpeg.slice(0, 2) == "\xff\xd8") { } else if (jpeg.slice(0, 23) == "data:image/jpeg;base64," || jpeg + .slice(0, 22) == "data:image/jpg;base64,") { + jpeg = atob(jpeg.split(",")[1]); + b64 = true; + } else { + throw new Error("Given data is not jpeg."); + } + + const segments = splitIntoSegments(jpeg); + const newSegments = segments.filter(function (seg) { + return !(seg.slice(0, 2) == "\xff\xe1" && + seg.slice(4, 10) == "Exif\x00\x00"); + }); + + let new_data = newSegments.join(""); + if (b64) { + new_data = "data:image/jpeg;base64," + btoa(new_data); + } + + return new_data; + } + insert(exif, jpeg) { + let b64 = false; + if (exif.slice(0, 6) != "\x45\x78\x69\x66\x00\x00") { + throw new Error("Given data is not exif."); + } + if (jpeg.slice(0, 2) == "\xff\xd8") { } else if (jpeg.slice(0, 23) == "data:image/jpeg;base64," || jpeg + .slice(0, 22) == "data:image/jpg;base64,") { + jpeg = atob(jpeg.split(",")[1]); + b64 = true; + } else { + throw new Error("Given data is not jpeg."); + } + + const exifStr = "\xff\xe1" + pack(">H", [exif.length + 2]) + exif; + const segments = splitIntoSegments(jpeg); + let new_data = mergeSegments(segments, exifStr); + if (b64) { + new_data = "data:image/jpeg;base64," + btoa(new_data); + } + + return new_data; + } + load(data) { + let input_data; + if (isString(data)) { + if (data.slice(0, 2) == "\xff\xd8") { + input_data = data; + } else if (data.slice(0, 23) == "data:image/jpeg;base64," || data.slice(0, 22) == "data:image/jpg;base64,") { + input_data = atob(data.split(",")[1]); + } else if (data.slice(0, 4) == "Exif") { + input_data = data.slice(6); + } else { + throw new Error("'load' gots invalid file data."); + } + } else { + throw new Error("'load' gots invalid type argument."); + } + + let exifDict = {}; + let exif_dict = { + "0th": {}, + "Exif": {}, + "GPS": {}, + "Interop": {}, + "1st": {}, + "thumbnail": null + }; + const exifReader = new ExifReader(input_data); + if (exifReader.tiftag === null) { + return exif_dict; + } + + if (exifReader.tiftag.slice(0, 2) == "\x49\x49") { + exifReader.endian_mark = "<"; + } else { + exifReader.endian_mark = ">"; + } + + let pointer = unpack(exifReader.endian_mark + "L", + exifReader.tiftag.slice(4, 8))[0]; + exif_dict["0th"] = exifReader.get_ifd(pointer, "0th"); + + const first_ifd_pointer = exif_dict["0th"]["first_ifd_pointer"]; + delete exif_dict["0th"]["first_ifd_pointer"]; + + if (34665 in exif_dict["0th"]) { + pointer = exif_dict["0th"][34665]; + exif_dict["Exif"] = exifReader.get_ifd(pointer, "Exif"); + } + if (34853 in exif_dict["0th"]) { + pointer = exif_dict["0th"][34853]; + exif_dict["GPS"] = exifReader.get_ifd(pointer, "GPS"); + } + if (40965 in exif_dict["Exif"]) { + pointer = exif_dict["Exif"][40965]; + exif_dict["Interop"] = exifReader.get_ifd(pointer, "Interop"); + } + if (first_ifd_pointer != "\x00\x00\x00\x00") { + pointer = unpack(exifReader.endian_mark + "L", + first_ifd_pointer)[0]; + exif_dict["1st"] = exifReader.get_ifd(pointer, "1st"); + if ((513 in exif_dict["1st"]) && (514 in exif_dict["1st"])) { + var end = exif_dict["1st"][513] + exif_dict["1st"][514]; + var thumb = exifReader.tiftag.slice(exif_dict["1st"][513], end); + exif_dict["thumbnail"] = thumb; + } + } + + return exif_dict; + } + dump(exif_dict_original) { + const TIFF_HEADER_LENGTH = 8; + + const exif_dict : any = cloneDeep(exif_dict_original); + const header = "Exif\x00\x00\x4d\x4d\x00\x2a\x00\x00\x00\x08"; + let exif_is = 0//false; + let gps_is = 0 //false; + let interop_is = 0//false; + let first_is = false; + + let zeroth_ifd, + exif_ifd, + interop_ifd, + gps_ifd, + first_ifd; + + if ("0th" in exif_dict) { + zeroth_ifd = exif_dict["0th"]; + } else { + zeroth_ifd = {}; + } + + if ((("Exif" in exif_dict) && (Object.keys(exif_dict["Exif"]).length)) || + (("Interop" in exif_dict) && (Object.keys(exif_dict["Interop"]).length))) { + zeroth_ifd[34665] = 1; + exif_is = 1//true; + exif_ifd = exif_dict["Exif"]; + if (("Interop" in exif_dict) && Object.keys(exif_dict["Interop"]).length) { + exif_ifd[40965] = 1; + interop_is = 1//true; + interop_ifd = exif_dict["Interop"]; + } else if (Object.keys(exif_ifd).indexOf(this.ExifIFD.InteroperabilityTag.toString()) > -1) { + delete exif_ifd[40965]; + } + } else if (Object.keys(zeroth_ifd).indexOf(this.ImageIFD.ExifTag.toString()) > -1) { + delete zeroth_ifd[34665]; + } + + if (("GPS" in exif_dict) && (Object.keys(exif_dict["GPS"]).length)) { + zeroth_ifd[this.ImageIFD.GPSTag] = 1; + gps_is = 1 //true; + gps_ifd = exif_dict["GPS"]; + } else if (Object.keys(zeroth_ifd).indexOf(this.ImageIFD.GPSTag.toString()) > -1) { + delete zeroth_ifd[this.ImageIFD.GPSTag]; + } + + if (("1st" in exif_dict) && + ("thumbnail" in exif_dict) && + (exif_dict["thumbnail"] != null)) { + first_is = true; + exif_dict["1st"][513] = 1; + exif_dict["1st"][514] = 1; + first_ifd = exif_dict["1st"]; + } + + const zeroth_set = _dict_to_bytes(zeroth_ifd, "0th", 0); + const zeroth_length = (zeroth_set[0].length + exif_is * 12 + gps_is * 12 + 4 + + zeroth_set[1].length); + + let exif_set, + exif_bytes = "", + exif_length = 0, + gps_set, + gps_bytes = "", + gps_length = 0, + interop_set, + interop_bytes = "", + interop_length = 0, + first_set, + first_bytes = "", + thumbnail; + if (exif_is) { + exif_set = _dict_to_bytes(exif_ifd, "Exif", zeroth_length); + exif_length = exif_set[0].length + interop_is * 12 + exif_set[1].length; + } + if (gps_is) { + gps_set = _dict_to_bytes(gps_ifd, "GPS", zeroth_length + exif_length); + gps_bytes = gps_set.join(""); + gps_length = gps_bytes.length; + } + if (interop_is) { + const offset = zeroth_length + exif_length + gps_length; + interop_set = _dict_to_bytes(interop_ifd, "Interop", offset); + interop_bytes = interop_set.join(""); + interop_length = interop_bytes.length; + } + if (first_is) { + const offset = zeroth_length + exif_length + gps_length + interop_length; + first_set = _dict_to_bytes(first_ifd, "1st", offset); + thumbnail = _get_thumbnail(exif_dict["thumbnail"]); + if (thumbnail.length > 64000) { + throw new Error("Given thumbnail is too large. max 64kB"); + } + } + + let exif_pointer = "", + gps_pointer = "", + interop_pointer = "", + first_ifd_pointer = "\x00\x00\x00\x00"; + if (exif_is) { + const pointer_value = TIFF_HEADER_LENGTH + zeroth_length; + const pointer_str = pack(">L", [pointer_value]); + const key = 34665; + const key_str = pack(">H", [key]); + const type_str = pack(">H", [TYPES["Long"]]); + const length_str = pack(">L", [1]); + exif_pointer = key_str + type_str + length_str + pointer_str; + } + if (gps_is) { + const pointer_value = TIFF_HEADER_LENGTH + zeroth_length + exif_length; + const pointer_str = pack(">L", [pointer_value]); + const key = 34853; + const key_str = pack(">H", [key]); + const type_str = pack(">H", [TYPES["Long"]]); + const length_str = pack(">L", [1]); + gps_pointer = key_str + type_str + length_str + pointer_str; + } + if (interop_is) { + const pointer_value = (TIFF_HEADER_LENGTH + + zeroth_length + exif_length + gps_length); + const pointer_str = pack(">L", [pointer_value]); + const key = 40965; + const key_str = pack(">H", [key]); + const type_str = pack(">H", [TYPES["Long"]]); + const length_str = pack(">L", [1]); + interop_pointer = key_str + type_str + length_str + pointer_str; + } + if (first_is) { + const pointer_value = (TIFF_HEADER_LENGTH + zeroth_length + + exif_length + gps_length + interop_length); + first_ifd_pointer = pack(">L", [pointer_value]); + const thumbnail_pointer = (pointer_value + first_set[0].length + 24 + + 4 + first_set[1].length); + const thumbnail_p_bytes = ("\x02\x01\x00\x04\x00\x00\x00\x01" + + pack(">L", [thumbnail_pointer])); + const thumbnail_length_bytes = ("\x02\x02\x00\x04\x00\x00\x00\x01" + + pack(">L", [thumbnail.length])); + first_bytes = (first_set[0] + thumbnail_p_bytes + + thumbnail_length_bytes + "\x00\x00\x00\x00" + + first_set[1] + thumbnail); + } + + const zeroth_bytes = (zeroth_set[0] + exif_pointer + gps_pointer + + first_ifd_pointer + zeroth_set[1]); + if (exif_is) { + exif_bytes = exif_set[0] + interop_pointer + exif_set[1]; + } + + return (header + zeroth_bytes + exif_bytes + gps_bytes + + interop_bytes + first_bytes); + } +} + + + +function _get_thumbnail(jpeg) { + let segments = splitIntoSegments(jpeg); + while (("\xff\xe0" <= segments[1].slice(0, 2)) && (segments[1].slice(0, 2) <= "\xff\xef")) { + segments = [segments[0]].concat(segments.slice(2)); + } + return segments.join(""); +} +function _pack_byte(array) { + return pack(">" + nStr("B", array.length), array); +} + +function _pack_short(array) { + return pack(">" + nStr("H", array.length), array); +} + +function _pack_long(array) { + return pack(">" + nStr("L", array.length), array); +} + +function _value_to_bytes(raw_value, value_type, offset) { + let four_bytes_over = ""; + let value_str = ""; + let length, + new_value, + num, + den; + + if (value_type == "Byte") { + length = raw_value.length; + if (length <= 4) { + value_str = (_pack_byte(raw_value) + + nStr("\x00", 4 - length)); + } else { + value_str = pack(">L", [offset]); + four_bytes_over = _pack_byte(raw_value); + } + } else if (value_type == "Short") { + length = raw_value.length; + if (length <= 2) { + value_str = (_pack_short(raw_value) + + nStr("\x00\x00", 2 - length)); + } else { + value_str = pack(">L", [offset]); + four_bytes_over = _pack_short(raw_value); + } + } else if (value_type == "Long") { + length = raw_value.length; + if (length <= 1) { + value_str = _pack_long(raw_value); + } else { + value_str = pack(">L", [offset]); + four_bytes_over = _pack_long(raw_value); + } + } else if (value_type == "Ascii") { + new_value = raw_value + "\x00"; + length = new_value.length; + if (length > 4) { + value_str = pack(">L", [offset]); + four_bytes_over = new_value; + } else { + value_str = new_value + nStr("\x00", 4 - length); + } + } else if (value_type == "Rational") { + if (typeof (raw_value[0]) == "number") { + length = 1; + num = raw_value[0]; + den = raw_value[1]; + new_value = pack(">L", [num]) + pack(">L", [den]); + } else { + length = raw_value.length; + new_value = ""; + for (var n = 0; n < length; n++) { + num = raw_value[n][0]; + den = raw_value[n][1]; + new_value += (pack(">L", [num]) + + pack(">L", [den])); + } + } + value_str = pack(">L", [offset]); + four_bytes_over = new_value; + } else if (value_type == "SRational") { + if (typeof (raw_value[0]) == "number") { + length = 1; + num = raw_value[0]; + den = raw_value[1]; + new_value = pack(">l", [num]) + pack(">l", [den]); + } else { + length = raw_value.length; + new_value = ""; + for (var n = 0; n < length; n++) { + num = raw_value[n][0]; + den = raw_value[n][1]; + new_value += (pack(">l", [num]) + + pack(">l", [den])); + } + } + value_str = pack(">L", [offset]); + four_bytes_over = new_value; + } else if (value_type == "Undefined") { + length = raw_value.length; + if (length > 4) { + value_str = pack(">L", [offset]); + four_bytes_over = raw_value; + } else { + value_str = raw_value + nStr("\x00", 4 - length); + } + } + + var length_str = pack(">L", [length]); + + return [length_str, value_str, four_bytes_over]; +} + +function _dict_to_bytes(ifd_dict, ifd, ifd_offset) { + const TIFF_HEADER_LENGTH = 8; + const tag_count = Object.keys(ifd_dict).length; + const entry_header = pack(">H", [tag_count]); + let entries_length; + if (["0th", "1st"].indexOf(ifd) > -1) { + entries_length = 2 + tag_count * 12 + 4; + } else { + entries_length = 2 + tag_count * 12; + } + let entries = ""; + let values = ""; + let key; + + for (key in ifd_dict) { + if (typeof (key) == "string") { + key = parseInt(key); + } + if ((ifd == "0th") && ([34665, 34853].indexOf(key) > -1)) { + continue; + } else if ((ifd == "Exif") && (key == 40965)) { + continue; + } else if ((ifd == "1st") && ([513, 514].indexOf(key) > -1)) { + continue; + } + + var raw_value = ifd_dict[key]; + var key_str = pack(">H", [key]); + var value_type = TAGS[ifd][key]["type"]; + var type_str = pack(">H", [TYPES[value_type]]); + + if (typeof (raw_value) == "number") { + raw_value = [raw_value]; + } + var offset = TIFF_HEADER_LENGTH + entries_length + ifd_offset + values.length; + var b = _value_to_bytes(raw_value, value_type, offset); + var length_str = b[0]; + var value_str = b[1]; + var four_bytes_over = b[2]; + + entries += key_str + type_str + length_str + value_str; + values += four_bytes_over; + } + + return [entry_header + entries, values]; +} + + + +class ExifReader { + tiftag = null + endian_mark = '' + constructor(data) { + let segments, + app1; + if (data.slice(0, 2) == "\xff\xd8") { // JPEG + segments = splitIntoSegments(data); + app1 = getExifSeg(segments); + if (app1) { + this.tiftag = app1.slice(10); + } + } else if (["\x49\x49", "\x4d\x4d"].indexOf(data.slice(0, 2)) > -1) { // TIFF + this.tiftag = data; + } else if (data.slice(0, 4) == "Exif") { // Exif + this.tiftag = data.slice(6); + } else { + throw new Error("Given file is neither JPEG nor TIFF."); + } + } + get_ifd(pointer, ifd_name) { + let ifd_dict = {}; + let tag_count = unpack(this.endian_mark + "H", + this.tiftag.slice(pointer, pointer + 2))[0]; + const offset = pointer + 2; + let t; + if (["0th", "1st"].indexOf(ifd_name) > -1) { + t = "Image"; + } else { + t = ifd_name; + } + + for (let x = 0; x < tag_count; x++) { + pointer = offset + 12 * x; + const tag = unpack(this.endian_mark + "H", + this.tiftag.slice(pointer, pointer + 2))[0]; + const value_type = unpack(this.endian_mark + "H", + this.tiftag.slice(pointer + 2, pointer + 4))[0]; + const value_num = unpack(this.endian_mark + "L", + this.tiftag.slice(pointer + 4, pointer + 8))[0]; + const value = this.tiftag.slice(pointer + 8, pointer + 12); + + const v_set = [value_type, value_num, value]; + if (tag in TAGS[t]) { + ifd_dict[tag] = this.convert_value(v_set); + } + } + + if (ifd_name == "0th") { + pointer = offset + 12 * tag_count; + ifd_dict["first_ifd_pointer"] = this.tiftag.slice(pointer, pointer + 4); + } + + return ifd_dict; + } + + convert_value(val) { + let data = null; + const t = val[0]; + const length = val[1]; + const value = val[2]; + let pointer; + + if (t == 1) { // BYTE + if (length > 4) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = unpack(this.endian_mark + nStr("B", length), + this.tiftag.slice(pointer, pointer + length)); + } else { + data = unpack(this.endian_mark + nStr("B", length), value.slice(0, length)); + } + } else if (t == 2) { // ASCII + if (length > 4) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = this.tiftag.slice(pointer, pointer + length - 1); + } else { + data = value.slice(0, length - 1); + } + } else if (t == 3) { // SHORT + if (length > 2) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = unpack(this.endian_mark + nStr("H", length), + this.tiftag.slice(pointer, pointer + length * 2)); + } else { + data = unpack(this.endian_mark + nStr("H", length), + value.slice(0, length * 2)); + } + } else if (t == 4) { // LONG + if (length > 1) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = unpack(this.endian_mark + nStr("L", length), + this.tiftag.slice(pointer, pointer + length * 4)); + } else { + data = unpack(this.endian_mark + nStr("L", length), + value); + } + } else if (t == 5) { // RATIONAL + pointer = unpack(this.endian_mark + "L", value)[0]; + if (length > 1) { + data = []; + for (let x = 0; x < length; x++) { + data.push([unpack(this.endian_mark + "L", + this.tiftag.slice(pointer + x * 8, pointer + 4 + x * 8))[0], + unpack(this.endian_mark + "L", + this.tiftag.slice(pointer + 4 + x * 8, pointer + 8 + x * 8))[0] + ]); + } + } else { + data = [unpack(this.endian_mark + "L", + this.tiftag.slice(pointer, pointer + 4))[0], + unpack(this.endian_mark + "L", + this.tiftag.slice(pointer + 4, pointer + 8))[0] + ]; + } + } else if (t == 7) { // UNDEFINED BYTES + if (length > 4) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = this.tiftag.slice(pointer, pointer + length); + } else { + data = value.slice(0, length); + } + } else if (t == 9) { // SLONG + if (length > 1) { + pointer = unpack(this.endian_mark + "L", value)[0]; + data = unpack(this.endian_mark + nStr("l", length), + this.tiftag.slice(pointer, pointer + length * 4)); + } else { + data = unpack(this.endian_mark + nStr("l", length), + value); + } + } else if (t == 10) { // SRATIONAL + pointer = unpack(this.endian_mark + "L", value)[0]; + if (length > 1) { + data = []; + for (let x = 0; x < length; x++) { + data.push([unpack(this.endian_mark + "l", + this.tiftag.slice(pointer + x * 8, pointer + 4 + x * 8))[0], + unpack(this.endian_mark + "l", + this.tiftag.slice(pointer + 4 + x * 8, pointer + 8 + x * 8))[0] + ]); + } + } else { + data = [unpack(this.endian_mark + "l", + this.tiftag.slice(pointer, pointer + 4))[0], + unpack(this.endian_mark + "l", + this.tiftag.slice(pointer + 4, pointer + 8))[0] + ]; + } + } else { + throw new Error("Exif might be wrong. Got incorrect value " + + "type to decode. type:" + t); + } + + if ((data instanceof Array) && (data.length == 1)) { + return data[0]; + } else { + return data; + } + } + +} + + +function pack(mark, array) { + if (!(array instanceof Array)) { + throw new Error("'pack' error. Got invalid type argument."); + } + if ((mark.length - 1) != array.length) { + throw new Error("'pack' error. " + (mark.length - 1) + " marks, " + array.length + " elements."); + } + + let littleEndian; + if (mark[0] == "<") { + littleEndian = true; + } else if (mark[0] == ">") { + littleEndian = false; + } else { + throw new Error(""); + } + let packed = ""; + let p = 1; + let val = null; + let c = null; + let valStr = null; + + while (c = mark[p]) { + if (c.toLowerCase() == "b") { + val = array[p - 1]; + if ((c == "b") && (val < 0)) { + val += 0x100; + } + if ((val > 0xff) || (val < 0)) { + throw new Error("'pack' error."); + } else { + valStr = String.fromCharCode(val); + } + } else if (c == "H") { + val = array[p - 1]; + if ((val > 0xffff) || (val < 0)) { + throw new Error("'pack' error."); + } else { + valStr = String.fromCharCode(Math.floor((val % 0x10000) / 0x100)) + + String.fromCharCode(val % 0x100); + if (littleEndian) { + valStr = valStr.split("").reverse().join(""); + } + } + } else if (c.toLowerCase() == "l") { + val = array[p - 1]; + if ((c == "l") && (val < 0)) { + val += 0x100000000; + } + if ((val > 0xffffffff) || (val < 0)) { + throw new Error("'pack' error."); + } else { + valStr = String.fromCharCode(Math.floor(val / 0x1000000)) + + String.fromCharCode(Math.floor((val % 0x1000000) / 0x10000)) + + String.fromCharCode(Math.floor((val % 0x10000) / 0x100)) + + String.fromCharCode(val % 0x100); + if (littleEndian) { + valStr = valStr.split("").reverse().join(""); + } + } + } else { + throw new Error("'pack' error."); + } + + packed += valStr; + p += 1; + } + + return packed; +} + + +function unpack(mark, str) { + if (typeof (str) != "string") { + throw new Error("'unpack' error. Got invalid type argument."); + } + let l = 0; + for (let markPointer = 1; markPointer < mark.length; markPointer++) { + if (mark[markPointer].toLowerCase() == "b") { + l += 1; + } else if (mark[markPointer].toLowerCase() == "h") { + l += 2; + } else if (mark[markPointer].toLowerCase() == "l") { + l += 4; + } else { + throw new Error("'unpack' error. Got invalid mark."); + } + } + + if (l != str.length) { + throw new Error("'unpack' error. Mismatch between symbol and string length. " + l + ":" + str.length); + } + + let littleEndian; + if (mark[0] == "<") { + littleEndian = true; + } else if (mark[0] == ">") { + littleEndian = false; + } else { + throw new Error("'unpack' error."); + } + let unpacked = []; + let strPointer = 0; + let p = 1; + let val = null; + let c = null; + let length = null; + let sliced = ""; + + while (c = mark[p]) { + if (c.toLowerCase() == "b") { + length = 1; + sliced = str.slice(strPointer, strPointer + length); + val = sliced.charCodeAt(0); + if ((c == "b") && (val >= 0x80)) { + val -= 0x100; + } + } else if (c == "H") { + length = 2; + sliced = str.slice(strPointer, strPointer + length); + if (littleEndian) { + sliced = sliced.split("").reverse().join(""); + } + val = sliced.charCodeAt(0) * 0x100 + + sliced.charCodeAt(1); + } else if (c.toLowerCase() == "l") { + length = 4; + sliced = str.slice(strPointer, strPointer + length); + if (littleEndian) { + sliced = sliced.split("").reverse().join(""); + } + val = sliced.charCodeAt(0) * 0x1000000 + + sliced.charCodeAt(1) * 0x10000 + + sliced.charCodeAt(2) * 0x100 + + sliced.charCodeAt(3); + if ((c == "l") && (val >= 0x80000000)) { + val -= 0x100000000; + } + } else { + throw new Error("'unpack' error. " + c); + } + + unpacked.push(val); + strPointer += length; + p += 1; + } + + return unpacked; +} + + +function nStr(ch, num) { + let str = ""; + for (let i = 0; i < num; i++) { + str += ch; + } + return str; +} + + +function splitIntoSegments(data) { + if (data.slice(0, 2) != "\xff\xd8") { + throw new Error("Given data isn't JPEG."); + } + + let head = 2; + const segments = ["\xff\xd8"]; + while (true) { + if (data.slice(head, head + 2) == "\xff\xda") { + segments.push(data.slice(head)); + break; + } else { + var length = unpack(">H", data.slice(head + 2, head + 4))[0]; + var endPoint = head + length + 2; + segments.push(data.slice(head, endPoint)); + head = endPoint; + } + + if (head >= data.length) { + throw new Error("Wrong JPEG data."); + } + } + return segments; +} + + +function getExifSeg(segments) { + let seg; + for (let i = 0; i < segments.length; i++) { + seg = segments[i]; + if (seg.slice(0, 2) == "\xff\xe1" && + seg.slice(4, 10) == "Exif\x00\x00") { + return seg; + } + } + return null; +} + + +function mergeSegments(segments, exif) { + let hasExifSegment = false; + let additionalAPP1ExifSegments = []; + + segments.forEach(function (segment, i) { + // Replace first occurence of APP1:Exif segment + if (segment.slice(0, 2) == "\xff\xe1" && + segment.slice(4, 10) == "Exif\x00\x00" + ) { + if (!hasExifSegment) { + segments[i] = exif; + hasExifSegment = true; + } else { + additionalAPP1ExifSegments.unshift(i); + } + } + }); + + // Remove additional occurences of APP1:Exif segment + additionalAPP1ExifSegments.forEach(function (segmentIndex) { + segments.splice(segmentIndex, 1); + }); + + if (!hasExifSegment && exif) { + segments = [segments[0], exif].concat(segments.slice(1)); + } + + return segments.join(""); +} + + diff --git a/src/uni_modules/lime-shared/platform/index.ts b/src/uni_modules/lime-shared/platform/index.ts new file mode 100644 index 00000000..af07091d --- /dev/null +++ b/src/uni_modules/lime-shared/platform/index.ts @@ -0,0 +1,27 @@ +// @ts-nocheck +declare var tt: Uni +declare var swan: Uni +declare var my: Uni +declare var dd: Uni +declare var ks: Uni +declare var jd: Uni +declare var qa: Uni +declare var qq: Uni +declare var qh: Uni +declare var qq: Uni + +export function platform(): Uni | WechatMiniprogram.Wx { + const UNDEFINED = 'undefined' + if(typeof wx !== UNDEFINED) return wx // 微信 + if(typeof tt !== UNDEFINED) return tt // 字节 飞书 + if(typeof swan !== UNDEFINED) return swan // 百度 + if(typeof my !== UNDEFINED) return my // 支付宝 + if(typeof dd !== UNDEFINED) return dd // 钉钉 + if(typeof ks !== UNDEFINED) return ks // 快手 + if(typeof jd !== UNDEFINED) return jd // 京东 + if(typeof qa !== UNDEFINED) return qa // 快应用 + if(typeof qq !== UNDEFINED) return qq // qq + if(typeof qh !== UNDEFINED) return qh // 360 + if(typeof uni !== UNDEFINED) return uni + return null +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/raf/index.ts b/src/uni_modules/lime-shared/raf/index.ts new file mode 100644 index 00000000..ec7650d6 --- /dev/null +++ b/src/uni_modules/lime-shared/raf/index.ts @@ -0,0 +1,30 @@ +// @ts-nocheck +import {isBrowser} from '../isBrowser' + +// 是否支持被动事件监听 +export const supportsPassive = true; + +// 请求动画帧 +export function raf(fn: FrameRequestCallback): number { + // 如果是在浏览器环境下,使用 requestAnimationFrame 方法 + if (isBrowser) { + return requestAnimationFrame(fn); // 请求动画帧 + } else { // 在非浏览器环境下,使用 setTimeout 模拟 + return setTimeout(fn, 1000 / 30); // 使用 setTimeout 模拟动画帧,每秒钟执行 30 次 + } +} + +// 取消动画帧 +export function cancelRaf(id: number) { + // 如果是在浏览器环境下,使用 cancelAnimationFrame 方法 + if (isBrowser) { + cancelAnimationFrame(id); // 取消动画帧 + } else { // 在非浏览器环境下,使用 clearTimeout 模拟 + clearTimeout(id); // 使用 clearTimeout 模拟取消动画帧 + } +} + +// 双倍动画帧 +export function doubleRaf(fn: FrameRequestCallback): void { + raf(() => raf(fn)); // 在下一帧回调中再次请求动画帧,实现双倍动画帧效果 +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/random/index.ts b/src/uni_modules/lime-shared/random/index.ts new file mode 100644 index 00000000..dbf2681a --- /dev/null +++ b/src/uni_modules/lime-shared/random/index.ts @@ -0,0 +1,22 @@ +// @ts-nocheck +/** + * 生成一个指定范围内的随机数 + * @param min 随机数的最小值 + * @param max 随机数的最大值 + * @param fixed 随机数的小数位数,默认为 0 + * @returns 生成的随机数 + */ +export function random(min: number, max: number, fixed: number = 0) { + // 将 min 和 max 转换为数字类型 + min = +min || 0; + max = +max || 0; + // 计算随机数范围内的一个随机数 + const num = Math.random() * (max - min) + min; + // 如果 fixed 参数为 0,则返回四舍五入的整数随机数;否则保留固定小数位数 + return fixed == 0 ? Math.round(num) : Number(num.toFixed(fixed)); +} + +// 示例 +// console.log(random(0, 10)); // 输出:在 0 和 10 之间的一个整数随机数 +// console.log(random(0, 1, 2)); // 输出:在 0 和 1 之间的一个保留两位小数的随机数 +// console.log(random(1, 100, 3)); // 输出:在 1 和 100 之间的一个保留三位小数的随机数 \ No newline at end of file diff --git a/src/uni_modules/lime-shared/range/index.ts b/src/uni_modules/lime-shared/range/index.ts new file mode 100644 index 00000000..80acfbfc --- /dev/null +++ b/src/uni_modules/lime-shared/range/index.ts @@ -0,0 +1,31 @@ +// @ts-nocheck +/** + * 生成一个数字范围的数组 + * @param start 范围的起始值 + * @param end 范围的结束值 + * @param step 步长,默认为 1 + * @param fromRight 是否从右侧开始生成,默认为 false + * @returns 生成的数字范围数组 + */ +export function range(start: number, end: number, step: number = 1, fromRight: boolean = false): number[] { + let index = -1; + // 计算范围的长度 + let length = Math.max(Math.ceil((end - start) / (step || 1)), 0); + // 创建一个长度为 length 的数组 + const result = new Array(length); + + // 使用循环生成数字范围数组 + while (length--) { + // 根据 fromRight 参数决定从左侧还是右侧开始填充数组 + result[fromRight ? length : ++index] = start; + start += step; + } + + return result; +} + + +// 示例 +// console.log(range(0, 5)); // 输出: [0, 1, 2, 3, 4] +// console.log(range(1, 10, 2, true)); // 输出: [9, 7, 5, 3, 1] +// console.log(range(5, 0, -1)); // 输出: [5, 4, 3, 2, 1] \ No newline at end of file diff --git a/src/uni_modules/lime-shared/readme.md b/src/uni_modules/lime-shared/readme.md new file mode 100644 index 00000000..09ec7e99 --- /dev/null +++ b/src/uni_modules/lime-shared/readme.md @@ -0,0 +1,251 @@ +# lime-shared 工具库 +- 本人插件的几个公共函数 + +## 引入 +```js +// 按需引入 +// 这种只会引入相关的方法 +import {getRect} from '@/uni_modules/lime-shared/getRect' + +// 全量引入 +// 这种引入方式,会全量打包 +import {getRect} from '@/uni_modules/lime-shared' +``` + +## Utils + +#### getRect +- 返回节点尺寸信息 + +```js +// 组件内需要传入上下文 +// 如果是nvue 则需要在节点上加与id或class同名的ref +getRect('#id',{context: this}).then(res => {}) +``` + +#### addUnit +- 将未带单位的数值添加px,如果有单位则返回原值 + +```js +addUnit(10) +// 10px +``` + +#### unitConvert +- 将带有rpx|px的字符转成number,若本身是number则直接返回 + +```js +unitConvert('10rpx') +// 5 设备不同 返回的值也不同 +unitConvert('10px') +// 10 +unitConvert(10) +// 10 +``` + +#### canIUseCanvas2d +- 环境是否支持使用 canvas 2d + +```js +canIUseCanvas2d() +// 若支持返回 true 否则 false +``` + + +#### getCurrentPage +- 获取当前页 + +```js +const page = getCurrentPage() +``` + + +#### base64ToPath +- 把base64的图片转成临时路径 + +```js +base64ToPath(`xxxxx`).then(res => {}) +``` + +#### pathToBase64 +- 把图片的临时路径转成base64 + +```js +pathToBase64(`xxxxx/xxx.png`).then(res => {}) +``` + +#### sleep +- 睡眠,让 async 内部程序等待一定时间后再执行 + +```js +async next () => { + await sleep(300) + console.log('limeui'); +} +``` + +#### isBase64 +- 判断字符串是否为base64 + +```js +isBase64('xxxxx') +``` + +#### throttle +- 节流 + +```js +throttle((nama) => {console.log(nama)}, 200)('limeui'); +``` + +#### debounce +- 防抖 + +```js +debounce((nama) => {console.log(nama)}, 200)('limeui'); +``` + +#### random +- 返回指定范围的随机数 + +```js +random(1, 5); +``` + +#### range +- 生成区间数组 + +```js +range(0, 5) +// [0,1,2,3,4,5] +``` + +#### clamp +- 夹在min和max之间的数值,如小于min,返回min, 如大于max,返回max,否侧原值返回 + +```js +clamp(0, 10, -1) +// 0 +clamp(0, 10, 11) +// 10 +clamp(0, 10, 9) +// 9 +``` + +#### floatAdd +- 返回两个浮点数相加的结果 + +```js +floatAdd(0.1, 0.2) // 0.3 +``` + + +#### fillZero +- 补零,如果传入的是`个位数`则在前面补0 + +```js +fillZero(9); +// 09 +``` + +#### exif +- 获取图片exif +- 支持临时路径、base64 + +```js +uni.chooseImage({ + count: 1, //最多可以选择的图片张数 + sizeType: "original", + success: (res) => { + exif.getData(res.tempFiles[0], function() { + let tagj = exif.getTag(this, "GPSLongitude"); + let Orientation = exif.getTag(this, 'Orientation'); + console.log(tagj, Orientation) + }) + } +}) +``` + +#### selectComponent +- 获取页面或当前实例的指定组件,会在页面或实例向所有的节点查找(包括子组件或子子组件) +- 仅vue3,vue2没有测试过 + +```js +// 当前页面 +const page = getCurrentPage() +selectComponent('.custom', {context: page}).then(res => { +}) +``` + + +#### createAnimation +- 创建动画,与uni.createAnimation使用方法一致,只为了抹平nvue + +```html + +``` +```js +const ball = ref(null) +const animation = createAnimation({ + transformOrigin: "50% 50%", + duration: 1000, + timingFunction: "ease", + delay: 0 +}) + +animation.scale(2,2).rotate(45).step() +// nvue 无导出数据,这样写只为了平台一致, +// nvue 需要把 ref 传入,其它平台不需要 +const animationData = animation.export(ball.value) +``` + + +## composition-api +- 因本人插件需要兼容vue2/vue3,故增加一个vue文件,代替条件编译 +- vue2需要在main.js加上这一段 +```js +// vue2 +import Vue from 'vue' +import VueCompositionAPI from '@vue/composition-api' +Vue.use(VueCompositionAPI) +``` + +```js +//使用 +import {computed, onMounted, watch, reactive} from '@/uni_modules/lime-shared/vue' +``` + + +## Hooks +#### useIntersectionObserver +- 使用 Intersection Observer 观察元素可见性的钩子函数 + +```html +
+

Hello world

+
+``` + +```js +// options 接口可传的参数,若在插件里context为必传 +interface UseIntersectionObserverOptions { + root ?: string; // 观察器的根元素选择器字符串 + rootMargin ?: { + top ?: number; // 根元素顶部边距 + bottom ?: number; // 根元素底部边距 + left ?: number; // 根元素左侧边距 + right ?: number; // 根元素右侧边距 + }; // 根元素的边距 + thresholds ?: any[]; // 交叉比例数组,用于指定何时触发回调函数 + context ?: any; // 上下文对象,用于指定观察器的上下文 + initialRatio ?: number; // 初始的交叉比例 + observeAll ?: boolean; // 是否同时观察所有交叉对象 +} +const options: UseIntersectionObserverOptions = { + rootMargin: {top: 44}, + context: this +} +const {stop} = useIntersectionObserver('.target', (result) => { + +}, options) +``` \ No newline at end of file diff --git a/src/uni_modules/lime-shared/selectComponent/index.ts b/src/uni_modules/lime-shared/selectComponent/index.ts new file mode 100644 index 00000000..39db2fe9 --- /dev/null +++ b/src/uni_modules/lime-shared/selectComponent/index.ts @@ -0,0 +1,152 @@ +// @ts-nocheck +interface SelectOptions { + context?: any + needAll?: boolean + node?: boolean +} +// #ifdef MP +function selectMPComponent(key: string, name: string, context: any, needAll: boolean) { + const {proxy, $vm} = context + context = $vm || proxy + if(!['ref','component'].includes(key)) { + const queue = [context] + let result = null + const selector = (key == 'id' ? '#': '.') + name; + while(queue.length > 0) { + const child = queue.shift(); + const flag = child?.selectComponent(selector) + if(flag) { + if(!needAll) {return result = flag.$vm} + return result = child.selectAllComponents(selector).map(item => item.$vm) + } else { + child.$children && (queue.push(...child.$children)); + } + } + return result + } else { + const {$templateRefs} = context.$ + const nameMap = {} + for (var i = 0; i < $templateRefs.length; i++) { + const item = $templateRefs[i] + nameMap[item.i] = item.r + } + let result = [] + if(context.$children.length) { + const queue = [...context.$children] + while(queue.length > 0) { + const child = queue.shift(); + if(key == 'component' && (child.type?.name === name || child.$?.type?.name === name)) { + result.push(child) + } else if(child.$refs && child.$refs[name]) { + result = child.$refs[name] + } else if(nameMap[child.id] === name){ + result.push(child) + } else { + child.$children && (queue.push(...child.$children)); + } + if(result.length && !needAll) { + return; + } + } + } + return needAll ? result : result[0] + } +} +// #endif +// #ifdef H5 +function selectH5Component(key: string, name: string, context: any, needAll: boolean) { + const {_, component } = context + const child = {component: _ || component || context, children: null , subTree: null, props: null} + let result = [] + let queue = [child] + while(queue.length > 0 ) { + const child = queue.shift() + const {component, children , props, subTree} = child + if(key === 'component' && component?.type?.name == name) { + result.push(component) + } else if(key === 'ref' && component && (props?.ref == name || component[key][name])) { + if(props?.ref == name) { + //exposed + result.push(component) + } else if(component[key][name]) { + result.push(component[key][name]) + } + } else if(key !== 'ref' && component?.exposed && new RegExp(`\\b${name}\\b`).test(component.attrs[key])) { + // exposed + result.push(component) + } else if(children && Array.isArray(children)) { + queue.push(...children) + } else if(!component && subTree) { + queue.push(subTree) + } else if(component?.subTree) { + queue.push(component.subTree) + } + if(result.length && !needAll) { + break + } + } + return needAll ? result : result[0] +} +// #endif +// #ifdef APP +function selectAPPComponent(key: string, name: string, context: any, needAll: boolean, node: boolean) { + let result = [] + // const {_, component} = context + // const child = {component: _ || component || context, children: null, props: null, subTree: null} + const queue = [context] + while(queue.length > 0) { + const child = queue.shift() + const {component, children, props, subTree} = child + const isComp = component && props && component.exposed && !node + if(key == 'component' && child.type && child.type.name === name) { + result.push(component) + } else if(props?.[key] === name && node) { + result.push(child) + } else if(key === 'ref' && isComp && (props.ref === name || props.ref_key === name)) { + // exposed + result.push(component) + } else if(key !== 'ref' && isComp && new RegExp(`\\b${name}\\b`).test(props[key])) { + // exposed + result.push(component) + } + // else if(component && component.subTree && Array.isArray(component.subTree.children)){ + // queue.push(...component.subTree.children) + // } + else if(subTree) { + queue.push(subTree) + } else if(component && component.subTree){ + queue.push(component.subTree) + } + else if(children && Array.isArray(children)) { + queue.push(...children) + } + if(result.length && !needAll) { + break; + } + } + return needAll ? result : result[0] +} +// #endif +export function selectComponent(selector: string, options: SelectOptions = {}) { + // . class + // # id + // $ ref + // @ component name + const reg = /^(\.|#|@|\$)([a-zA-Z_0-9\-]+)$/; + if(!reg.test(selector)) return null + let { context, needAll, node} = options + const [,prefix, name] = selector.match(reg) + const symbolMappings = {'.': 'class', '#': 'id', '$':'ref', '@':'component'} + + const key = symbolMappings [prefix] //prefix === '.' ? 'class' : prefix === '#' ? 'id' : 'ref'; + console.log('key', key) + // #ifdef MP + return selectMPComponent(key, name, context, needAll) + // #endif + // #ifdef H5 + return selectH5Component(key, name, context, needAll) + // #endif + // #ifdef APP + return selectAPPComponent(key, name, context, needAll, node) + // #endif +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/sleep/index.ts b/src/uni_modules/lime-shared/sleep/index.ts new file mode 100644 index 00000000..3ae6926e --- /dev/null +++ b/src/uni_modules/lime-shared/sleep/index.ts @@ -0,0 +1,30 @@ +// @ts-nocheck +/** + * 延迟指定时间后解析的 Promise + * @param delay 延迟的时间(以毫秒为单位),默认为 300 毫秒 + * @returns 一个 Promise,在延迟结束后解析 + */ +export const sleep = (delay: number = 300) => + new Promise(resolve => setTimeout(resolve, delay)); + + +// 示例 +// async function example() { +// console.log("Start"); + +// // 延迟 1 秒后执行 +// await sleep(1000); +// console.log("1 second later"); + +// // 延迟 500 毫秒后执行 +// await sleep(500); +// console.log("500 milliseconds later"); + +// // 延迟 2 秒后执行 +// await sleep(2000); +// console.log("2 seconds later"); + +// console.log("End"); +// } + +// example(); \ No newline at end of file diff --git a/src/uni_modules/lime-shared/throttle/index.ts b/src/uni_modules/lime-shared/throttle/index.ts new file mode 100644 index 00000000..740f788b --- /dev/null +++ b/src/uni_modules/lime-shared/throttle/index.ts @@ -0,0 +1,41 @@ +// @ts-nocheck +/** + * 节流函数,用于限制函数的调用频率 + * @param fn 要进行节流的函数 + * @param delay 两次调用之间的最小间隔时间 + * @returns 节流后的函数 + */ +export function throttle(fn: (...args: any[]) => void, delay: number) { + let flag = true; // 标记是否可以执行函数 + + return (...args: any[]) => { + if (flag) { + flag = false; // 设置为不可执行状态 + fn(...args); // 执行传入的函数 + + setTimeout(() => { + flag = true; // 经过指定时间后,设置为可执行状态 + }, delay); + } + }; +} + + +// // 示例 +// // 定义一个被节流的函数 +// function handleScroll() { +// console.log("Scroll event handled!"); +// } + +// // 使用节流函数对 handleScroll 进行节流,间隔时间为 500 毫秒 +// const throttledScroll = throttle(handleScroll, 500); + +// // 模拟多次调用 handleScroll +// throttledScroll(); // 输出 "Scroll event handled!" +// throttledScroll(); // 不会输出 +// throttledScroll(); // 不会输出 + +// // 经过 500 毫秒后,再次调用 handleScroll +// setTimeout(() => { +// throttledScroll(); // 输出 "Scroll event handled!" +// }, 500); \ No newline at end of file diff --git a/src/uni_modules/lime-shared/toArray/index.ts b/src/uni_modules/lime-shared/toArray/index.ts new file mode 100644 index 00000000..b59b0afa --- /dev/null +++ b/src/uni_modules/lime-shared/toArray/index.ts @@ -0,0 +1,13 @@ +// @ts-nocheck +/** + * 将一个或多个元素转换为数组 + * @param item 要转换为数组的元素 + * @returns 转换后的数组 + */ +export const toArray = (item: T | T[]): T[] => Array.isArray(item) ? item : [item]; + +// 示例 +// console.log(toArray(5)); // 输出: [5] +// console.log(toArray("hello")); // 输出: ["hello"] +// console.log(toArray([1, 2, 3])); // 输出: [1, 2, 3] +// console.log(toArray(["apple", "banana"])); // 输出: ["apple", "banana"] \ No newline at end of file diff --git a/src/uni_modules/lime-shared/toNumber/index.ts b/src/uni_modules/lime-shared/toNumber/index.ts new file mode 100644 index 00000000..a0745bb3 --- /dev/null +++ b/src/uni_modules/lime-shared/toNumber/index.ts @@ -0,0 +1,15 @@ +// @ts-nocheck +/** + * 将字符串转换为数字 + * @param val 要转换的字符串 + * @returns 转换后的数字或原始字符串 + */ +export function toNumber(val: string): number | string { + const n = parseFloat(val); // 使用 parseFloat 函数将字符串转换为浮点数 + return isNaN(n) ? val : n; // 使用 isNaN 函数判断是否为非数字,返回转换后的数字或原始字符串 +} + +// 示例 +// console.log(toNumber("123")); // 输出: 123 +// console.log(toNumber("3.14")); // 输出: 3.14 +// console.log(toNumber("hello")); // 输出: "hello" \ No newline at end of file diff --git a/src/uni_modules/lime-shared/unitConvert/index.ts b/src/uni_modules/lime-shared/unitConvert/index.ts new file mode 100644 index 00000000..7cedccf6 --- /dev/null +++ b/src/uni_modules/lime-shared/unitConvert/index.ts @@ -0,0 +1,39 @@ +// @ts-nocheck +import {isString} from '../isString' +import {isNumeric} from '../isNumeric' + +/** + * 单位转换函数,将字符串数字或带有单位的字符串转换为数字 + * @param value 要转换的值,可以是字符串数字或带有单位的字符串 + * @returns 转换后的数字,如果无法转换则返回0 + */ +export function unitConvert(value: string | number): number { + // 如果是字符串数字 + if (isNumeric(value)) { + return Number(value); + } + // 如果有单位 + if (isString(value)) { + const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g; + const results = reg.exec(value); + if (!value || !results) { + return 0; + } + const unit = results[3]; + value = parseFloat(value); + if (unit === 'rpx') { + return uni.upx2px(value); + } + if (unit === 'px') { + return value * 1; + } + // 如果是其他单位,可以继续添加对应的转换逻辑 + } + return 0; +} + +// 示例 +// console.log(unitConvert("123")); // 输出: 123 (字符串数字转换为数字) +// console.log(unitConvert("3.14em")); // 输出: 0 (无法识别的单位) +// console.log(unitConvert("20rpx")); // 输出: 根据具体情况而定 (根据单位进行转换) +// console.log(unitConvert(10)); // 输出: 10 (数字不需要转换) \ No newline at end of file diff --git a/src/uni_modules/lime-shared/useIntersectionObserver/index.ts b/src/uni_modules/lime-shared/useIntersectionObserver/index.ts new file mode 100644 index 00000000..b5862fe8 --- /dev/null +++ b/src/uni_modules/lime-shared/useIntersectionObserver/index.ts @@ -0,0 +1,81 @@ +import { watch, unref, Ref } from "../vue" +// #ifdef APP-NVUE +// const dom = weex.requireModule('dom') +// const dom = uni.requireNativePlugin('dom') +// #endif + +interface UseIntersectionObserverOptions { + root ?: string; // 观察器的根元素选择器字符串 + rootMargin ?: { + top ?: number; // 根元素顶部边距 + bottom ?: number; // 根元素底部边距 + left ?: number; // 根元素左侧边距 + right ?: number; // 根元素右侧边距 + }; // 根元素的边距 + thresholds ?: any[]; // 交叉比例数组,用于指定何时触发回调函数 + context ?: any; // 上下文对象,用于指定观察器的上下文 + initialRatio ?: number; // 初始的交叉比例 + observeAll ?: boolean; // 是否同时观察所有交叉对象 +} + +/** + * 使用 Intersection Observer 观察元素可见性的自定义钩子函数 + * @param {Ref | string} target - 目标元素,可以是一个字符串或 ref 对象 + * @param {(result: UniNamespace.ObserveResult) => void} callback - 回调函数,当目标元素的可见性发生变化时调用 + * @param {UseIntersectionObserverOptions} options - 可选的配置参数 + * @returns {Object} - 包含 stop 方法的对象,用于停止观察 + */ +export function useIntersectionObserver( + target : Ref | string, + callback : (result : UniNamespace.ObserveResult) => void, + options : UseIntersectionObserverOptions = {}) { + const { + root, // 观察器的根元素选择器 + rootMargin = { top: 0, bottom: 0 }, // 根元素的边距,默认为顶部和底部都为0 + thresholds = [0], // 交叉比例数组,默认为[0] + initialRatio = 0, // 初始交叉比例,默认为0 + observeAll = false, // 是否同时观察所有交叉对象,默认为false + context // 上下文对象,用于指定观察器的上下文 + } = options + const noop = () => { }; // 空函数,用于初始化 cleanup + let cleanup = noop; // 清理函数,用于停止 Intersection Observer 的观察 + + const stopWatch = watch(() => ({ el: unref(target), root: unref(root) }), ({ el, root }) => { + if (!el) { + return + } + // #ifndef APP-NVUE + // 创建 Intersection Observer 实例 + const observer = uni.createIntersectionObserver(context, { thresholds, initialRatio, observeAll }) + if (root) { + // 相对于根元素设置边界 + observer.relativeTo(root, rootMargin) + } else { + // 相对于视口设置边界 + observer.relativeToViewport(rootMargin) + } + // 观察目标元素的可见性变化 + observer.observe(el, callback) + cleanup = () => { + // 停止观察 + observer.disconnect() + // 将 cleanup 函数重置为空函数 + cleanup = noop + } + // #endif + // #ifdef APP-NVUE + // dom.getComponentRect(el, (res) => { + // console.log('res', res) + // }) + // #endif + }, { immediate: true, flush: 'post' }) + + const stop = () => { + // 调用 cleanup 函数停止观察 + cleanup && cleanup() + // 停止 watch + stopWatch && stopWatch() + } + + return { stop } +} \ No newline at end of file diff --git a/src/uni_modules/lime-shared/vue/index.ts b/src/uni_modules/lime-shared/vue/index.ts new file mode 100644 index 00000000..91a4a7f5 --- /dev/null +++ b/src/uni_modules/lime-shared/vue/index.ts @@ -0,0 +1,8 @@ +// @ts-nocheck + +// #ifdef VUE3 +export * from 'vue'; +// #endif +// #ifndef VUE3 +export * from '@vue/composition-api'; +// #endif