├── LICENSE ├── README.md ├── app.js ├── app.json ├── app.wxss ├── assets ├── blazeface_v1 │ ├── group1-shard1of1.bin │ └── model.json ├── facemesh_v1 │ ├── group1-shard1of1.bin │ └── model.json ├── handdetector_v1 │ ├── group1-shard1of2.bin │ ├── group1-shard2of2.bin │ └── model.json ├── handskeleton_v1 │ ├── anchors.json │ ├── group1-shard1of2.bin │ ├── group1-shard2of2.bin │ └── model.json ├── mobilenet_v2_050_224 │ ├── group1-shard1of2.bin │ ├── group1-shard2of2.bin │ └── model.json └── sunglass.glb ├── miniprogram_npm ├── @tensorflow-models │ ├── blazeface │ │ └── index.js │ ├── face-landmarks-detection │ │ └── index.js │ └── handpose │ │ ├── index.js │ │ └── index.js.map ├── @tensorflow │ ├── tfjs-backend-cpu │ │ └── index.js │ ├── tfjs-backend-webgl │ │ └── index.js │ ├── tfjs-converter │ │ └── index.js │ └── tfjs-core │ │ └── index.js ├── abab │ └── index.js ├── balanced-match │ └── index.js ├── base64-js │ └── index.js ├── brace-expansion │ └── index.js ├── concat-map │ └── index.js ├── fetch-wechat │ └── index.js ├── fs.realpath │ └── index.js ├── glob │ └── index.js ├── inflight │ └── index.js ├── inherits │ └── index.js ├── minimatch │ └── index.js ├── node-fetch │ └── index.js ├── once │ └── index.js ├── path-is-absolute │ └── index.js ├── rimraf │ └── index.js ├── seedrandom │ └── index.js ├── text-encoder │ └── index.js ├── threejs-miniprogram │ └── index.js └── wrappy │ └── index.js ├── package-lock.json ├── package.json ├── package_face_2d_mask ├── pages │ ├── camera │ │ ├── camera.js │ │ ├── camera.json │ │ ├── camera.wxml │ │ └── camera.wxss │ └── photo │ │ ├── photo.js │ │ ├── photo.json │ │ ├── photo.wxml │ │ └── photo.wxss └── utils │ ├── cat_beard.png │ ├── faceBusiness.js │ └── modelBusiness.js ├── package_face_3d_mask ├── pages │ ├── camera │ │ ├── camera.js │ │ ├── camera.json │ │ ├── camera.wxml │ │ └── camera.wxss │ └── photo │ │ ├── photo.js │ │ ├── photo.json │ │ ├── photo.wxml │ │ └── photo.wxss └── utils │ ├── GLTFLoader.js │ ├── faceBusiness.js │ └── modelBusiness.js ├── package_handpose_mask ├── pages │ ├── camera │ │ ├── camera.js │ │ ├── camera.json │ │ ├── camera.wxml │ │ └── camera.wxss │ └── photo │ │ ├── photo.js │ │ ├── photo.json │ │ ├── photo.wxml │ │ └── photo.wxss └── utils │ ├── cat_beard.png │ ├── handBusiness.js │ └── modelBusiness.js ├── package_mobilenet ├── pages │ ├── camera │ │ ├── camera.js │ │ ├── camera.json │ │ ├── camera.wxml │ │ └── camera.wxss │ └── photo │ │ ├── photo.js │ │ ├── photo.json │ │ ├── photo.wxml │ │ └── photo.wxss └── utils │ ├── imagenet_classes.js │ └── mobilenet.js ├── pages └── index │ ├── index.js │ ├── index.json │ ├── index.wxml │ └── index.wxss ├── plugin ├── config.js ├── file_storage.js ├── index.js ├── local_storage.js ├── model_artifacts.js └── wechat_platform.js ├── project.config.json ├── screenshot ├── 1.jpg ├── 3-1.jpg ├── 3-2.jpg ├── 3-3.jpg ├── 4-1.jpg ├── 4-2.jpg ├── 4-3.jpg ├── 5.jpg ├── 6.jpg └── 7.jpg ├── sitemap.json └── style └── weui.wxss /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 AR Fashion 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Chinese README](https://zhuanlan.zhihu.com/p/81636351) 2 | 3 | ## Updated 4 | 5 | | Date   | Update | 6 | | -- | -- | 7 | | 2022-01-05 | New: Added a image classify demo using tfjs. | 8 | | 2021-09-08 | New: Added a hand pose demo using tfjs. It is slow and about 500 ms per detection.| 9 | | 2021-03-13 | Bug Fixed: 1. The image of the face 2d mask is not displayed on android WeChat. 2. When enter the demo UI on the second time, the 3D model is not displayed.| 10 | | 2021-03-11 | New: A Face AR using "face-landmarks-detection" and "TensorFlow.js". Update: Replace "face-api.js" with "face-landmarks-detection", the codes of "face-api.js" are removed. | 11 | | 2019-09-07 | New: A Face detecting and recognition with "face-api.js". | 12 | 13 | ## Introduction on WeChat Mini-program AR 14 | 15 | TensorFlow.js is a JavaScript library for machine learning. 16 | There is a WeChat Mini-program plugin for TensorFlow.js. 17 | 18 | [tfjs-wechat](https://github.com/tensorflow/tfjs-wechat) 19 | 20 | We can create AR effects with TensorFlow.js. A "face-landmarks-detection" library is based on TensorFlow.js. 21 | 22 | The "face-landmarks-detection" library offers a face detection in the browser environment. 23 | 24 | [face-landmarks-detection](https://github.com/tensorflow/tfjs-models/tree/master/face-landmarks-detection) 25 | 26 | Why choose "face-landmarks-detection"? 27 | 28 | Because I didn't find a tiny model of TensorFlow.js for face detecting until I found the "face-landmarks-detection" library. 29 | 30 | This demo demonstrates a face AR. 31 | 32 | Index Page of the WeChat Mini-program 33 | 34 | ![avatar](screenshot/1.jpg) 35 | 36 | ## Face Detecting and 3D Mask 37 | 38 | Use the demo to scan a face. Expect a effect below. 39 | 40 | ![avatar](screenshot/3-1.jpg) 41 | 42 | A effect of translating and scaling. 43 | 44 | ![avatar](screenshot/3-2.jpg) 45 | 46 | A effect of rotating. 47 | 48 | ![avatar](screenshot/3-3.jpg) 49 | 50 | ## Face Detecting and 2D Mask 51 | 52 | Use the demo to scan a face. Expect a effect below. 53 | 54 | ![avatar](screenshot/4-1.jpg) 55 | 56 | A effect of translating and scaling. 57 | 58 | ![avatar](screenshot/4-2.jpg) 59 | 60 | A effect of rotating. 61 | 62 | ![avatar](screenshot/4-3.jpg) 63 | 64 | ## Hand Pose and 2D Mask 65 | 66 | Use the demo to scan a hand. Expect a effect below. 67 | 68 | ![avatar](screenshot/5.jpg) 69 | 70 | ## Image Classify 71 | 72 | Use the demo to scan a cup. Expect a effect below. 73 | 74 | ![avatar](screenshot/7.jpg) 75 | 76 | ## How to build 77 | 78 | The WeChat Mini-program includes some npm packages. We install and compile the npm packages. 79 | 80 | step 1: run "npm install" 81 | 82 | step 2: run "WeChat developer tool -- Tool Menu -- build npm", a folder "miniprogram_npm" will be created. 83 | 84 | The project has included a "miniprogram_npm" folder precompiled. 85 | 86 | File: /package.json 87 | 88 | ```javascript 89 | "dependencies": { 90 | "@tensorflow-models/face-landmarks-detection": "0.0.3", 91 | "@tensorflow-models/handpose": "0.0.6", 92 | "@tensorflow/tfjs-backend-webgl": "2.1.0", 93 | "@tensorflow/tfjs-converter": "2.1.0", 94 | "@tensorflow/tfjs-core": "2.1.0", 95 | "abab": "2.0.0", 96 | "base64-js": "1.3.1", 97 | "fetch-wechat": "0.0.3", 98 | "text-encoder": "0.0.4", 99 | "threejs-miniprogram": "0.0.2" 100 | } 101 | ``` 102 | 103 | ## Set the url of the "TensorFlow.js" model 104 | 105 | You can search a keyword "BLAZEFACE_MODEL_URL" in the "blazeface" folder. The search result is modified. 106 | 107 | File: /miniprogram_npm/@tensorflow-models/blazeface 108 | 109 | ```javascript 110 | // modified 111 | var BLAZEFACE_MODEL_URL = 'https://m.sanyue.red/demo/tfjs/blazeface_v1'; 112 | ``` 113 | 114 | You can search a keyword "FACEMESH_GRAPHMODEL_PATH" in the "face-landmarks-detection" folder. 115 | 116 | File: /miniprogram_npm/@tensorflow-models/face-landmarks-detection 117 | 118 | ```javascript 119 | // modified 120 | var FACEMESH_GRAPHMODEL_PATH = 'https://m.sanyue.red/demo/tfjs/facemesh_v1'; 121 | ``` 122 | 123 | You can search keywords that are "HANDDETECT_MODEL_PATH" and "HANDPOSE_MODEL_PATH" in the "handpose" folder. 124 | 125 | File: /miniprogram_npm/@tensorflow-models/handpose 126 | 127 | ```javascript 128 | // modified 129 | HANDDETECT_MODEL_PATH = 'https://m.sanyue.red/demo/tfjs/handdetector_v1'; 130 | 131 | // modified 132 | HANDPOSE_MODEL_PATH = 'https://m.sanyue.red/demo/tfjs/handskeleton_v1'; 133 | ``` 134 | 135 | You can search keywords that are "mobilenet_model_path" in the file "mobilenet.js". 136 | 137 | File: /package_mobilenet/utils/mobilenet.js 138 | 139 | ```javascript 140 | // mobilenet_model_path 141 | '2.00': { 142 | '0.50': { 143 | url: 'https://m.sanyue.red/demo/tfjs/mobilenet_v2_050_224', 144 | inputRange: [0, 1] 145 | }, 146 | ``` 147 | 148 | ## Set the url of the 3D model 149 | 150 | You may replace the default url of a gltf model for 3D mask. 151 | 152 | File: /package_face_3d_mask/pages/photo/photo.js and /package_face_3d_mask/pagescamera/camera.js 153 | 154 | ```javascript 155 | // a url of gltf model 156 | const modelUrl = 'https://m.sanyue.red/demo/gltf/sunglass.glb';; 157 | ``` 158 | 159 | ## Set the url of the 2D sprite image 160 | 161 | You may replace the default url of a image for 2D mask. 162 | 163 | File: /package_face_2d_mask/pages/photo/photo.js and /package_face_2d_mask/pages/camera/camera.js 164 | 165 | ```javascript 166 | // a url of sprite image 167 | const modelUrl = '../../utils/cat_beard.png'; 168 | ``` 169 | 170 | ## How to put a 3D model or a image on an other position 171 | 172 | This is a map of the 486 keypoints of a face. 173 | 174 | [486 keypoints Map](https://github.com/tensorflow/tfjs-models/raw/master/face-landmarks-detection/mesh_map.jpg) 175 | 176 | For example, a number 168, number 122 and number 351 are the middle of the eyes. 177 | 178 | File: /package_face_3d_mask/utils/modelBusiness.js 179 | 180 | ```javascript 181 | // index of the track points of the face 182 | const trackPointA = 168; 183 | const trackPointB = 122; 184 | const trackPointC = 351; 185 | ``` 186 | For example, a number 0, number 61 and number 291 are the mouth. 187 | 188 | File: /package_face_2d_mask/utils/modelBusiness.js 189 | 190 | ```javascript 191 | // index of the track points of the face 192 | const trackPointA = 0; 193 | const trackPointB = 61; 194 | const trackPointC = 291; 195 | ``` 196 | 197 | ## Hand Pose Map 198 | 199 | The landmarks of the hand pose. 200 | 201 | ![avatar](screenshot/6.jpg) 202 | 203 | ```javascript 204 | // the "predictions" is an array of objects describing each detected hand. 205 | [ 206 | { 207 | handInViewConfidence: 1, 208 | boundingBox: { 209 | topLeft: [162.91, -17.42], 210 | bottomRight: [548.56, 368.23], 211 | }, 212 | landmarks: [ 213 | [472.52, 298.59, 0.00], 214 | [412.80, 315.64, -6.18], 215 | // etc. 216 | ], 217 | annotations: { 218 | thumb: [ 219 | [412.80, 315.64, -6.18] 220 | [350.02, 298.38, -7.14], 221 | // etc. 222 | ], 223 | // etc. 224 | } 225 | } 226 | ] 227 | ``` -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2019 Google LLC. All Rights Reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * ============================================================================= 16 | */ 17 | 18 | const fetchWechat = require('fetch-wechat'); 19 | const tf = require('@tensorflow/tfjs-core'); 20 | const webgl = require('@tensorflow/tfjs-backend-webgl'); 21 | const plugin = require('./plugin/index.js'); 22 | const ENABLE_DEBUG = true; 23 | //app.js 24 | App({ 25 | globalData: { 26 | localStorageIO: plugin.localStorageIO, 27 | fileStorageIO: plugin.fileStorageIO, 28 | }, 29 | onLaunch: async function () { 30 | /* 31 | 注意 由于最新版本的WeChat的OffscreenCanvas会随页面跳转而失效, 32 | 在app.js的 onLaunch 函数中设置 tfjs 会导致小程序退出或页面跳转之后操作出错。 33 | 建议在使用tfjs的page的onLoad中调用 configPlugin 函数。 34 | */ 35 | plugin.configPlugin({ 36 | fetchFunc: fetchWechat.fetchFunc(), 37 | tf, 38 | webgl, 39 | canvas: wx.createOffscreenCanvas() 40 | }, 41 | ENABLE_DEBUG); 42 | 43 | /* 44 | 注意 使用WASM暂时只能导入 2.0.0的tfjs库。因为2.0.1 版本wasm有和WeChat兼容性问题。 45 | 中低端手机的GPU往往相对CPU要弱一些,而WASM backend是跑在CPU上的,这就为中低端手机提供了另一个加速平台。 46 | const info = wx.getSystemInfoSync(); 47 | console.log(info.platform); 48 | if (info.platform == 'android') { 49 | setWasmPath( 50 | 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@2.0.0/wasm-out/tfjs-backend-wasm.wasm', 51 | true); 52 | await tf.setBackend('wasm'); 53 | console.log('set wasm as backend'); 54 | } 55 | */ 56 | } 57 | }) 58 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index" 4 | ], 5 | "subpackages": [ 6 | { 7 | "root": "package_face_3d_mask", 8 | "pages": [ 9 | "pages/camera/camera", 10 | "pages/photo/photo" 11 | ] 12 | }, 13 | { 14 | "root": "package_face_2d_mask", 15 | "pages": [ 16 | "pages/camera/camera", 17 | "pages/photo/photo" 18 | ] 19 | }, 20 | { 21 | "root": "package_handpose_mask", 22 | "pages": [ 23 | "pages/camera/camera", 24 | "pages/photo/photo" 25 | ] 26 | } 27 | ], 28 | "window": { 29 | "backgroundTextStyle": "light", 30 | "navigationBarBackgroundColor": "#fff", 31 | "navigationBarTitleText": "Miniprogram AI", 32 | "navigationBarTextStyle": "black" 33 | }, 34 | "sitemapLocation": "sitemap.json" 35 | } 36 | -------------------------------------------------------------------------------- /app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | @import "style/weui.wxss" 3 | 4 | page{ 5 | background-color: #F8F8F8; 6 | font-size: 16px; 7 | font-family: -apple-system-font,Helvetica Neue,Helvetica,sans-serif; 8 | } 9 | .page__hd { 10 | padding: 40px; 11 | } 12 | .page__bd { 13 | padding-bottom: 40px; 14 | } 15 | .page__bd_spacing { 16 | padding-left: 15px; 17 | padding-right: 15px; 18 | } 19 | 20 | .page__ft{ 21 | padding-bottom: 10px; 22 | text-align: center; 23 | } 24 | 25 | .page__title { 26 | text-align: left; 27 | font-size: 20px; 28 | font-weight: 400; 29 | } 30 | 31 | .page__desc { 32 | margin-top: 5px; 33 | color: #888888; 34 | text-align: left; 35 | font-size: 14px; 36 | } 37 | 38 | .marginTop10{ 39 | margin-top: 10px; 40 | } -------------------------------------------------------------------------------- /assets/blazeface_v1/group1-shard1of1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-TFJS/5152b650c33a5fadc4be16496441ec0e02c79329/assets/blazeface_v1/group1-shard1of1.bin -------------------------------------------------------------------------------- /assets/facemesh_v1/group1-shard1of1.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-TFJS/5152b650c33a5fadc4be16496441ec0e02c79329/assets/facemesh_v1/group1-shard1of1.bin -------------------------------------------------------------------------------- /assets/handdetector_v1/group1-shard1of2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-TFJS/5152b650c33a5fadc4be16496441ec0e02c79329/assets/handdetector_v1/group1-shard1of2.bin -------------------------------------------------------------------------------- /assets/handdetector_v1/group1-shard2of2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-TFJS/5152b650c33a5fadc4be16496441ec0e02c79329/assets/handdetector_v1/group1-shard2of2.bin -------------------------------------------------------------------------------- /assets/handskeleton_v1/group1-shard1of2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-TFJS/5152b650c33a5fadc4be16496441ec0e02c79329/assets/handskeleton_v1/group1-shard1of2.bin -------------------------------------------------------------------------------- /assets/handskeleton_v1/group1-shard2of2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-TFJS/5152b650c33a5fadc4be16496441ec0e02c79329/assets/handskeleton_v1/group1-shard2of2.bin -------------------------------------------------------------------------------- /assets/mobilenet_v2_050_224/group1-shard1of2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-TFJS/5152b650c33a5fadc4be16496441ec0e02c79329/assets/mobilenet_v2_050_224/group1-shard1of2.bin -------------------------------------------------------------------------------- /assets/mobilenet_v2_050_224/group1-shard2of2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-TFJS/5152b650c33a5fadc4be16496441ec0e02c79329/assets/mobilenet_v2_050_224/group1-shard2of2.bin -------------------------------------------------------------------------------- /assets/sunglass.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-TFJS/5152b650c33a5fadc4be16496441ec0e02c79329/assets/sunglass.glb -------------------------------------------------------------------------------- /miniprogram_npm/abab/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415622, function(require, module, exports) { 8 | 9 | 10 | const atob = require("./lib/atob"); 11 | const btoa = require("./lib/btoa"); 12 | 13 | module.exports = { 14 | atob, 15 | btoa 16 | }; 17 | 18 | }, function(modId) {var map = {"./lib/atob":1615362415623,"./lib/btoa":1615362415624}; return __REQUIRE__(map[modId], modId); }) 19 | __DEFINE__(1615362415623, function(require, module, exports) { 20 | 21 | 22 | /** 23 | * Implementation of atob() according to the HTML and Infra specs, except that 24 | * instead of throwing INVALID_CHARACTER_ERR we return null. 25 | */ 26 | function atob(data) { 27 | // Web IDL requires DOMStrings to just be converted using ECMAScript 28 | // ToString, which in our case amounts to using a template literal. 29 | data = `${data}`; 30 | // "Remove all ASCII whitespace from data." 31 | data = data.replace(/[ \t\n\f\r]/g, ""); 32 | // "If data's length divides by 4 leaving no remainder, then: if data ends 33 | // with one or two U+003D (=) code points, then remove them from data." 34 | if (data.length % 4 === 0) { 35 | data = data.replace(/==?$/, ""); 36 | } 37 | // "If data's length divides by 4 leaving a remainder of 1, then return 38 | // failure." 39 | // 40 | // "If data contains a code point that is not one of 41 | // 42 | // U+002B (+) 43 | // U+002F (/) 44 | // ASCII alphanumeric 45 | // 46 | // then return failure." 47 | if (data.length % 4 === 1 || /[^+/0-9A-Za-z]/.test(data)) { 48 | return null; 49 | } 50 | // "Let output be an empty byte sequence." 51 | let output = ""; 52 | // "Let buffer be an empty buffer that can have bits appended to it." 53 | // 54 | // We append bits via left-shift and or. accumulatedBits is used to track 55 | // when we've gotten to 24 bits. 56 | let buffer = 0; 57 | let accumulatedBits = 0; 58 | // "Let position be a position variable for data, initially pointing at the 59 | // start of data." 60 | // 61 | // "While position does not point past the end of data:" 62 | for (let i = 0; i < data.length; i++) { 63 | // "Find the code point pointed to by position in the second column of 64 | // Table 1: The Base 64 Alphabet of RFC 4648. Let n be the number given in 65 | // the first cell of the same row. 66 | // 67 | // "Append to buffer the six bits corresponding to n, most significant bit 68 | // first." 69 | // 70 | // atobLookup() implements the table from RFC 4648. 71 | buffer <<= 6; 72 | buffer |= atobLookup(data[i]); 73 | accumulatedBits += 6; 74 | // "If buffer has accumulated 24 bits, interpret them as three 8-bit 75 | // big-endian numbers. Append three bytes with values equal to those 76 | // numbers to output, in the same order, and then empty buffer." 77 | if (accumulatedBits === 24) { 78 | output += String.fromCharCode((buffer & 0xff0000) >> 16); 79 | output += String.fromCharCode((buffer & 0xff00) >> 8); 80 | output += String.fromCharCode(buffer & 0xff); 81 | buffer = accumulatedBits = 0; 82 | } 83 | // "Advance position by 1." 84 | } 85 | // "If buffer is not empty, it contains either 12 or 18 bits. If it contains 86 | // 12 bits, then discard the last four and interpret the remaining eight as 87 | // an 8-bit big-endian number. If it contains 18 bits, then discard the last 88 | // two and interpret the remaining 16 as two 8-bit big-endian numbers. Append 89 | // the one or two bytes with values equal to those one or two numbers to 90 | // output, in the same order." 91 | if (accumulatedBits === 12) { 92 | buffer >>= 4; 93 | output += String.fromCharCode(buffer); 94 | } else if (accumulatedBits === 18) { 95 | buffer >>= 2; 96 | output += String.fromCharCode((buffer & 0xff00) >> 8); 97 | output += String.fromCharCode(buffer & 0xff); 98 | } 99 | // "Return output." 100 | return output; 101 | } 102 | /** 103 | * A lookup table for atob(), which converts an ASCII character to the 104 | * corresponding six-bit number. 105 | */ 106 | function atobLookup(chr) { 107 | if (/[A-Z]/.test(chr)) { 108 | return chr.charCodeAt(0) - "A".charCodeAt(0); 109 | } 110 | if (/[a-z]/.test(chr)) { 111 | return chr.charCodeAt(0) - "a".charCodeAt(0) + 26; 112 | } 113 | if (/[0-9]/.test(chr)) { 114 | return chr.charCodeAt(0) - "0".charCodeAt(0) + 52; 115 | } 116 | if (chr === "+") { 117 | return 62; 118 | } 119 | if (chr === "/") { 120 | return 63; 121 | } 122 | // Throw exception; should not be hit in tests 123 | return undefined; 124 | } 125 | 126 | module.exports = atob; 127 | 128 | }, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); }) 129 | __DEFINE__(1615362415624, function(require, module, exports) { 130 | 131 | 132 | /** 133 | * btoa() as defined by the HTML and Infra specs, which mostly just references 134 | * RFC 4648. 135 | */ 136 | function btoa(s) { 137 | let i; 138 | // String conversion as required by Web IDL. 139 | s = `${s}`; 140 | // "The btoa() method must throw an "InvalidCharacterError" DOMException if 141 | // data contains any character whose code point is greater than U+00FF." 142 | for (i = 0; i < s.length; i++) { 143 | if (s.charCodeAt(i) > 255) { 144 | return null; 145 | } 146 | } 147 | let out = ""; 148 | for (i = 0; i < s.length; i += 3) { 149 | const groupsOfSix = [undefined, undefined, undefined, undefined]; 150 | groupsOfSix[0] = s.charCodeAt(i) >> 2; 151 | groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4; 152 | if (s.length > i + 1) { 153 | groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4; 154 | groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2; 155 | } 156 | if (s.length > i + 2) { 157 | groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6; 158 | groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f; 159 | } 160 | for (let j = 0; j < groupsOfSix.length; j++) { 161 | if (typeof groupsOfSix[j] === "undefined") { 162 | out += "="; 163 | } else { 164 | out += btoaLookup(groupsOfSix[j]); 165 | } 166 | } 167 | } 168 | return out; 169 | } 170 | 171 | /** 172 | * Lookup table for btoa(), which converts a six-bit number into the 173 | * corresponding ASCII character. 174 | */ 175 | function btoaLookup(idx) { 176 | if (idx < 26) { 177 | return String.fromCharCode(idx + "A".charCodeAt(0)); 178 | } 179 | if (idx < 52) { 180 | return String.fromCharCode(idx - 26 + "a".charCodeAt(0)); 181 | } 182 | if (idx < 62) { 183 | return String.fromCharCode(idx - 52 + "0".charCodeAt(0)); 184 | } 185 | if (idx === 62) { 186 | return "+"; 187 | } 188 | if (idx === 63) { 189 | return "/"; 190 | } 191 | // Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests. 192 | return undefined; 193 | } 194 | 195 | module.exports = btoa; 196 | 197 | }, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); }) 198 | return __REQUIRE__(1615362415622); 199 | })() 200 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/balanced-match/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415625, function(require, module, exports) { 8 | 9 | module.exports = balanced; 10 | function balanced(a, b, str) { 11 | if (a instanceof RegExp) a = maybeMatch(a, str); 12 | if (b instanceof RegExp) b = maybeMatch(b, str); 13 | 14 | var r = range(a, b, str); 15 | 16 | return r && { 17 | start: r[0], 18 | end: r[1], 19 | pre: str.slice(0, r[0]), 20 | body: str.slice(r[0] + a.length, r[1]), 21 | post: str.slice(r[1] + b.length) 22 | }; 23 | } 24 | 25 | function maybeMatch(reg, str) { 26 | var m = str.match(reg); 27 | return m ? m[0] : null; 28 | } 29 | 30 | balanced.range = range; 31 | function range(a, b, str) { 32 | var begs, beg, left, right, result; 33 | var ai = str.indexOf(a); 34 | var bi = str.indexOf(b, ai + 1); 35 | var i = ai; 36 | 37 | if (ai >= 0 && bi > 0) { 38 | begs = []; 39 | left = str.length; 40 | 41 | while (i >= 0 && !result) { 42 | if (i == ai) { 43 | begs.push(i); 44 | ai = str.indexOf(a, i + 1); 45 | } else if (begs.length == 1) { 46 | result = [ begs.pop(), bi ]; 47 | } else { 48 | beg = begs.pop(); 49 | if (beg < left) { 50 | left = beg; 51 | right = bi; 52 | } 53 | 54 | bi = str.indexOf(b, i + 1); 55 | } 56 | 57 | i = ai < bi && ai >= 0 ? ai : bi; 58 | } 59 | 60 | if (begs.length) { 61 | result = [ left, right ]; 62 | } 63 | } 64 | 65 | return result; 66 | } 67 | 68 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 69 | return __REQUIRE__(1615362415625); 70 | })() 71 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/base64-js/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415626, function(require, module, exports) { 8 | 9 | 10 | exports.byteLength = byteLength 11 | exports.toByteArray = toByteArray 12 | exports.fromByteArray = fromByteArray 13 | 14 | var lookup = [] 15 | var revLookup = [] 16 | var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array 17 | 18 | var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' 19 | for (var i = 0, len = code.length; i < len; ++i) { 20 | lookup[i] = code[i] 21 | revLookup[code.charCodeAt(i)] = i 22 | } 23 | 24 | // Support decoding URL-safe base64 strings, as Node.js does. 25 | // See: https://en.wikipedia.org/wiki/Base64#URL_applications 26 | revLookup['-'.charCodeAt(0)] = 62 27 | revLookup['_'.charCodeAt(0)] = 63 28 | 29 | function getLens (b64) { 30 | var len = b64.length 31 | 32 | if (len % 4 > 0) { 33 | throw new Error('Invalid string. Length must be a multiple of 4') 34 | } 35 | 36 | // Trim off extra bytes after placeholder bytes are found 37 | // See: https://github.com/beatgammit/base64-js/issues/42 38 | var validLen = b64.indexOf('=') 39 | if (validLen === -1) validLen = len 40 | 41 | var placeHoldersLen = validLen === len 42 | ? 0 43 | : 4 - (validLen % 4) 44 | 45 | return [validLen, placeHoldersLen] 46 | } 47 | 48 | // base64 is 4/3 + up to two characters of the original data 49 | function byteLength (b64) { 50 | var lens = getLens(b64) 51 | var validLen = lens[0] 52 | var placeHoldersLen = lens[1] 53 | return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen 54 | } 55 | 56 | function _byteLength (b64, validLen, placeHoldersLen) { 57 | return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen 58 | } 59 | 60 | function toByteArray (b64) { 61 | var tmp 62 | var lens = getLens(b64) 63 | var validLen = lens[0] 64 | var placeHoldersLen = lens[1] 65 | 66 | var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) 67 | 68 | var curByte = 0 69 | 70 | // if there are placeholders, only get up to the last complete 4 chars 71 | var len = placeHoldersLen > 0 72 | ? validLen - 4 73 | : validLen 74 | 75 | var i 76 | for (i = 0; i < len; i += 4) { 77 | tmp = 78 | (revLookup[b64.charCodeAt(i)] << 18) | 79 | (revLookup[b64.charCodeAt(i + 1)] << 12) | 80 | (revLookup[b64.charCodeAt(i + 2)] << 6) | 81 | revLookup[b64.charCodeAt(i + 3)] 82 | arr[curByte++] = (tmp >> 16) & 0xFF 83 | arr[curByte++] = (tmp >> 8) & 0xFF 84 | arr[curByte++] = tmp & 0xFF 85 | } 86 | 87 | if (placeHoldersLen === 2) { 88 | tmp = 89 | (revLookup[b64.charCodeAt(i)] << 2) | 90 | (revLookup[b64.charCodeAt(i + 1)] >> 4) 91 | arr[curByte++] = tmp & 0xFF 92 | } 93 | 94 | if (placeHoldersLen === 1) { 95 | tmp = 96 | (revLookup[b64.charCodeAt(i)] << 10) | 97 | (revLookup[b64.charCodeAt(i + 1)] << 4) | 98 | (revLookup[b64.charCodeAt(i + 2)] >> 2) 99 | arr[curByte++] = (tmp >> 8) & 0xFF 100 | arr[curByte++] = tmp & 0xFF 101 | } 102 | 103 | return arr 104 | } 105 | 106 | function tripletToBase64 (num) { 107 | return lookup[num >> 18 & 0x3F] + 108 | lookup[num >> 12 & 0x3F] + 109 | lookup[num >> 6 & 0x3F] + 110 | lookup[num & 0x3F] 111 | } 112 | 113 | function encodeChunk (uint8, start, end) { 114 | var tmp 115 | var output = [] 116 | for (var i = start; i < end; i += 3) { 117 | tmp = 118 | ((uint8[i] << 16) & 0xFF0000) + 119 | ((uint8[i + 1] << 8) & 0xFF00) + 120 | (uint8[i + 2] & 0xFF) 121 | output.push(tripletToBase64(tmp)) 122 | } 123 | return output.join('') 124 | } 125 | 126 | function fromByteArray (uint8) { 127 | var tmp 128 | var len = uint8.length 129 | var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes 130 | var parts = [] 131 | var maxChunkLength = 16383 // must be multiple of 3 132 | 133 | // go through the array every three bytes, we'll deal with trailing stuff later 134 | for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { 135 | parts.push(encodeChunk( 136 | uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength) 137 | )) 138 | } 139 | 140 | // pad the end with zeros, but make sure to not forget the extra bytes 141 | if (extraBytes === 1) { 142 | tmp = uint8[len - 1] 143 | parts.push( 144 | lookup[tmp >> 2] + 145 | lookup[(tmp << 4) & 0x3F] + 146 | '==' 147 | ) 148 | } else if (extraBytes === 2) { 149 | tmp = (uint8[len - 2] << 8) + uint8[len - 1] 150 | parts.push( 151 | lookup[tmp >> 10] + 152 | lookup[(tmp >> 4) & 0x3F] + 153 | lookup[(tmp << 2) & 0x3F] + 154 | '=' 155 | ) 156 | } 157 | 158 | return parts.join('') 159 | } 160 | 161 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 162 | return __REQUIRE__(1615362415626); 163 | })() 164 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/brace-expansion/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415627, function(require, module, exports) { 8 | var concatMap = require('concat-map'); 9 | var balanced = require('balanced-match'); 10 | 11 | module.exports = expandTop; 12 | 13 | var escSlash = '\0SLASH'+Math.random()+'\0'; 14 | var escOpen = '\0OPEN'+Math.random()+'\0'; 15 | var escClose = '\0CLOSE'+Math.random()+'\0'; 16 | var escComma = '\0COMMA'+Math.random()+'\0'; 17 | var escPeriod = '\0PERIOD'+Math.random()+'\0'; 18 | 19 | function numeric(str) { 20 | return parseInt(str, 10) == str 21 | ? parseInt(str, 10) 22 | : str.charCodeAt(0); 23 | } 24 | 25 | function escapeBraces(str) { 26 | return str.split('\\\\').join(escSlash) 27 | .split('\\{').join(escOpen) 28 | .split('\\}').join(escClose) 29 | .split('\\,').join(escComma) 30 | .split('\\.').join(escPeriod); 31 | } 32 | 33 | function unescapeBraces(str) { 34 | return str.split(escSlash).join('\\') 35 | .split(escOpen).join('{') 36 | .split(escClose).join('}') 37 | .split(escComma).join(',') 38 | .split(escPeriod).join('.'); 39 | } 40 | 41 | 42 | // Basically just str.split(","), but handling cases 43 | // where we have nested braced sections, which should be 44 | // treated as individual members, like {a,{b,c},d} 45 | function parseCommaParts(str) { 46 | if (!str) 47 | return ['']; 48 | 49 | var parts = []; 50 | var m = balanced('{', '}', str); 51 | 52 | if (!m) 53 | return str.split(','); 54 | 55 | var pre = m.pre; 56 | var body = m.body; 57 | var post = m.post; 58 | var p = pre.split(','); 59 | 60 | p[p.length-1] += '{' + body + '}'; 61 | var postParts = parseCommaParts(post); 62 | if (post.length) { 63 | p[p.length-1] += postParts.shift(); 64 | p.push.apply(p, postParts); 65 | } 66 | 67 | parts.push.apply(parts, p); 68 | 69 | return parts; 70 | } 71 | 72 | function expandTop(str) { 73 | if (!str) 74 | return []; 75 | 76 | // I don't know why Bash 4.3 does this, but it does. 77 | // Anything starting with {} will have the first two bytes preserved 78 | // but *only* at the top level, so {},a}b will not expand to anything, 79 | // but a{},b}c will be expanded to [a}c,abc]. 80 | // One could argue that this is a bug in Bash, but since the goal of 81 | // this module is to match Bash's rules, we escape a leading {} 82 | if (str.substr(0, 2) === '{}') { 83 | str = '\\{\\}' + str.substr(2); 84 | } 85 | 86 | return expand(escapeBraces(str), true).map(unescapeBraces); 87 | } 88 | 89 | function identity(e) { 90 | return e; 91 | } 92 | 93 | function embrace(str) { 94 | return '{' + str + '}'; 95 | } 96 | function isPadded(el) { 97 | return /^-?0\d/.test(el); 98 | } 99 | 100 | function lte(i, y) { 101 | return i <= y; 102 | } 103 | function gte(i, y) { 104 | return i >= y; 105 | } 106 | 107 | function expand(str, isTop) { 108 | var expansions = []; 109 | 110 | var m = balanced('{', '}', str); 111 | if (!m || /\$$/.test(m.pre)) return [str]; 112 | 113 | var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body); 114 | var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body); 115 | var isSequence = isNumericSequence || isAlphaSequence; 116 | var isOptions = m.body.indexOf(',') >= 0; 117 | if (!isSequence && !isOptions) { 118 | // {a},b} 119 | if (m.post.match(/,.*\}/)) { 120 | str = m.pre + '{' + m.body + escClose + m.post; 121 | return expand(str); 122 | } 123 | return [str]; 124 | } 125 | 126 | var n; 127 | if (isSequence) { 128 | n = m.body.split(/\.\./); 129 | } else { 130 | n = parseCommaParts(m.body); 131 | if (n.length === 1) { 132 | // x{{a,b}}y ==> x{a}y x{b}y 133 | n = expand(n[0], false).map(embrace); 134 | if (n.length === 1) { 135 | var post = m.post.length 136 | ? expand(m.post, false) 137 | : ['']; 138 | return post.map(function(p) { 139 | return m.pre + n[0] + p; 140 | }); 141 | } 142 | } 143 | } 144 | 145 | // at this point, n is the parts, and we know it's not a comma set 146 | // with a single entry. 147 | 148 | // no need to expand pre, since it is guaranteed to be free of brace-sets 149 | var pre = m.pre; 150 | var post = m.post.length 151 | ? expand(m.post, false) 152 | : ['']; 153 | 154 | var N; 155 | 156 | if (isSequence) { 157 | var x = numeric(n[0]); 158 | var y = numeric(n[1]); 159 | var width = Math.max(n[0].length, n[1].length) 160 | var incr = n.length == 3 161 | ? Math.abs(numeric(n[2])) 162 | : 1; 163 | var test = lte; 164 | var reverse = y < x; 165 | if (reverse) { 166 | incr *= -1; 167 | test = gte; 168 | } 169 | var pad = n.some(isPadded); 170 | 171 | N = []; 172 | 173 | for (var i = x; test(i, y); i += incr) { 174 | var c; 175 | if (isAlphaSequence) { 176 | c = String.fromCharCode(i); 177 | if (c === '\\') 178 | c = ''; 179 | } else { 180 | c = String(i); 181 | if (pad) { 182 | var need = width - c.length; 183 | if (need > 0) { 184 | var z = new Array(need + 1).join('0'); 185 | if (i < 0) 186 | c = '-' + z + c.slice(1); 187 | else 188 | c = z + c; 189 | } 190 | } 191 | } 192 | N.push(c); 193 | } 194 | } else { 195 | N = concatMap(n, function(el) { return expand(el, false) }); 196 | } 197 | 198 | for (var j = 0; j < N.length; j++) { 199 | for (var k = 0; k < post.length; k++) { 200 | var expansion = pre + N[j] + post[k]; 201 | if (!isTop || isSequence || expansion) 202 | expansions.push(expansion); 203 | } 204 | } 205 | 206 | return expansions; 207 | } 208 | 209 | 210 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 211 | return __REQUIRE__(1615362415627); 212 | })() 213 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/concat-map/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415628, function(require, module, exports) { 8 | module.exports = function (xs, fn) { 9 | var res = []; 10 | for (var i = 0; i < xs.length; i++) { 11 | var x = fn(xs[i], i); 12 | if (isArray(x)) res.push.apply(res, x); 13 | else res.push(x); 14 | } 15 | return res; 16 | }; 17 | 18 | var isArray = Array.isArray || function (xs) { 19 | return Object.prototype.toString.call(xs) === '[object Array]'; 20 | }; 21 | 22 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 23 | return __REQUIRE__(1615362415628); 24 | })() 25 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/fetch-wechat/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415629, function(require, module, exports) { 8 | 9 | Object.defineProperty(exports, "__esModule", { value: true }); 10 | exports.TEXT_FILE_EXTS = /\.(txt|json|html|txt|csv)/; 11 | function parseResponse(url, res) { 12 | var header = res.header || {}; 13 | header = Object.keys(header).reduce(function (map, key) { 14 | map[key.toLowerCase()] = header[key]; 15 | return map; 16 | }, {}); 17 | return { 18 | ok: ((res.statusCode / 200) | 0) === 1, 19 | status: res.statusCode, 20 | statusText: res.statusCode, 21 | url: url, 22 | clone: function () { return parseResponse(url, res); }, 23 | text: function () { 24 | return Promise.resolve(typeof res.data === 'string' ? res.data : JSON.stringify(res.data)); 25 | }, 26 | json: function () { 27 | if (typeof res.data === 'object') 28 | return Promise.resolve(res.data); 29 | var json = {}; 30 | try { 31 | json = JSON.parse(res.data); 32 | } 33 | catch (err) { 34 | console.error(err); 35 | } 36 | return Promise.resolve(json); 37 | }, 38 | arrayBuffer: function () { 39 | return Promise.resolve(res.data); 40 | }, 41 | headers: { 42 | keys: function () { return Object.keys(header); }, 43 | entries: function () { 44 | var all = []; 45 | for (var key in header) { 46 | if (header.hasOwnProperty(key)) { 47 | all.push([key, header[key]]); 48 | } 49 | } 50 | return all; 51 | }, 52 | get: function (n) { return header[n.toLowerCase()]; }, 53 | has: function (n) { return n.toLowerCase() in header; } 54 | } 55 | }; 56 | } 57 | exports.parseResponse = parseResponse; 58 | function fetchFunc() { 59 | // tslint:disable-next-line:no-any 60 | return function (url, options) { 61 | options = options || {}; 62 | var dataType = url.match(exports.TEXT_FILE_EXTS) ? 'text' : 'arraybuffer'; 63 | return new Promise(function (resolve, reject) { 64 | wx.request({ 65 | url: url, 66 | method: options.method || 'GET', 67 | data: options.body, 68 | header: options.headers, 69 | dataType: dataType, 70 | responseType: dataType, 71 | success: function (resp) { return resolve(parseResponse(url, resp)); }, 72 | fail: function (err) { return reject(err); } 73 | }); 74 | }); 75 | }; 76 | } 77 | exports.fetchFunc = fetchFunc; 78 | function setWechatFetch(debug) { 79 | if (debug === void 0) { debug = false; } 80 | // tslint:disable-next-line:no-any 81 | var typedGlobal = global; 82 | if (typeof typedGlobal.fetch !== 'function') { 83 | if (debug) { 84 | console.log('setup global fetch...'); 85 | } 86 | typedGlobal.fetch = fetchFunc(); 87 | } 88 | } 89 | exports.setWechatFetch = setWechatFetch; 90 | //# sourceMappingURL=index.js.map 91 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 92 | return __REQUIRE__(1615362415629); 93 | })() 94 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/fs.realpath/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415630, function(require, module, exports) { 8 | module.exports = realpath 9 | realpath.realpath = realpath 10 | realpath.sync = realpathSync 11 | realpath.realpathSync = realpathSync 12 | realpath.monkeypatch = monkeypatch 13 | realpath.unmonkeypatch = unmonkeypatch 14 | 15 | var fs = require('fs') 16 | var origRealpath = fs.realpath 17 | var origRealpathSync = fs.realpathSync 18 | 19 | var version = process.version 20 | var ok = /^v[0-5]\./.test(version) 21 | var old = require('./old.js') 22 | 23 | function newError (er) { 24 | return er && er.syscall === 'realpath' && ( 25 | er.code === 'ELOOP' || 26 | er.code === 'ENOMEM' || 27 | er.code === 'ENAMETOOLONG' 28 | ) 29 | } 30 | 31 | function realpath (p, cache, cb) { 32 | if (ok) { 33 | return origRealpath(p, cache, cb) 34 | } 35 | 36 | if (typeof cache === 'function') { 37 | cb = cache 38 | cache = null 39 | } 40 | origRealpath(p, cache, function (er, result) { 41 | if (newError(er)) { 42 | old.realpath(p, cache, cb) 43 | } else { 44 | cb(er, result) 45 | } 46 | }) 47 | } 48 | 49 | function realpathSync (p, cache) { 50 | if (ok) { 51 | return origRealpathSync(p, cache) 52 | } 53 | 54 | try { 55 | return origRealpathSync(p, cache) 56 | } catch (er) { 57 | if (newError(er)) { 58 | return old.realpathSync(p, cache) 59 | } else { 60 | throw er 61 | } 62 | } 63 | } 64 | 65 | function monkeypatch () { 66 | fs.realpath = realpath 67 | fs.realpathSync = realpathSync 68 | } 69 | 70 | function unmonkeypatch () { 71 | fs.realpath = origRealpath 72 | fs.realpathSync = origRealpathSync 73 | } 74 | 75 | }, function(modId) {var map = {"./old.js":1615362415631}; return __REQUIRE__(map[modId], modId); }) 76 | __DEFINE__(1615362415631, function(require, module, exports) { 77 | // Copyright Joyent, Inc. and other Node contributors. 78 | // 79 | // Permission is hereby granted, free of charge, to any person obtaining a 80 | // copy of this software and associated documentation files (the 81 | // "Software"), to deal in the Software without restriction, including 82 | // without limitation the rights to use, copy, modify, merge, publish, 83 | // distribute, sublicense, and/or sell copies of the Software, and to permit 84 | // persons to whom the Software is furnished to do so, subject to the 85 | // following conditions: 86 | // 87 | // The above copyright notice and this permission notice shall be included 88 | // in all copies or substantial portions of the Software. 89 | // 90 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 91 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 92 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 93 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 94 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 95 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 96 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 97 | 98 | var pathModule = require('path'); 99 | var isWindows = process.platform === 'win32'; 100 | var fs = require('fs'); 101 | 102 | // JavaScript implementation of realpath, ported from node pre-v6 103 | 104 | var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); 105 | 106 | function rethrow() { 107 | // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and 108 | // is fairly slow to generate. 109 | var callback; 110 | if (DEBUG) { 111 | var backtrace = new Error; 112 | callback = debugCallback; 113 | } else 114 | callback = missingCallback; 115 | 116 | return callback; 117 | 118 | function debugCallback(err) { 119 | if (err) { 120 | backtrace.message = err.message; 121 | err = backtrace; 122 | missingCallback(err); 123 | } 124 | } 125 | 126 | function missingCallback(err) { 127 | if (err) { 128 | if (process.throwDeprecation) 129 | throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs 130 | else if (!process.noDeprecation) { 131 | var msg = 'fs: missing callback ' + (err.stack || err.message); 132 | if (process.traceDeprecation) 133 | console.trace(msg); 134 | else 135 | console.error(msg); 136 | } 137 | } 138 | } 139 | } 140 | 141 | function maybeCallback(cb) { 142 | return typeof cb === 'function' ? cb : rethrow(); 143 | } 144 | 145 | var normalize = pathModule.normalize; 146 | 147 | // Regexp that finds the next partion of a (partial) path 148 | // result is [base_with_slash, base], e.g. ['somedir/', 'somedir'] 149 | if (isWindows) { 150 | var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; 151 | } else { 152 | var nextPartRe = /(.*?)(?:[\/]+|$)/g; 153 | } 154 | 155 | // Regex to find the device root, including trailing slash. E.g. 'c:\\'. 156 | if (isWindows) { 157 | var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; 158 | } else { 159 | var splitRootRe = /^[\/]*/; 160 | } 161 | 162 | exports.realpathSync = function realpathSync(p, cache) { 163 | // make p is absolute 164 | p = pathModule.resolve(p); 165 | 166 | if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { 167 | return cache[p]; 168 | } 169 | 170 | var original = p, 171 | seenLinks = {}, 172 | knownHard = {}; 173 | 174 | // current character position in p 175 | var pos; 176 | // the partial path so far, including a trailing slash if any 177 | var current; 178 | // the partial path without a trailing slash (except when pointing at a root) 179 | var base; 180 | // the partial path scanned in the previous round, with slash 181 | var previous; 182 | 183 | start(); 184 | 185 | function start() { 186 | // Skip over roots 187 | var m = splitRootRe.exec(p); 188 | pos = m[0].length; 189 | current = m[0]; 190 | base = m[0]; 191 | previous = ''; 192 | 193 | // On windows, check that the root exists. On unix there is no need. 194 | if (isWindows && !knownHard[base]) { 195 | fs.lstatSync(base); 196 | knownHard[base] = true; 197 | } 198 | } 199 | 200 | // walk down the path, swapping out linked pathparts for their real 201 | // values 202 | // NB: p.length changes. 203 | while (pos < p.length) { 204 | // find the next part 205 | nextPartRe.lastIndex = pos; 206 | var result = nextPartRe.exec(p); 207 | previous = current; 208 | current += result[0]; 209 | base = previous + result[1]; 210 | pos = nextPartRe.lastIndex; 211 | 212 | // continue if not a symlink 213 | if (knownHard[base] || (cache && cache[base] === base)) { 214 | continue; 215 | } 216 | 217 | var resolvedLink; 218 | if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { 219 | // some known symbolic link. no need to stat again. 220 | resolvedLink = cache[base]; 221 | } else { 222 | var stat = fs.lstatSync(base); 223 | if (!stat.isSymbolicLink()) { 224 | knownHard[base] = true; 225 | if (cache) cache[base] = base; 226 | continue; 227 | } 228 | 229 | // read the link if it wasn't read before 230 | // dev/ino always return 0 on windows, so skip the check. 231 | var linkTarget = null; 232 | if (!isWindows) { 233 | var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); 234 | if (seenLinks.hasOwnProperty(id)) { 235 | linkTarget = seenLinks[id]; 236 | } 237 | } 238 | if (linkTarget === null) { 239 | fs.statSync(base); 240 | linkTarget = fs.readlinkSync(base); 241 | } 242 | resolvedLink = pathModule.resolve(previous, linkTarget); 243 | // track this, if given a cache. 244 | if (cache) cache[base] = resolvedLink; 245 | if (!isWindows) seenLinks[id] = linkTarget; 246 | } 247 | 248 | // resolve the link, then start over 249 | p = pathModule.resolve(resolvedLink, p.slice(pos)); 250 | start(); 251 | } 252 | 253 | if (cache) cache[original] = p; 254 | 255 | return p; 256 | }; 257 | 258 | 259 | exports.realpath = function realpath(p, cache, cb) { 260 | if (typeof cb !== 'function') { 261 | cb = maybeCallback(cache); 262 | cache = null; 263 | } 264 | 265 | // make p is absolute 266 | p = pathModule.resolve(p); 267 | 268 | if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { 269 | return process.nextTick(cb.bind(null, null, cache[p])); 270 | } 271 | 272 | var original = p, 273 | seenLinks = {}, 274 | knownHard = {}; 275 | 276 | // current character position in p 277 | var pos; 278 | // the partial path so far, including a trailing slash if any 279 | var current; 280 | // the partial path without a trailing slash (except when pointing at a root) 281 | var base; 282 | // the partial path scanned in the previous round, with slash 283 | var previous; 284 | 285 | start(); 286 | 287 | function start() { 288 | // Skip over roots 289 | var m = splitRootRe.exec(p); 290 | pos = m[0].length; 291 | current = m[0]; 292 | base = m[0]; 293 | previous = ''; 294 | 295 | // On windows, check that the root exists. On unix there is no need. 296 | if (isWindows && !knownHard[base]) { 297 | fs.lstat(base, function(err) { 298 | if (err) return cb(err); 299 | knownHard[base] = true; 300 | LOOP(); 301 | }); 302 | } else { 303 | process.nextTick(LOOP); 304 | } 305 | } 306 | 307 | // walk down the path, swapping out linked pathparts for their real 308 | // values 309 | function LOOP() { 310 | // stop if scanned past end of path 311 | if (pos >= p.length) { 312 | if (cache) cache[original] = p; 313 | return cb(null, p); 314 | } 315 | 316 | // find the next part 317 | nextPartRe.lastIndex = pos; 318 | var result = nextPartRe.exec(p); 319 | previous = current; 320 | current += result[0]; 321 | base = previous + result[1]; 322 | pos = nextPartRe.lastIndex; 323 | 324 | // continue if not a symlink 325 | if (knownHard[base] || (cache && cache[base] === base)) { 326 | return process.nextTick(LOOP); 327 | } 328 | 329 | if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { 330 | // known symbolic link. no need to stat again. 331 | return gotResolvedLink(cache[base]); 332 | } 333 | 334 | return fs.lstat(base, gotStat); 335 | } 336 | 337 | function gotStat(err, stat) { 338 | if (err) return cb(err); 339 | 340 | // if not a symlink, skip to the next path part 341 | if (!stat.isSymbolicLink()) { 342 | knownHard[base] = true; 343 | if (cache) cache[base] = base; 344 | return process.nextTick(LOOP); 345 | } 346 | 347 | // stat & read the link if not read before 348 | // call gotTarget as soon as the link target is known 349 | // dev/ino always return 0 on windows, so skip the check. 350 | if (!isWindows) { 351 | var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); 352 | if (seenLinks.hasOwnProperty(id)) { 353 | return gotTarget(null, seenLinks[id], base); 354 | } 355 | } 356 | fs.stat(base, function(err) { 357 | if (err) return cb(err); 358 | 359 | fs.readlink(base, function(err, target) { 360 | if (!isWindows) seenLinks[id] = target; 361 | gotTarget(err, target); 362 | }); 363 | }); 364 | } 365 | 366 | function gotTarget(err, target, base) { 367 | if (err) return cb(err); 368 | 369 | var resolvedLink = pathModule.resolve(previous, target); 370 | if (cache) cache[base] = resolvedLink; 371 | gotResolvedLink(resolvedLink); 372 | } 373 | 374 | function gotResolvedLink(resolvedLink) { 375 | // resolve the link, then start over 376 | p = pathModule.resolve(resolvedLink, p.slice(pos)); 377 | start(); 378 | } 379 | }; 380 | 381 | }, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); }) 382 | return __REQUIRE__(1615362415630); 383 | })() 384 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/inflight/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415635, function(require, module, exports) { 8 | var wrappy = require('wrappy') 9 | var reqs = Object.create(null) 10 | var once = require('once') 11 | 12 | module.exports = wrappy(inflight) 13 | 14 | function inflight (key, cb) { 15 | if (reqs[key]) { 16 | reqs[key].push(cb) 17 | return null 18 | } else { 19 | reqs[key] = [cb] 20 | return makeres(key) 21 | } 22 | } 23 | 24 | function makeres (key) { 25 | return once(function RES () { 26 | var cbs = reqs[key] 27 | var len = cbs.length 28 | var args = slice(arguments) 29 | 30 | // XXX It's somewhat ambiguous whether a new callback added in this 31 | // pass should be queued for later execution if something in the 32 | // list of callbacks throws, or if it should just be discarded. 33 | // However, it's such an edge case that it hardly matters, and either 34 | // choice is likely as surprising as the other. 35 | // As it happens, we do go ahead and schedule it for later execution. 36 | try { 37 | for (var i = 0; i < len; i++) { 38 | cbs[i].apply(null, args) 39 | } 40 | } finally { 41 | if (cbs.length > len) { 42 | // added more in the interim. 43 | // de-zalgo, just in case, but don't call again. 44 | cbs.splice(0, len) 45 | process.nextTick(function () { 46 | RES.apply(null, args) 47 | }) 48 | } else { 49 | delete reqs[key] 50 | } 51 | } 52 | }) 53 | } 54 | 55 | function slice (args) { 56 | var length = args.length 57 | var array = [] 58 | 59 | for (var i = 0; i < length; i++) array[i] = args[i] 60 | return array 61 | } 62 | 63 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 64 | return __REQUIRE__(1615362415635); 65 | })() 66 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/inherits/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415636, function(require, module, exports) { 8 | try { 9 | var util = require('util'); 10 | /* istanbul ignore next */ 11 | if (typeof util.inherits !== 'function') throw ''; 12 | module.exports = util.inherits; 13 | } catch (e) { 14 | /* istanbul ignore next */ 15 | module.exports = require('./inherits_browser.js'); 16 | } 17 | 18 | }, function(modId) {var map = {"./inherits_browser.js":1615362415637}; return __REQUIRE__(map[modId], modId); }) 19 | __DEFINE__(1615362415637, function(require, module, exports) { 20 | if (typeof Object.create === 'function') { 21 | // implementation from standard node.js 'util' module 22 | module.exports = function inherits(ctor, superCtor) { 23 | if (superCtor) { 24 | ctor.super_ = superCtor 25 | ctor.prototype = Object.create(superCtor.prototype, { 26 | constructor: { 27 | value: ctor, 28 | enumerable: false, 29 | writable: true, 30 | configurable: true 31 | } 32 | }) 33 | } 34 | }; 35 | } else { 36 | // old school shim for old browsers 37 | module.exports = function inherits(ctor, superCtor) { 38 | if (superCtor) { 39 | ctor.super_ = superCtor 40 | var TempCtor = function () {} 41 | TempCtor.prototype = superCtor.prototype 42 | ctor.prototype = new TempCtor() 43 | ctor.prototype.constructor = ctor 44 | } 45 | } 46 | } 47 | 48 | }, function(modId) { var map = {}; return __REQUIRE__(map[modId], modId); }) 49 | return __REQUIRE__(1615362415636); 50 | })() 51 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/once/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415640, function(require, module, exports) { 8 | var wrappy = require('wrappy') 9 | module.exports = wrappy(once) 10 | module.exports.strict = wrappy(onceStrict) 11 | 12 | once.proto = once(function () { 13 | Object.defineProperty(Function.prototype, 'once', { 14 | value: function () { 15 | return once(this) 16 | }, 17 | configurable: true 18 | }) 19 | 20 | Object.defineProperty(Function.prototype, 'onceStrict', { 21 | value: function () { 22 | return onceStrict(this) 23 | }, 24 | configurable: true 25 | }) 26 | }) 27 | 28 | function once (fn) { 29 | var f = function () { 30 | if (f.called) return f.value 31 | f.called = true 32 | return f.value = fn.apply(this, arguments) 33 | } 34 | f.called = false 35 | return f 36 | } 37 | 38 | function onceStrict (fn) { 39 | var f = function () { 40 | if (f.called) 41 | throw new Error(f.onceError) 42 | f.called = true 43 | return f.value = fn.apply(this, arguments) 44 | } 45 | var name = fn.name || 'Function wrapped with `once`' 46 | f.onceError = name + " shouldn't be called more than once" 47 | f.called = false 48 | return f 49 | } 50 | 51 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 52 | return __REQUIRE__(1615362415640); 53 | })() 54 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/path-is-absolute/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415641, function(require, module, exports) { 8 | 9 | 10 | function posix(path) { 11 | return path.charAt(0) === '/'; 12 | } 13 | 14 | function win32(path) { 15 | // https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56 16 | var splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; 17 | var result = splitDeviceRe.exec(path); 18 | var device = result[1] || ''; 19 | var isUnc = Boolean(device && device.charAt(1) !== ':'); 20 | 21 | // UNC paths are always absolute 22 | return Boolean(result[2] || isUnc); 23 | } 24 | 25 | module.exports = process.platform === 'win32' ? win32 : posix; 26 | module.exports.posix = posix; 27 | module.exports.win32 = win32; 28 | 29 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 30 | return __REQUIRE__(1615362415641); 31 | })() 32 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/rimraf/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415642, function(require, module, exports) { 8 | const assert = require("assert") 9 | const path = require("path") 10 | const fs = require("fs") 11 | let glob = undefined 12 | try { 13 | glob = require("glob") 14 | } catch (_err) { 15 | // treat glob as optional. 16 | } 17 | 18 | const defaultGlobOpts = { 19 | nosort: true, 20 | silent: true 21 | } 22 | 23 | // for EMFILE handling 24 | let timeout = 0 25 | 26 | const isWindows = (process.platform === "win32") 27 | 28 | const defaults = options => { 29 | const methods = [ 30 | 'unlink', 31 | 'chmod', 32 | 'stat', 33 | 'lstat', 34 | 'rmdir', 35 | 'readdir' 36 | ] 37 | methods.forEach(m => { 38 | options[m] = options[m] || fs[m] 39 | m = m + 'Sync' 40 | options[m] = options[m] || fs[m] 41 | }) 42 | 43 | options.maxBusyTries = options.maxBusyTries || 3 44 | options.emfileWait = options.emfileWait || 1000 45 | if (options.glob === false) { 46 | options.disableGlob = true 47 | } 48 | if (options.disableGlob !== true && glob === undefined) { 49 | throw Error('glob dependency not found, set `options.disableGlob = true` if intentional') 50 | } 51 | options.disableGlob = options.disableGlob || false 52 | options.glob = options.glob || defaultGlobOpts 53 | } 54 | 55 | const rimraf = (p, options, cb) => { 56 | if (typeof options === 'function') { 57 | cb = options 58 | options = {} 59 | } 60 | 61 | assert(p, 'rimraf: missing path') 62 | assert.equal(typeof p, 'string', 'rimraf: path should be a string') 63 | assert.equal(typeof cb, 'function', 'rimraf: callback function required') 64 | assert(options, 'rimraf: invalid options argument provided') 65 | assert.equal(typeof options, 'object', 'rimraf: options should be object') 66 | 67 | defaults(options) 68 | 69 | let busyTries = 0 70 | let errState = null 71 | let n = 0 72 | 73 | const next = (er) => { 74 | errState = errState || er 75 | if (--n === 0) 76 | cb(errState) 77 | } 78 | 79 | const afterGlob = (er, results) => { 80 | if (er) 81 | return cb(er) 82 | 83 | n = results.length 84 | if (n === 0) 85 | return cb() 86 | 87 | results.forEach(p => { 88 | const CB = (er) => { 89 | if (er) { 90 | if ((er.code === "EBUSY" || er.code === "ENOTEMPTY" || er.code === "EPERM") && 91 | busyTries < options.maxBusyTries) { 92 | busyTries ++ 93 | // try again, with the same exact callback as this one. 94 | return setTimeout(() => rimraf_(p, options, CB), busyTries * 100) 95 | } 96 | 97 | // this one won't happen if graceful-fs is used. 98 | if (er.code === "EMFILE" && timeout < options.emfileWait) { 99 | return setTimeout(() => rimraf_(p, options, CB), timeout ++) 100 | } 101 | 102 | // already gone 103 | if (er.code === "ENOENT") er = null 104 | } 105 | 106 | timeout = 0 107 | next(er) 108 | } 109 | rimraf_(p, options, CB) 110 | }) 111 | } 112 | 113 | if (options.disableGlob || !glob.hasMagic(p)) 114 | return afterGlob(null, [p]) 115 | 116 | options.lstat(p, (er, stat) => { 117 | if (!er) 118 | return afterGlob(null, [p]) 119 | 120 | glob(p, options.glob, afterGlob) 121 | }) 122 | 123 | } 124 | 125 | // Two possible strategies. 126 | // 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR 127 | // 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR 128 | // 129 | // Both result in an extra syscall when you guess wrong. However, there 130 | // are likely far more normal files in the world than directories. This 131 | // is based on the assumption that a the average number of files per 132 | // directory is >= 1. 133 | // 134 | // If anyone ever complains about this, then I guess the strategy could 135 | // be made configurable somehow. But until then, YAGNI. 136 | const rimraf_ = (p, options, cb) => { 137 | assert(p) 138 | assert(options) 139 | assert(typeof cb === 'function') 140 | 141 | // sunos lets the root user unlink directories, which is... weird. 142 | // so we have to lstat here and make sure it's not a dir. 143 | options.lstat(p, (er, st) => { 144 | if (er && er.code === "ENOENT") 145 | return cb(null) 146 | 147 | // Windows can EPERM on stat. Life is suffering. 148 | if (er && er.code === "EPERM" && isWindows) 149 | fixWinEPERM(p, options, er, cb) 150 | 151 | if (st && st.isDirectory()) 152 | return rmdir(p, options, er, cb) 153 | 154 | options.unlink(p, er => { 155 | if (er) { 156 | if (er.code === "ENOENT") 157 | return cb(null) 158 | if (er.code === "EPERM") 159 | return (isWindows) 160 | ? fixWinEPERM(p, options, er, cb) 161 | : rmdir(p, options, er, cb) 162 | if (er.code === "EISDIR") 163 | return rmdir(p, options, er, cb) 164 | } 165 | return cb(er) 166 | }) 167 | }) 168 | } 169 | 170 | const fixWinEPERM = (p, options, er, cb) => { 171 | assert(p) 172 | assert(options) 173 | assert(typeof cb === 'function') 174 | 175 | options.chmod(p, 0o666, er2 => { 176 | if (er2) 177 | cb(er2.code === "ENOENT" ? null : er) 178 | else 179 | options.stat(p, (er3, stats) => { 180 | if (er3) 181 | cb(er3.code === "ENOENT" ? null : er) 182 | else if (stats.isDirectory()) 183 | rmdir(p, options, er, cb) 184 | else 185 | options.unlink(p, cb) 186 | }) 187 | }) 188 | } 189 | 190 | const fixWinEPERMSync = (p, options, er) => { 191 | assert(p) 192 | assert(options) 193 | 194 | try { 195 | options.chmodSync(p, 0o666) 196 | } catch (er2) { 197 | if (er2.code === "ENOENT") 198 | return 199 | else 200 | throw er 201 | } 202 | 203 | let stats 204 | try { 205 | stats = options.statSync(p) 206 | } catch (er3) { 207 | if (er3.code === "ENOENT") 208 | return 209 | else 210 | throw er 211 | } 212 | 213 | if (stats.isDirectory()) 214 | rmdirSync(p, options, er) 215 | else 216 | options.unlinkSync(p) 217 | } 218 | 219 | const rmdir = (p, options, originalEr, cb) => { 220 | assert(p) 221 | assert(options) 222 | assert(typeof cb === 'function') 223 | 224 | // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS) 225 | // if we guessed wrong, and it's not a directory, then 226 | // raise the original error. 227 | options.rmdir(p, er => { 228 | if (er && (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM")) 229 | rmkids(p, options, cb) 230 | else if (er && er.code === "ENOTDIR") 231 | cb(originalEr) 232 | else 233 | cb(er) 234 | }) 235 | } 236 | 237 | const rmkids = (p, options, cb) => { 238 | assert(p) 239 | assert(options) 240 | assert(typeof cb === 'function') 241 | 242 | options.readdir(p, (er, files) => { 243 | if (er) 244 | return cb(er) 245 | let n = files.length 246 | if (n === 0) 247 | return options.rmdir(p, cb) 248 | let errState 249 | files.forEach(f => { 250 | rimraf(path.join(p, f), options, er => { 251 | if (errState) 252 | return 253 | if (er) 254 | return cb(errState = er) 255 | if (--n === 0) 256 | options.rmdir(p, cb) 257 | }) 258 | }) 259 | }) 260 | } 261 | 262 | // this looks simpler, and is strictly *faster*, but will 263 | // tie up the JavaScript thread and fail on excessively 264 | // deep directory trees. 265 | const rimrafSync = (p, options) => { 266 | options = options || {} 267 | defaults(options) 268 | 269 | assert(p, 'rimraf: missing path') 270 | assert.equal(typeof p, 'string', 'rimraf: path should be a string') 271 | assert(options, 'rimraf: missing options') 272 | assert.equal(typeof options, 'object', 'rimraf: options should be object') 273 | 274 | let results 275 | 276 | if (options.disableGlob || !glob.hasMagic(p)) { 277 | results = [p] 278 | } else { 279 | try { 280 | options.lstatSync(p) 281 | results = [p] 282 | } catch (er) { 283 | results = glob.sync(p, options.glob) 284 | } 285 | } 286 | 287 | if (!results.length) 288 | return 289 | 290 | for (let i = 0; i < results.length; i++) { 291 | const p = results[i] 292 | 293 | let st 294 | try { 295 | st = options.lstatSync(p) 296 | } catch (er) { 297 | if (er.code === "ENOENT") 298 | return 299 | 300 | // Windows can EPERM on stat. Life is suffering. 301 | if (er.code === "EPERM" && isWindows) 302 | fixWinEPERMSync(p, options, er) 303 | } 304 | 305 | try { 306 | // sunos lets the root user unlink directories, which is... weird. 307 | if (st && st.isDirectory()) 308 | rmdirSync(p, options, null) 309 | else 310 | options.unlinkSync(p) 311 | } catch (er) { 312 | if (er.code === "ENOENT") 313 | return 314 | if (er.code === "EPERM") 315 | return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er) 316 | if (er.code !== "EISDIR") 317 | throw er 318 | 319 | rmdirSync(p, options, er) 320 | } 321 | } 322 | } 323 | 324 | const rmdirSync = (p, options, originalEr) => { 325 | assert(p) 326 | assert(options) 327 | 328 | try { 329 | options.rmdirSync(p) 330 | } catch (er) { 331 | if (er.code === "ENOENT") 332 | return 333 | if (er.code === "ENOTDIR") 334 | throw originalEr 335 | if (er.code === "ENOTEMPTY" || er.code === "EEXIST" || er.code === "EPERM") 336 | rmkidsSync(p, options) 337 | } 338 | } 339 | 340 | const rmkidsSync = (p, options) => { 341 | assert(p) 342 | assert(options) 343 | options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options)) 344 | 345 | // We only end up here once we got ENOTEMPTY at least once, and 346 | // at this point, we are guaranteed to have removed all the kids. 347 | // So, we know that it won't be ENOENT or ENOTDIR or anything else. 348 | // try really hard to delete stuff on windows, because it has a 349 | // PROFOUNDLY annoying habit of not closing handles promptly when 350 | // files are deleted, resulting in spurious ENOTEMPTY errors. 351 | const retries = isWindows ? 100 : 1 352 | let i = 0 353 | do { 354 | let threw = true 355 | try { 356 | const ret = options.rmdirSync(p, options) 357 | threw = false 358 | return ret 359 | } finally { 360 | if (++i < retries && threw) 361 | continue 362 | } 363 | } while (true) 364 | } 365 | 366 | module.exports = rimraf 367 | rimraf.sync = rimrafSync 368 | 369 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 370 | return __REQUIRE__(1615362415642); 371 | })() 372 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/text-encoder/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415651, function(require, module, exports) { 8 | var utf8Encodings = [ 9 | 'utf8', 10 | 'utf-8', 11 | 'unicode-1-1-utf-8' 12 | ]; 13 | 14 | function TextEncoder(encoding) { 15 | if (utf8Encodings.indexOf(encoding) < 0 && typeof encoding !== 'undefined' && encoding != null) { 16 | throw new RangeError('Invalid encoding type. Only utf-8 is supported'); 17 | } else { 18 | this.encoding = 'utf-8'; 19 | this.encode = function(str) { 20 | if (typeof str !== 'string') { 21 | throw new TypeError('passed argument must be of tye string'); 22 | } 23 | var binstr = unescape(encodeURIComponent(str)), 24 | arr = new Uint8Array(binstr.length); 25 | const split = binstr.split(''); 26 | for (let i = 0; i < split.length; i++) { 27 | arr[i] = split[i].charCodeAt(0); 28 | } 29 | return arr; 30 | }; 31 | } 32 | } 33 | 34 | function TextDecoder(encoding) { 35 | if (utf8Encodings.indexOf(encoding) < 0 && typeof encoding !== 'undefined' && encoding != null) { 36 | throw new RangeError('Invalid encoding type. Only utf-8 is supported'); 37 | } 38 | else { 39 | this.encoding = 'utf-8'; 40 | this.decode = function (view, options) { 41 | if (typeof view === 'undefined') { 42 | return ''; 43 | } 44 | 45 | var stream = (typeof options !== 'undefined' && stream in options) ? options.stream : false; 46 | if (typeof stream !== 'boolean') { 47 | throw new TypeError('stream option must be boolean'); 48 | } 49 | 50 | if (!ArrayBuffer.isView(view)) { 51 | throw new TypeError('passed argument must be an array buffer view'); 52 | } else { 53 | var arr = new Uint8Array(view.buffer, view.byteOffset, view.byteLength), 54 | charArr = new Array(arr.length); 55 | for (let i = 0; i < arr.length; i++) { 56 | charArr[i] = String.fromCharCode(arr[i]); 57 | } 58 | return decodeURIComponent(escape(charArr.join(''))); 59 | } 60 | } 61 | } 62 | } 63 | 64 | module.exports = { 65 | TextEncoder, 66 | TextDecoder, 67 | }; 68 | 69 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 70 | return __REQUIRE__(1615362415651); 71 | })() 72 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /miniprogram_npm/wrappy/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (function() { 2 | var __MODS__ = {}; 3 | var __DEFINE__ = function(modId, func, req) { var m = { exports: {}, _tempexports: {} }; __MODS__[modId] = { status: 0, func: func, req: req, m: m }; }; 4 | var __REQUIRE__ = function(modId, source) { if(!__MODS__[modId]) return require(source); if(!__MODS__[modId].status) { var m = __MODS__[modId].m; m._exports = m._tempexports; var desp = Object.getOwnPropertyDescriptor(m, "exports"); if (desp && desp.configurable) Object.defineProperty(m, "exports", { set: function (val) { if(typeof val === "object" && val !== m._exports) { m._exports.__proto__ = val.__proto__; Object.keys(val).forEach(function (k) { m._exports[k] = val[k]; }); } m._tempexports = val }, get: function () { return m._tempexports; } }); __MODS__[modId].status = 1; __MODS__[modId].func(__MODS__[modId].req, m, m.exports); } return __MODS__[modId].m.exports; }; 5 | var __REQUIRE_WILDCARD__ = function(obj) { if(obj && obj.__esModule) { return obj; } else { var newObj = {}; if(obj != null) { for(var k in obj) { if (Object.prototype.hasOwnProperty.call(obj, k)) newObj[k] = obj[k]; } } newObj.default = obj; return newObj; } }; 6 | var __REQUIRE_DEFAULT__ = function(obj) { return obj && obj.__esModule ? obj.default : obj; }; 7 | __DEFINE__(1615362415652, function(require, module, exports) { 8 | // Returns a wrapper function that returns a wrapped callback 9 | // The wrapper function should do some stuff, and return a 10 | // presumably different callback function. 11 | // This makes sure that own properties are retained, so that 12 | // decorations and such are not lost along the way. 13 | module.exports = wrappy 14 | function wrappy (fn, cb) { 15 | if (fn && cb) return wrappy(fn)(cb) 16 | 17 | if (typeof fn !== 'function') 18 | throw new TypeError('need wrapper function') 19 | 20 | Object.keys(fn).forEach(function (k) { 21 | wrapper[k] = fn[k] 22 | }) 23 | 24 | return wrapper 25 | 26 | function wrapper() { 27 | var args = new Array(arguments.length) 28 | for (var i = 0; i < args.length; i++) { 29 | args[i] = arguments[i] 30 | } 31 | var ret = fn.apply(this, args) 32 | var cb = args[args.length-1] 33 | if (typeof ret === 'function' && ret !== cb) { 34 | Object.keys(cb).forEach(function (k) { 35 | ret[k] = cb[k] 36 | }) 37 | } 38 | return ret 39 | } 40 | } 41 | 42 | }, function(modId) {var map = {}; return __REQUIRE__(map[modId], modId); }) 43 | return __REQUIRE__(1615362415652); 44 | })() 45 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "red-sanyue-wechat-ar-tfjs", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@tensorflow-models/blazeface": { 8 | "version": "0.0.7", 9 | "resolved": "https://registry.npm.taobao.org/@tensorflow-models/blazeface/download/@tensorflow-models/blazeface-0.0.7.tgz", 10 | "integrity": "sha1-ChZJmgzJf8h+Ea4feBQHMcX2O/I=" 11 | }, 12 | "@tensorflow-models/face-landmarks-detection": { 13 | "version": "0.0.3", 14 | "resolved": "https://registry.npm.taobao.org/@tensorflow-models/face-landmarks-detection/download/@tensorflow-models/face-landmarks-detection-0.0.3.tgz", 15 | "integrity": "sha1-b987SaUHlqzwi36jUPjqWlpF7KE=", 16 | "requires": { 17 | "@tensorflow-models/blazeface": "0.0.7", 18 | "rimraf": "3.0.2" 19 | } 20 | }, 21 | "@tensorflow-models/handpose": { 22 | "version": "0.0.6", 23 | "resolved": "https://registry.nlark.com/@tensorflow-models/handpose/download/@tensorflow-models/handpose-0.0.6.tgz", 24 | "integrity": "sha1-nLODzipyNQGoPQAShBRdmjBwP3c=", 25 | "requires": { 26 | "rimraf": "3.0.2" 27 | } 28 | }, 29 | "@tensorflow/tfjs-backend-cpu": { 30 | "version": "2.1.0", 31 | "resolved": "https://registry.npm.taobao.org/@tensorflow/tfjs-backend-cpu/download/@tensorflow/tfjs-backend-cpu-2.1.0.tgz", 32 | "integrity": "sha1-Bnj/P9aM5L8dRJP0kD777ZteyxA=", 33 | "requires": { 34 | "@types/seedrandom": "2.4.27", 35 | "seedrandom": "2.4.3" 36 | } 37 | }, 38 | "@tensorflow/tfjs-backend-webgl": { 39 | "version": "2.1.0", 40 | "resolved": "https://registry.npm.taobao.org/@tensorflow/tfjs-backend-webgl/download/@tensorflow/tfjs-backend-webgl-2.1.0.tgz", 41 | "integrity": "sha1-9v6wmx4Dmj7IgpVl61lemCkltzQ=", 42 | "requires": { 43 | "@tensorflow/tfjs-backend-cpu": "2.1.0", 44 | "@types/offscreencanvas": "2019.3.0", 45 | "@types/seedrandom": "2.4.27", 46 | "@types/webgl-ext": "0.0.30", 47 | "@types/webgl2": "0.0.4", 48 | "seedrandom": "2.4.3" 49 | } 50 | }, 51 | "@tensorflow/tfjs-converter": { 52 | "version": "2.1.0", 53 | "resolved": "https://registry.npm.taobao.org/@tensorflow/tfjs-converter/download/@tensorflow/tfjs-converter-2.1.0.tgz", 54 | "integrity": "sha1-PdIm8RyrQSzr8LP40rf2m7/umU8=" 55 | }, 56 | "@tensorflow/tfjs-core": { 57 | "version": "2.1.0", 58 | "resolved": "https://registry.npm.taobao.org/@tensorflow/tfjs-core/download/@tensorflow/tfjs-core-2.1.0.tgz", 59 | "integrity": "sha1-dGvj21bHlDuoSAxw91cIStScetE=", 60 | "requires": { 61 | "@types/offscreencanvas": "2019.3.0", 62 | "@types/seedrandom": "2.4.27", 63 | "@types/webgl-ext": "0.0.30", 64 | "@types/webgl2": "0.0.4", 65 | "node-fetch": "2.1.2", 66 | "seedrandom": "2.4.3" 67 | } 68 | }, 69 | "@types/offscreencanvas": { 70 | "version": "2019.3.0", 71 | "resolved": "https://registry.npm.taobao.org/@types/offscreencanvas/download/@types/offscreencanvas-2019.3.0.tgz", 72 | "integrity": "sha1-MzZCjsfpGAz0Vm3+pdoE61hqZVM=" 73 | }, 74 | "@types/seedrandom": { 75 | "version": "2.4.27", 76 | "resolved": "https://registry.npm.taobao.org/@types/seedrandom/download/@types/seedrandom-2.4.27.tgz", 77 | "integrity": "sha1-nbVjk33YaRX2kJK8QyWdL0hXjkE=" 78 | }, 79 | "@types/webgl-ext": { 80 | "version": "0.0.30", 81 | "resolved": "https://registry.npm.taobao.org/@types/webgl-ext/download/@types/webgl-ext-0.0.30.tgz", 82 | "integrity": "sha1-DOSYwWpBoj0VKJ4LhE2UWyXw+50=" 83 | }, 84 | "@types/webgl2": { 85 | "version": "0.0.4", 86 | "resolved": "https://registry.npm.taobao.org/@types/webgl2/download/@types/webgl2-0.0.4.tgz", 87 | "integrity": "sha1-w7D51rRlxmE46E5kyzvfg3PCwnk=" 88 | }, 89 | "abab": { 90 | "version": "2.0.0", 91 | "resolved": "https://registry.npm.taobao.org/abab/download/abab-2.0.0.tgz", 92 | "integrity": "sha1-q6CrTF7uLUx500h9hUUPsjduuw8=" 93 | }, 94 | "balanced-match": { 95 | "version": "1.0.0", 96 | "resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz", 97 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 98 | }, 99 | "base64-js": { 100 | "version": "1.3.1", 101 | "resolved": "https://registry.npm.taobao.org/base64-js/download/base64-js-1.3.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbase64-js%2Fdownload%2Fbase64-js-1.3.1.tgz", 102 | "integrity": "sha1-WOzoy3XdB+ce0IxzarxfrE2/jfE=" 103 | }, 104 | "brace-expansion": { 105 | "version": "1.1.11", 106 | "resolved": "https://registry.npm.taobao.org/brace-expansion/download/brace-expansion-1.1.11.tgz", 107 | "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", 108 | "requires": { 109 | "balanced-match": "1.0.0", 110 | "concat-map": "0.0.1" 111 | } 112 | }, 113 | "concat-map": { 114 | "version": "0.0.1", 115 | "resolved": "https://registry.npm.taobao.org/concat-map/download/concat-map-0.0.1.tgz", 116 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 117 | }, 118 | "fetch-wechat": { 119 | "version": "0.0.3", 120 | "resolved": "https://registry.npm.taobao.org/fetch-wechat/download/fetch-wechat-0.0.3.tgz", 121 | "integrity": "sha1-J4nDMqJL+fQRS1gMAtKTRkbnTxI=" 122 | }, 123 | "fs.realpath": { 124 | "version": "1.0.0", 125 | "resolved": "https://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz", 126 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 127 | }, 128 | "glob": { 129 | "version": "7.1.6", 130 | "resolved": "https://registry.npm.taobao.org/glob/download/glob-7.1.6.tgz", 131 | "integrity": "sha1-FB8zuBp8JJLhJVlDB0gMRmeSeKY=", 132 | "requires": { 133 | "fs.realpath": "1.0.0", 134 | "inflight": "1.0.6", 135 | "inherits": "2.0.4", 136 | "minimatch": "3.0.4", 137 | "once": "1.4.0", 138 | "path-is-absolute": "1.0.1" 139 | } 140 | }, 141 | "inflight": { 142 | "version": "1.0.6", 143 | "resolved": "https://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz", 144 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 145 | "requires": { 146 | "once": "1.4.0", 147 | "wrappy": "1.0.2" 148 | } 149 | }, 150 | "inherits": { 151 | "version": "2.0.4", 152 | "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz", 153 | "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=" 154 | }, 155 | "minimatch": { 156 | "version": "3.0.4", 157 | "resolved": "https://registry.npm.taobao.org/minimatch/download/minimatch-3.0.4.tgz", 158 | "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", 159 | "requires": { 160 | "brace-expansion": "1.1.11" 161 | } 162 | }, 163 | "node-fetch": { 164 | "version": "2.1.2", 165 | "resolved": "https://registry.npm.taobao.org/node-fetch/download/node-fetch-2.1.2.tgz?cache=0&sync_timestamp=1599309667528&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-fetch%2Fdownload%2Fnode-fetch-2.1.2.tgz", 166 | "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" 167 | }, 168 | "once": { 169 | "version": "1.4.0", 170 | "resolved": "https://registry.npm.taobao.org/once/download/once-1.4.0.tgz", 171 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 172 | "requires": { 173 | "wrappy": "1.0.2" 174 | } 175 | }, 176 | "path-is-absolute": { 177 | "version": "1.0.1", 178 | "resolved": "https://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz", 179 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 180 | }, 181 | "rimraf": { 182 | "version": "3.0.2", 183 | "resolved": "https://registry.npm.taobao.org/rimraf/download/rimraf-3.0.2.tgz", 184 | "integrity": "sha1-8aVAK6YiCtUswSgrrBrjqkn9Bho=", 185 | "requires": { 186 | "glob": "7.1.6" 187 | } 188 | }, 189 | "seedrandom": { 190 | "version": "2.4.3", 191 | "resolved": "https://registry.npm.taobao.org/seedrandom/download/seedrandom-2.4.3.tgz", 192 | "integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw=" 193 | }, 194 | "text-encoder": { 195 | "version": "0.0.4", 196 | "resolved": "https://registry.npm.taobao.org/text-encoder/download/text-encoder-0.0.4.tgz", 197 | "integrity": "sha1-++5Mb7JR3QLi9A+Zg93WjFwWbiw=" 198 | }, 199 | "threejs-miniprogram": { 200 | "version": "0.0.2", 201 | "resolved": "https://registry.npm.taobao.org/threejs-miniprogram/download/threejs-miniprogram-0.0.2.tgz", 202 | "integrity": "sha1-XKYB6hXNiuuazLYe5GeS7tq8uTM=" 203 | }, 204 | "wrappy": { 205 | "version": "1.0.2", 206 | "resolved": "https://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz", 207 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "red-sanyue-wechat-ar-tfjs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "Apache-2.0", 12 | "dependencies": { 13 | "@tensorflow-models/face-landmarks-detection": "0.0.3", 14 | "@tensorflow-models/handpose": "0.0.6", 15 | "@tensorflow/tfjs-backend-webgl": "2.1.0", 16 | "@tensorflow/tfjs-converter": "2.1.0", 17 | "@tensorflow/tfjs-core": "2.1.0", 18 | "abab": "2.0.0", 19 | "base64-js": "1.3.1", 20 | "fetch-wechat": "0.0.3", 21 | "text-encoder": "0.0.4", 22 | "threejs-miniprogram": "0.0.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /package_face_2d_mask/pages/camera/camera.js: -------------------------------------------------------------------------------- 1 | const face = require('../../utils/faceBusiness.js') 2 | const model = require('../../utils/modelBusiness.js'); 3 | const canvasWebGLId = 'canvasWebGL'; 4 | // throttling 5 | const cameraFrameMax = 3; 6 | // a url of gltf model 7 | const modelUrl = '../../utils/cat_beard.png'; 8 | // camera listener 9 | var listener = null; 10 | 11 | Page({ 12 | data: { 13 | devicePosition: 'front', 14 | }, 15 | async onReady() { 16 | var _that = this; 17 | wx.showLoading({ 18 | title: 'Loading TFJS...', 19 | }); 20 | await face.loadModel(); 21 | wx.hideLoading(); 22 | // load 3d model 23 | model.initThree(canvasWebGLId, modelUrl); 24 | _that.startTacking(); 25 | }, 26 | onUnload: function () { 27 | this.stopTacking(); 28 | console.log('onUnload', 'listener is stop'); 29 | 30 | model.stopAnimate(); 31 | model.dispose(); 32 | }, 33 | startTacking() { 34 | var _that = this; 35 | var count = 0; 36 | const context = wx.createCameraContext(); 37 | 38 | // real-time 39 | listener = context.onCameraFrame(async function (res) { 40 | // it is throttling 41 | if (count < cameraFrameMax) { 42 | count++; 43 | return; 44 | } 45 | count = 0; 46 | console.log('onCameraFrame:', res.width, res.height); 47 | const frame = { 48 | data: new Uint8Array(res.data), 49 | width: res.width, 50 | height: res.height, 51 | }; 52 | 53 | // process 54 | var result = await face.detect(frame); 55 | 56 | if (result && result.prediction) { 57 | var canvasWidth = frame.width; 58 | var canvasHeight = frame.height; 59 | 60 | // set the rotation and position of the 3d model. 61 | model.setModel(result.prediction, 62 | canvasWidth, 63 | canvasHeight); 64 | } else { 65 | var message = 'No results.'; 66 | wx.showToast({ 67 | title: message, 68 | icon: 'none' 69 | }); 70 | } 71 | }); 72 | // start 73 | listener.start(); 74 | console.log('startTacking', 'listener is start'); 75 | }, 76 | stopTacking() { 77 | if (listener) { 78 | listener.stop(); 79 | } 80 | }, 81 | changeDirection() { 82 | var status = this.data.devicePosition; 83 | if (status === 'back') { 84 | status = 'front'; 85 | } else { 86 | status = 'back'; 87 | } 88 | this.setData({ 89 | devicePosition: status, 90 | }); 91 | } 92 | }) 93 | -------------------------------------------------------------------------------- /package_face_2d_mask/pages/camera/camera.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "Access a camera" 3 | } 4 | -------------------------------------------------------------------------------- /package_face_2d_mask/pages/camera/camera.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /package_face_2d_mask/pages/camera/camera.wxss: -------------------------------------------------------------------------------- 1 | .page__bd{ 2 | padding-bottom:0; 3 | } 4 | .page__bd_spacing{ 5 | padding-top:10px; 6 | } 7 | camera{ 8 | width: 375px; 9 | height: 500px; 10 | z-index: 1; 11 | margin:auto; 12 | } 13 | 14 | .canvasWebGL{ 15 | width: 100%; 16 | height: 100%; 17 | z-index: 3; 18 | margin:auto; 19 | } 20 | -------------------------------------------------------------------------------- /package_face_2d_mask/pages/photo/photo.js: -------------------------------------------------------------------------------- 1 | const face = require('../../utils/faceBusiness.js'); 2 | const model = require('../../utils/modelBusiness.js'); 3 | const canvasId = 'canvas2d'; 4 | const canvasWebGLId = 'canvasWebGL'; 5 | const maxCanvasWidth = 375; 6 | // a url of sprite image 7 | const modelUrl = '../../utils/cat_beard.png'; 8 | 9 | Page({ 10 | data: { 11 | btnText: 'Take a photo', 12 | devicePosition: 'front', 13 | // if it is taking photo 14 | isRunning: true, 15 | }, 16 | async onReady() { 17 | // load tfjs model 18 | wx.showLoading({ 19 | title: 'Loading TFJS...', 20 | }); 21 | await face.loadModel(); 22 | wx.hideLoading(); 23 | 24 | // load 3d model 25 | model.initThree(canvasWebGLId, modelUrl); 26 | }, 27 | onUnload: function () { 28 | model.stopAnimate(); 29 | model.dispose(); 30 | }, 31 | processPhoto(photoPath, imageWidth, imageHeight) { 32 | var _that = this; 33 | const ctx = wx.createCanvasContext(canvasId); 34 | // the width of the scale image 35 | var canvasWidth = imageWidth; 36 | if (canvasWidth > maxCanvasWidth) { 37 | canvasWidth = maxCanvasWidth; 38 | } 39 | // the height of the scale image 40 | var canvasHeight = Math.floor(canvasWidth * (imageHeight / imageWidth)); 41 | // draw image on canvas 42 | ctx.drawImage(photoPath, 0, 0, canvasWidth, canvasHeight); 43 | // waiting for drawing 44 | ctx.draw(false, function () { 45 | // get image data from canvas 46 | wx.canvasGetImageData({ 47 | canvasId: canvasId, 48 | x: 0, 49 | y: 0, 50 | width: canvasWidth, 51 | height: canvasHeight, 52 | async success(res) { 53 | console.log('size of frame:', res.width, res.height); 54 | const frame = { 55 | data: new Uint8Array(res.data), 56 | width: res.width, 57 | height: res.height, 58 | }; 59 | // process 60 | var result = await face.detect(frame); 61 | 62 | if (result && result.prediction) { 63 | // set the rotation and position of the 3d model. 64 | model.setModel(result.prediction, 65 | canvasWidth, 66 | canvasHeight); 67 | // put the 3d model on the image 68 | model.setSceneBackground(frame); 69 | } else { 70 | var message = 'No results.'; 71 | wx.showToast({ 72 | title: message, 73 | icon: 'none' 74 | }); 75 | } 76 | 77 | 78 | } 79 | }); 80 | }); 81 | }, 82 | takePhoto() { 83 | var _that = this; 84 | const context = wx.createCameraContext(); 85 | const ctx = wx.createCanvasContext(canvasId); 86 | if (_that.data.isRunning) { 87 | _that.setData({ 88 | btnText: 'Retry', 89 | isRunning: false, 90 | }); 91 | // take a photo 92 | context.takePhoto({ 93 | quality: 'normal', 94 | success: (res) => { 95 | var photoPath = res.tempImagePath; 96 | //get size of image 97 | wx.getImageInfo({ 98 | src: photoPath, 99 | success(res) { 100 | console.log('size of image:', res.width, res.height); 101 | _that.processPhoto(photoPath, res.width, res.height); 102 | } 103 | }); 104 | } 105 | }); 106 | } 107 | else { 108 | _that.setData({ 109 | btnText: 'Take a photo', 110 | isRunning: true, 111 | }); 112 | // clear 2d canvas 113 | ctx.clearRect(0, 0); 114 | ctx.draw(); 115 | // clear 3d canvas 116 | model.clearSceneBackground(); 117 | } 118 | }, 119 | changeDirection() { 120 | var status = this.data.devicePosition; 121 | if (status === 'back') { 122 | status = 'front'; 123 | } else { 124 | status = 'back'; 125 | } 126 | this.setData({ 127 | devicePosition: status, 128 | }); 129 | } 130 | }); 131 | -------------------------------------------------------------------------------- /package_face_2d_mask/pages/photo/photo.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "Take a photo" 3 | } 4 | -------------------------------------------------------------------------------- /package_face_2d_mask/pages/photo/photo.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /package_face_2d_mask/pages/photo/photo.wxss: -------------------------------------------------------------------------------- 1 | .page__bd{ 2 | padding-bottom:0; 3 | } 4 | .page__bd_spacing{ 5 | padding-top:10px; 6 | } 7 | 8 | camera{ 9 | width: 375px; 10 | height: 500px; 11 | z-index: 1; 12 | margin:auto; 13 | } 14 | 15 | .canvasWebGL{ 16 | width: 375px; 17 | height: 500px; 18 | z-index: 3; 19 | margin:auto; 20 | } 21 | 22 | .canvas1{ 23 | width: 375px; 24 | height: 500px; 25 | z-index: 2; 26 | margin:auto; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /package_face_2d_mask/utils/cat_beard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-TFJS/5152b650c33a5fadc4be16496441ec0e02c79329/package_face_2d_mask/utils/cat_beard.png -------------------------------------------------------------------------------- /package_face_2d_mask/utils/faceBusiness.js: -------------------------------------------------------------------------------- 1 | const faceLandmarksDetection = require('@tensorflow-models/face-landmarks-detection'); 2 | require('@tensorflow/tfjs-backend-webgl'); 3 | const detectionConfidence = 0.8; 4 | const maxFaces = 1; 5 | var model; 6 | 7 | async function loadModel() { 8 | model = await faceLandmarksDetection.load( 9 | faceLandmarksDetection.SupportedPackages.mediapipeFacemesh, { 10 | shouldLoadIrisModel: false, 11 | detectionConfidence: detectionConfidence, 12 | maxFaces: maxFaces, 13 | }); 14 | console.log('facemesh model is loaded.'); 15 | } 16 | 17 | async function detect(frame) { 18 | if (!model) { 19 | console.log('facemesh model has not been loaded.'); 20 | return; 21 | } 22 | var start = new Date(); 23 | const predictions = await model.estimateFaces({ 24 | input: frame, 25 | predictIrises: false, 26 | }); 27 | var end = new Date() - start; 28 | console.log('detect', end, 'ms'); 29 | 30 | return { prediction: predictions[0] }; 31 | } 32 | 33 | module.exports = { loadModel, detect }; 34 | -------------------------------------------------------------------------------- /package_face_2d_mask/utils/modelBusiness.js: -------------------------------------------------------------------------------- 1 | const { createScopedThreejs } = require('threejs-miniprogram'); 2 | // the scale of the image 3 | const initScale = 240; 4 | // index of the track points of the face 5 | const trackPointA = 0; 6 | const trackPointB = 61; 7 | const trackPointC = 291; 8 | var camera, scene, renderer; 9 | var canvas; 10 | var THREE; 11 | var mainModel, requestId; 12 | var canvasWidth, canvasHeight; 13 | 14 | function initThree(canvasId, modelUrl) { 15 | wx.createSelectorQuery() 16 | .select('#' + canvasId) 17 | .node() 18 | .exec((res) => { 19 | canvas = res[0].node; 20 | THREE = createScopedThreejs(canvas); 21 | 22 | initScene(); 23 | loadModel(modelUrl); 24 | }); 25 | } 26 | 27 | function initScene() { 28 | camera = new THREE.OrthographicCamera(1, 1, 1, 1, -1000, 1000); 29 | // set the camera 30 | setSize(); 31 | scene = new THREE.Scene(); 32 | // ambient light 33 | scene.add(new THREE.AmbientLight(0xffffff)); 34 | // direction light 35 | var directionallight = new THREE.DirectionalLight(0xffffff, 1); 36 | directionallight.position.set(0, 0, 1000); 37 | scene.add(directionallight); 38 | 39 | // init render 40 | renderer = new THREE.WebGLRenderer({ 41 | antialias: true, 42 | alpha: true, 43 | }); 44 | const devicePixelRatio = wx.getSystemInfoSync().pixelRatio; 45 | console.log('device pixel ratio', devicePixelRatio); 46 | renderer.setPixelRatio(devicePixelRatio); 47 | renderer.setSize(canvas.width, canvas.height); 48 | 49 | animate(); 50 | } 51 | 52 | function loadModel(modelUrl) { 53 | wx.showLoading({ 54 | title: 'Loading Sprite...', 55 | }); 56 | const sprite_map = new THREE.TextureLoader().load(modelUrl); 57 | const sprite_material = new THREE.SpriteMaterial({ map: sprite_map }); 58 | var sprite = new THREE.Sprite(sprite_material); 59 | sprite.scale.setScalar(initScale); 60 | mainModel = sprite; 61 | scene.add(mainModel); 62 | wx.hideLoading(); 63 | console.log('loadSprite', 'success'); 64 | } 65 | 66 | function updateModel(modelUrl) { 67 | // loading 68 | wx.showLoading({ 69 | title: 'Loading Sprite...', 70 | }); 71 | // sprite 72 | const sprite_map = new THREE.TextureLoader().load(modelUrl); 73 | const sprite_material = new THREE.SpriteMaterial({ map: sprite_map }); 74 | var sprite = new THREE.Sprite(sprite_material); 75 | sprite.scale.setScalar(initScale); 76 | // remove old model 77 | scene.remove(mainModel); 78 | // save new model 79 | mainModel = sprite; 80 | // add new model 81 | scene.add(mainModel); 82 | wx.hideLoading(); 83 | console.log('updateSprite', 'success'); 84 | } 85 | 86 | function setSize() { 87 | if (!camera) { 88 | return; 89 | } 90 | const w = canvasWidth; 91 | const h = canvasHeight; 92 | camera.left = -0.5 * w; 93 | camera.right = 0.5 * w; 94 | camera.top = 0.5 * h; 95 | camera.bottom = -0.5 * h; 96 | camera.updateProjectionMatrix(); 97 | } 98 | 99 | function setModel(prediction, 100 | _canvasWidth, 101 | _canvasHeight) { 102 | 103 | if (!mainModel) { 104 | console.log('setModel', '3d model is not loaded.'); 105 | return; 106 | } 107 | 108 | if (_canvasWidth !== canvasWidth) { 109 | canvasWidth = _canvasWidth; 110 | canvasHeight = _canvasHeight; 111 | setSize(); 112 | } 113 | 114 | const result = calcTriangle(prediction, 115 | trackPointA, 116 | trackPointB, 117 | trackPointC); 118 | console.log('calcTriangle', result); 119 | 120 | // rotation 121 | var rotation = new THREE.Euler(); 122 | rotation.setFromRotationMatrix(result.rotation); 123 | mainModel.material.rotation = rotation.y; 124 | // position 125 | mainModel.position.copy(result.position); 126 | // scale 127 | mainModel.scale.setScalar(initScale * result.scale); 128 | } 129 | 130 | function getPosition(prediction, id) { 131 | var p = prediction.scaledMesh[id]; 132 | var x = p[0] - 0.5 * canvasWidth; 133 | var y = 0.5 * canvasHeight - p[1]; 134 | var z = p[2]; 135 | return new THREE.Vector3(x, y, z); 136 | } 137 | 138 | function getScale(prediction, id1, id2) { 139 | var p1 = prediction.mesh[id1]; 140 | var p1_scaled = prediction.scaledMesh[id1]; 141 | var p2 = prediction.mesh[id2]; 142 | var p2_scaled = prediction.scaledMesh[id2]; 143 | 144 | var a = p2[0] - p1[0]; 145 | var b = p2_scaled[0] - p1_scaled[0]; 146 | return b / a; 147 | } 148 | 149 | function calcTriangle(prediction, id0, id1, id2) { 150 | var p0 = getPosition(prediction, id0); 151 | var p1 = getPosition(prediction, id1); 152 | var p2 = getPosition(prediction, id2); 153 | 154 | // position 155 | var triangle = new THREE.Triangle(); 156 | triangle.set(p0, p1, p2); 157 | const center = new THREE.Vector3(); 158 | triangle.getMidpoint(center); 159 | 160 | // rotation 161 | const rotation = new THREE.Matrix4(); 162 | const x = p1.clone().sub(p2).normalize(); 163 | const y = p1.clone().sub(p0).normalize(); 164 | const z = new THREE.Vector3().crossVectors(x, y); 165 | const y2 = new THREE.Vector3().crossVectors(x, z).normalize(); 166 | const z2 = new THREE.Vector3().crossVectors(x, y2).normalize(); 167 | rotation.makeBasis(x, y2, z2); 168 | 169 | // scale 170 | var scale = getScale(prediction, id1, id2); 171 | 172 | return { 173 | position: center, 174 | rotation: rotation, 175 | scale: scale, 176 | }; 177 | } 178 | 179 | function setSceneBackground(frame) { 180 | var texture = new THREE.DataTexture(frame.data, 181 | frame.width, 182 | frame.height, 183 | THREE.RGBAFormat); 184 | texture.flipY = true; 185 | texture.needsUpdate = true; 186 | scene.background = texture; 187 | } 188 | 189 | function clearSceneBackground() { 190 | scene.background = null; 191 | } 192 | 193 | function animate() { 194 | requestId = canvas.requestAnimationFrame(animate); 195 | renderer.render(scene, camera); 196 | } 197 | 198 | function stopAnimate() { 199 | if (canvas && requestId) { 200 | canvas.cancelAnimationFrame(requestId); 201 | } 202 | } 203 | 204 | function dispose() { 205 | camera = null; 206 | scene = null; 207 | renderer = null; 208 | canvas = null; 209 | THREE = null; 210 | mainModel = null; 211 | requestId = null; 212 | canvasWidth = null; 213 | canvasHeight = null; 214 | } 215 | 216 | module.exports = { 217 | initThree, 218 | stopAnimate, 219 | updateModel, 220 | setModel, 221 | setSceneBackground, 222 | clearSceneBackground, 223 | dispose, 224 | } -------------------------------------------------------------------------------- /package_face_3d_mask/pages/camera/camera.js: -------------------------------------------------------------------------------- 1 | const face = require('../../utils/faceBusiness.js') 2 | const model = require('../../utils/modelBusiness.js'); 3 | const canvasWebGLId = 'canvasWebGL'; 4 | // throttling 5 | const cameraFrameMax = 3; 6 | // a url of gltf model 7 | const modelUrl = 'https://m.sanyue.red/demo/gltf/sunglass.glb'; 8 | // camera listener 9 | var listener = null; 10 | 11 | Page({ 12 | data: { 13 | devicePosition: 'front', 14 | }, 15 | async onReady() { 16 | var _that = this; 17 | wx.showLoading({ 18 | title: 'Loading TFJS...', 19 | }); 20 | await face.loadModel(); 21 | wx.hideLoading(); 22 | 23 | // load 3d model 24 | model.initThree(canvasWebGLId, modelUrl); 25 | 26 | _that.startTacking(); 27 | }, 28 | onUnload: function () { 29 | this.stopTacking(); 30 | console.log('onUnload', 'listener is stop'); 31 | 32 | model.stopAnimate(); 33 | model.dispose(); 34 | }, 35 | startTacking() { 36 | var _that = this; 37 | var count = 0; 38 | const context = wx.createCameraContext(); 39 | 40 | // real-time 41 | listener = context.onCameraFrame(async function (res) { 42 | // it is throttling 43 | if (count < cameraFrameMax) { 44 | count++; 45 | return; 46 | } 47 | count = 0; 48 | console.log('onCameraFrame:', res.width, res.height); 49 | const frame = { 50 | data: new Uint8Array(res.data), 51 | width: res.width, 52 | height: res.height, 53 | }; 54 | 55 | // process 56 | var result = await face.detect(frame); 57 | 58 | if (result && result.prediction) { 59 | var canvasWidth = frame.width; 60 | var canvasHeight = frame.height; 61 | 62 | // set the rotation and position of the 3d model. 63 | model.setModel(result.prediction, 64 | canvasWidth, 65 | canvasHeight); 66 | } else { 67 | var message = 'No results.'; 68 | wx.showToast({ 69 | title: message, 70 | icon: 'none' 71 | }); 72 | } 73 | }); 74 | // start 75 | listener.start(); 76 | console.log('startTacking', 'listener is start'); 77 | }, 78 | stopTacking() { 79 | if (listener) { 80 | listener.stop(); 81 | } 82 | }, 83 | changeDirection() { 84 | var status = this.data.devicePosition; 85 | if (status === 'back') { 86 | status = 'front'; 87 | } else { 88 | status = 'back'; 89 | } 90 | this.setData({ 91 | devicePosition: status, 92 | }); 93 | } 94 | }) 95 | -------------------------------------------------------------------------------- /package_face_3d_mask/pages/camera/camera.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "Access a camera" 3 | } 4 | -------------------------------------------------------------------------------- /package_face_3d_mask/pages/camera/camera.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /package_face_3d_mask/pages/camera/camera.wxss: -------------------------------------------------------------------------------- 1 | .page__bd{ 2 | padding-bottom:0; 3 | } 4 | .page__bd_spacing{ 5 | padding-top:10px; 6 | } 7 | camera{ 8 | width: 375px; 9 | height: 500px; 10 | z-index: 1; 11 | margin:auto; 12 | } 13 | 14 | .canvasWebGL{ 15 | width: 100%; 16 | height: 100%; 17 | z-index: 3; 18 | margin:auto; 19 | } 20 | -------------------------------------------------------------------------------- /package_face_3d_mask/pages/photo/photo.js: -------------------------------------------------------------------------------- 1 | const face = require('../../utils/faceBusiness.js'); 2 | const model = require('../../utils/modelBusiness.js'); 3 | const canvasId = 'canvas2d'; 4 | const canvasWebGLId = 'canvasWebGL'; 5 | const maxCanvasWidth = 375; 6 | // a url of gltf model 7 | const modelUrl = 'https://m.sanyue.red/demo/gltf/sunglass.glb'; 8 | 9 | Page({ 10 | data: { 11 | btnText: 'Take a photo', 12 | devicePosition: 'front', 13 | // if it is taking photo 14 | isRunning: true, 15 | }, 16 | async onReady() { 17 | // load tfjs model 18 | wx.showLoading({ 19 | title: 'Loading TFJS...', 20 | }); 21 | await face.loadModel(); 22 | wx.hideLoading(); 23 | 24 | // load 3d model 25 | model.initThree(canvasWebGLId, modelUrl); 26 | }, 27 | onUnload: function () { 28 | model.stopAnimate(); 29 | model.dispose(); 30 | }, 31 | processPhoto(photoPath, imageWidth, imageHeight) { 32 | var _that = this; 33 | const ctx = wx.createCanvasContext(canvasId); 34 | // the width of the scale image 35 | var canvasWidth = imageWidth; 36 | if (canvasWidth > maxCanvasWidth) { 37 | canvasWidth = maxCanvasWidth; 38 | } 39 | // the height of the scale image 40 | var canvasHeight = Math.floor(canvasWidth * (imageHeight / imageWidth)); 41 | // draw image on canvas 42 | ctx.drawImage(photoPath, 0, 0, canvasWidth, canvasHeight); 43 | // waiting for drawing 44 | ctx.draw(false, function () { 45 | // get image data from canvas 46 | wx.canvasGetImageData({ 47 | canvasId: canvasId, 48 | x: 0, 49 | y: 0, 50 | width: canvasWidth, 51 | height: canvasHeight, 52 | async success(res) { 53 | console.log('size of frame:', res.width, res.height); 54 | const frame = { 55 | data: new Uint8Array(res.data), 56 | width: res.width, 57 | height: res.height, 58 | }; 59 | // process 60 | var result = await face.detect(frame); 61 | 62 | if (result && result.prediction) { 63 | // set the rotation and position of the 3d model. 64 | model.setModel(result.prediction, 65 | canvasWidth, 66 | canvasHeight); 67 | // put the 3d model on the image 68 | model.setSceneBackground(frame); 69 | } else { 70 | var message = 'No results.'; 71 | wx.showToast({ 72 | title: message, 73 | icon: 'none' 74 | }); 75 | } 76 | 77 | 78 | } 79 | }); 80 | }); 81 | }, 82 | takePhoto() { 83 | var _that = this; 84 | const context = wx.createCameraContext(); 85 | const ctx = wx.createCanvasContext(canvasId); 86 | if (_that.data.isRunning) { 87 | _that.setData({ 88 | btnText: 'Retry', 89 | isRunning: false, 90 | }); 91 | // take a photo 92 | context.takePhoto({ 93 | quality: 'normal', 94 | success: (res) => { 95 | var photoPath = res.tempImagePath; 96 | //get size of image 97 | wx.getImageInfo({ 98 | src: photoPath, 99 | success(res) { 100 | console.log('size of image:', res.width, res.height); 101 | _that.processPhoto(photoPath, res.width, res.height); 102 | } 103 | }); 104 | } 105 | }); 106 | } 107 | else { 108 | _that.setData({ 109 | btnText: 'Take a photo', 110 | isRunning: true, 111 | }); 112 | // clear 2d canvas 113 | ctx.clearRect(0, 0); 114 | ctx.draw(); 115 | // clear 3d canvas 116 | model.clearSceneBackground(); 117 | } 118 | }, 119 | changeDirection() { 120 | var status = this.data.devicePosition; 121 | if (status === 'back') { 122 | status = 'front'; 123 | } else { 124 | status = 'back'; 125 | } 126 | this.setData({ 127 | devicePosition: status, 128 | }); 129 | } 130 | }); 131 | -------------------------------------------------------------------------------- /package_face_3d_mask/pages/photo/photo.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "Take a photo" 3 | } 4 | -------------------------------------------------------------------------------- /package_face_3d_mask/pages/photo/photo.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /package_face_3d_mask/pages/photo/photo.wxss: -------------------------------------------------------------------------------- 1 | .page__bd{ 2 | padding-bottom:0; 3 | } 4 | .page__bd_spacing{ 5 | padding-top:10px; 6 | } 7 | 8 | camera{ 9 | width: 375px; 10 | height: 500px; 11 | z-index: 1; 12 | margin:auto; 13 | } 14 | 15 | .canvasWebGL{ 16 | width: 375px; 17 | height: 500px; 18 | z-index: 3; 19 | margin:auto; 20 | } 21 | 22 | .canvas1{ 23 | width: 375px; 24 | height: 500px; 25 | z-index: 2; 26 | margin:auto; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /package_face_3d_mask/utils/faceBusiness.js: -------------------------------------------------------------------------------- 1 | const faceLandmarksDetection = require('@tensorflow-models/face-landmarks-detection'); 2 | require('@tensorflow/tfjs-backend-webgl'); 3 | const detectionConfidence = 0.8; 4 | const maxFaces = 1; 5 | var model; 6 | 7 | async function loadModel() { 8 | model = await faceLandmarksDetection.load( 9 | faceLandmarksDetection.SupportedPackages.mediapipeFacemesh, { 10 | shouldLoadIrisModel: false, 11 | detectionConfidence: detectionConfidence, 12 | maxFaces: maxFaces, 13 | }); 14 | console.log('facemesh model is loaded.'); 15 | } 16 | 17 | async function detect(frame) { 18 | if (!model) { 19 | console.log('facemesh model has not been loaded.'); 20 | return; 21 | } 22 | var start = new Date(); 23 | const predictions = await model.estimateFaces({ 24 | input: frame, 25 | predictIrises: false, 26 | }); 27 | var end = new Date() - start; 28 | console.log('detect', end, 'ms'); 29 | 30 | return { prediction: predictions[0] }; 31 | } 32 | 33 | module.exports = { loadModel, detect }; 34 | -------------------------------------------------------------------------------- /package_face_3d_mask/utils/modelBusiness.js: -------------------------------------------------------------------------------- 1 | const { createScopedThreejs } = require('threejs-miniprogram'); 2 | const { registerGLTFLoader } = require('./GLTFLoader.js'); 3 | // the scale of the 3d model 4 | const initScale = 0.19; 5 | // index of the track points of the face 6 | const trackPointA = 168; 7 | const trackPointB = 122; 8 | const trackPointC = 351; 9 | var camera, scene, renderer; 10 | var canvas; 11 | var THREE; 12 | var mainModel, requestId; 13 | var canvasWidth, canvasHeight; 14 | 15 | function initThree(canvasId, modelUrl) { 16 | wx.createSelectorQuery() 17 | .select('#' + canvasId) 18 | .node() 19 | .exec((res) => { 20 | canvas = res[0].node; 21 | THREE = createScopedThreejs(canvas); 22 | 23 | initScene(); 24 | loadModel(modelUrl); 25 | }); 26 | } 27 | 28 | function initScene() { 29 | camera = new THREE.OrthographicCamera(1, 1, 1, 1, -1000, 1000); 30 | // set the camera 31 | setSize(); 32 | scene = new THREE.Scene(); 33 | // ambient light 34 | scene.add(new THREE.AmbientLight(0xffffff)); 35 | // direction light 36 | var directionallight = new THREE.DirectionalLight(0xffffff, 1); 37 | directionallight.position.set(0, 0, 1000); 38 | scene.add(directionallight); 39 | 40 | // init render 41 | renderer = new THREE.WebGLRenderer({ 42 | antialias: true, 43 | alpha: true, 44 | }); 45 | const devicePixelRatio = wx.getSystemInfoSync().pixelRatio; 46 | console.log('device pixel ratio', devicePixelRatio); 47 | renderer.setPixelRatio(devicePixelRatio); 48 | renderer.setSize(canvas.width, canvas.height); 49 | 50 | animate(); 51 | } 52 | 53 | function loadModel(modelUrl) { 54 | registerGLTFLoader(THREE); 55 | var loader = new THREE.GLTFLoader(); 56 | wx.showLoading({ 57 | title: 'Loading 3D...', 58 | }); 59 | loader.load(modelUrl, 60 | function (gltf) { 61 | console.log('loadModel', 'success'); 62 | var model = gltf.scene; 63 | model.scale.setScalar(initScale); 64 | // save model 65 | mainModel = model; 66 | scene.add(model); 67 | wx.hideLoading(); 68 | }, 69 | null, 70 | function (error) { 71 | console.log('loadModel', error); 72 | wx.hideLoading(); 73 | wx.showToast({ 74 | title: 'Loading model failed.', 75 | icon: 'none', 76 | duration: 3000, 77 | }); 78 | }); 79 | } 80 | 81 | function updateModel(modelUrl) { 82 | var loader = new THREE.GLTFLoader(); 83 | // loading 84 | wx.showLoading({ 85 | title: 'Loading 3D...', 86 | }); 87 | loader.load(modelUrl, 88 | function (gltf) { 89 | console.log('updateModel', 'success'); 90 | var model = gltf.scene; 91 | model.scale.setScalar(initScale); 92 | // remove old model 93 | scene.remove(mainModel); 94 | // save new model 95 | mainModel = model; 96 | // add new model 97 | scene.add(model); 98 | wx.hideLoading(); 99 | }, 100 | null, 101 | function (error) { 102 | console.log('updateModel', error); 103 | wx.hideLoading(); 104 | wx.showToast({ 105 | title: 'Loading model failed.', 106 | icon: 'none', 107 | duration: 3000, 108 | }); 109 | }); 110 | 111 | wx.hideLoading(); 112 | } 113 | 114 | function setSize() { 115 | if (!camera) { 116 | return; 117 | } 118 | const w = canvasWidth; 119 | const h = canvasHeight; 120 | camera.left = -0.5 * w; 121 | camera.right = 0.5 * w; 122 | camera.top = 0.5 * h; 123 | camera.bottom = -0.5 * h; 124 | camera.updateProjectionMatrix(); 125 | } 126 | 127 | function setModel(prediction, 128 | _canvasWidth, 129 | _canvasHeight) { 130 | 131 | if (!mainModel) { 132 | console.log('setModel', '3d model is not loaded.'); 133 | return; 134 | } 135 | 136 | if (_canvasWidth !== canvasWidth) { 137 | canvasWidth = _canvasWidth; 138 | canvasHeight = _canvasHeight; 139 | setSize(); 140 | } 141 | 142 | const result = calcTriangle(prediction, 143 | trackPointA, 144 | trackPointB, 145 | trackPointC); 146 | console.log('calcTriangle', result); 147 | 148 | 149 | // rotation 150 | mainModel.rotation.setFromRotationMatrix( 151 | result.rotation); 152 | // position 153 | mainModel.position.copy(result.position); 154 | // scal 155 | mainModel.scale.setScalar(initScale * result.scale); 156 | } 157 | 158 | function getPosition(prediction, id) { 159 | var p = prediction.scaledMesh[id]; 160 | var x = p[0] - 0.5 * canvasWidth; 161 | var y = 0.5 * canvasHeight - p[1]; 162 | var z = p[2]; 163 | return new THREE.Vector3(x, y, z); 164 | } 165 | 166 | function getScale(prediction, id1, id2) { 167 | var p1 = prediction.mesh[id1]; 168 | var p1_scaled = prediction.scaledMesh[id1]; 169 | var p2 = prediction.mesh[id2]; 170 | var p2_scaled = prediction.scaledMesh[id2]; 171 | 172 | var a = p2[0] - p1[0]; 173 | var b = p2_scaled[0] - p1_scaled[0]; 174 | return b / a; 175 | } 176 | 177 | function calcTriangle(prediction, id0, id1, id2) { 178 | var p0 = getPosition(prediction, id0); 179 | var p1 = getPosition(prediction, id1); 180 | var p2 = getPosition(prediction, id2); 181 | 182 | // position 183 | var triangle = new THREE.Triangle(); 184 | triangle.set(p0, p1, p2); 185 | const center = new THREE.Vector3(); 186 | triangle.getMidpoint(center); 187 | 188 | // rotation 189 | const rotation = new THREE.Matrix4(); 190 | const x = p1.clone().sub(p2).normalize(); 191 | const y = p1.clone().sub(p0).normalize(); 192 | const z = new THREE.Vector3().crossVectors(x, y); 193 | const y2 = new THREE.Vector3().crossVectors(x, z).normalize(); 194 | const z2 = new THREE.Vector3().crossVectors(x, y2).normalize(); 195 | rotation.makeBasis(x, y2, z2); 196 | 197 | // scale 198 | var scale = getScale(prediction, id1, id2); 199 | 200 | return { 201 | position: center, 202 | rotation: rotation, 203 | scale: scale, 204 | }; 205 | } 206 | 207 | function setSceneBackground(frame) { 208 | var texture = new THREE.DataTexture(frame.data, 209 | frame.width, 210 | frame.height, 211 | THREE.RGBAFormat); 212 | texture.flipY = true; 213 | texture.needsUpdate = true; 214 | scene.background = texture; 215 | } 216 | 217 | function clearSceneBackground() { 218 | scene.background = null; 219 | } 220 | 221 | function animate() { 222 | requestId = canvas.requestAnimationFrame(animate); 223 | renderer.render(scene, camera); 224 | } 225 | 226 | function stopAnimate() { 227 | if (canvas && requestId) { 228 | canvas.cancelAnimationFrame(requestId); 229 | } 230 | } 231 | 232 | function dispose() { 233 | camera = null; 234 | scene = null; 235 | renderer = null; 236 | canvas = null; 237 | THREE = null; 238 | mainModel = null; 239 | requestId = null; 240 | canvasWidth = null; 241 | canvasHeight = null; 242 | } 243 | 244 | module.exports = { 245 | initThree, 246 | stopAnimate, 247 | updateModel, 248 | setModel, 249 | setSceneBackground, 250 | clearSceneBackground, 251 | dispose, 252 | } -------------------------------------------------------------------------------- /package_handpose_mask/pages/camera/camera.js: -------------------------------------------------------------------------------- 1 | const hand = require('../../utils/handBusiness.js'); 2 | const model = require('../../utils/modelBusiness.js'); 3 | const canvasWebGLId = 'canvasWebGL'; 4 | // a url of gltf model 5 | const modelUrl = '../../utils/cat_beard.png'; 6 | // camera listener 7 | var listener = null; 8 | 9 | Page({ 10 | // throttling for Android 11 | intervalTimeout: 500, 12 | data: { 13 | devicePosition: 'back', 14 | }, 15 | onReady() { 16 | const system = wx.getSystemInfoSync().system; 17 | // if iOS 18 | if (system.indexOf('iOS') !== -1) { 19 | // throttling for iOS 20 | this.intervalTimeout = 3000; 21 | } 22 | }, 23 | async onLoad() { 24 | var _that = this; 25 | wx.showLoading({ 26 | title: 'Loading TFJS...', 27 | }); 28 | await hand.loadModel(); 29 | wx.hideLoading(); 30 | 31 | setTimeout(function () { 32 | // load 3d model 33 | model.initThree(canvasWebGLId, modelUrl); 34 | }, 150) 35 | _that.startTacking(); 36 | }, 37 | onUnload: function () { 38 | this.stopTacking(); 39 | console.log('onUnload', 'listener is stop'); 40 | 41 | model.stopAnimate(); 42 | model.dispose(); 43 | }, 44 | async onCameraFrame_callback(frame, 45 | canvasWidth, 46 | canvasHeight) { 47 | 48 | // process 49 | var result = await hand.detect(frame); 50 | 51 | if (result && result.prediction.length > 0) { 52 | var canvasWidth = frame.width; 53 | var canvasHeight = frame.height; 54 | 55 | // set the rotation and position of the 3d model. 56 | model.setModel(result.prediction[0], 57 | canvasWidth, 58 | canvasHeight); 59 | } else { 60 | var message = 'No results.'; 61 | console.log('onCameraFrame_callback', message) 62 | } 63 | 64 | }, 65 | startTacking() { 66 | var _that = this; 67 | const context = wx.createCameraContext(); 68 | 69 | // real-time 70 | var frameData; 71 | var canvasWidth; 72 | var canvasHeight; 73 | this.listener = context.onCameraFrame(function (res) { 74 | frameData = { 75 | data: new Uint8Array(res.data), 76 | width: res.width, 77 | height: res.height, 78 | };; 79 | canvasWidth = res.width; 80 | canvasHeight = res.height; 81 | // console.log('onCameraFrame:', res.width, res.height); 82 | }); 83 | 84 | this.intervalId = setInterval(function () { 85 | if (frameData) { 86 | _that.onCameraFrame_callback(frameData, 87 | canvasWidth, 88 | canvasHeight); 89 | } 90 | }, this.intervalTimeout); 91 | 92 | // start 93 | this.listener.start(); 94 | console.log('startTacking', 'listener is running'); 95 | }, 96 | stopTacking() { 97 | if (this.listener) { 98 | this.listener.stop(); 99 | this.listener = null; 100 | } 101 | clearInterval(this.intervalId); 102 | }, 103 | changeDirection() { 104 | var status = this.data.devicePosition; 105 | if (status === 'back') { 106 | status = 'front'; 107 | } else { 108 | status = 'back'; 109 | } 110 | this.setData({ 111 | devicePosition: status, 112 | }); 113 | } 114 | }) 115 | -------------------------------------------------------------------------------- /package_handpose_mask/pages/camera/camera.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "Access a camera" 3 | } 4 | -------------------------------------------------------------------------------- /package_handpose_mask/pages/camera/camera.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /package_handpose_mask/pages/camera/camera.wxss: -------------------------------------------------------------------------------- 1 | .page__bd{ 2 | padding-bottom:0; 3 | } 4 | .page__bd_spacing{ 5 | padding-top:10px; 6 | } 7 | camera{ 8 | width: 375px; 9 | height: 500px; 10 | z-index: 1; 11 | margin:auto; 12 | } 13 | -------------------------------------------------------------------------------- /package_handpose_mask/pages/photo/photo.js: -------------------------------------------------------------------------------- 1 | const hand = require('../../utils/handBusiness.js'); 2 | const model = require('../../utils/modelBusiness.js'); 3 | const canvasId = 'canvas2d' 4 | const canvasWebGLId = 'canvasWebGL'; 5 | const maxCanvasWidth = 375; 6 | // a url of gltf model 7 | const modelUrl = '../../utils/cat_beard.png'; 8 | 9 | Page({ 10 | data: { 11 | btnText: 'Take a photo', 12 | devicePosition: 'back', 13 | // if it is taking photo 14 | isRunning: true, 15 | }, 16 | async onLoad() { 17 | // var _that = this; 18 | // load tfjs model 19 | wx.showLoading({ 20 | title: 'Loading TFJS...', 21 | }); 22 | await hand.loadModel(); 23 | wx.hideLoading(); 24 | 25 | setTimeout(function () { 26 | // load 3d model 27 | model.initThree(canvasWebGLId, modelUrl); 28 | }, 150) 29 | 30 | }, 31 | onUnload: function () { 32 | model.stopAnimate(); 33 | model.dispose(); 34 | }, 35 | processPhoto(photoPath, imageWidth, imageHeight) { 36 | var _that = this; 37 | const ctx = wx.createCanvasContext(canvasId); 38 | // the width of the scale image 39 | var canvasWidth = imageWidth; 40 | if (canvasWidth > maxCanvasWidth) { 41 | canvasWidth = maxCanvasWidth; 42 | } 43 | // the height of the scale image 44 | var canvasHeight = Math.floor(canvasWidth * (imageHeight / imageWidth)); 45 | // draw image on canvas 46 | ctx.drawImage(photoPath, 0, 0, canvasWidth, canvasHeight); 47 | // waiting for drawing 48 | ctx.draw(false, function () { 49 | // get image data from canvas 50 | wx.canvasGetImageData({ 51 | canvasId: canvasId, 52 | x: 0, 53 | y: 0, 54 | width: canvasWidth, 55 | height: canvasHeight, 56 | async success(res) { 57 | console.log('size of frame:', res.width, res.height); 58 | const frame = { 59 | data: new Uint8Array(res.data), 60 | width: res.width, 61 | height: res.height, 62 | }; 63 | // process 64 | var result = await hand.detect(frame); 65 | 66 | if (result && result.prediction.length > 0) { 67 | // set the rotation and position of the 3d model. 68 | model.setModel(result.prediction[0], 69 | canvasWidth, 70 | canvasHeight); 71 | // put the 3d model on the image 72 | model.setSceneBackground(frame); 73 | } else { 74 | var message = 'No results.'; 75 | console.log('processPhoto', message) 76 | wx.showToast({ 77 | title: message, 78 | icon: 'none' 79 | }); 80 | } 81 | } 82 | }); 83 | }); 84 | }, 85 | takePhoto() { 86 | var _that = this; 87 | const context = wx.createCameraContext(); 88 | const ctx = wx.createCanvasContext(canvasId); 89 | if (_that.data.isRunning) { 90 | _that.setData({ 91 | btnText: 'Retry', 92 | isRunning: false, 93 | }); 94 | // take a photo 95 | context.takePhoto({ 96 | quality: 'normal', 97 | success: (res) => { 98 | var photoPath = res.tempImagePath; 99 | //get size of image 100 | wx.getImageInfo({ 101 | src: photoPath, 102 | success(res) { 103 | console.log('size of image:', res.width, res.height); 104 | _that.processPhoto(photoPath, res.width, res.height); 105 | } 106 | }); 107 | } 108 | }); 109 | } 110 | else { 111 | _that.setData({ 112 | btnText: 'Take a photo', 113 | isRunning: true, 114 | }); 115 | // clear 2d canvas 116 | ctx.clearRect(0, 0); 117 | ctx.draw(); 118 | // clear 3d canvas 119 | model.clearSceneBackground(); 120 | } 121 | }, 122 | changeDirection() { 123 | var status = this.data.devicePosition; 124 | if (status === 'back') { 125 | status = 'front'; 126 | } else { 127 | status = 'back'; 128 | } 129 | this.setData({ 130 | devicePosition: status, 131 | }); 132 | } 133 | }); 134 | -------------------------------------------------------------------------------- /package_handpose_mask/pages/photo/photo.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "Take a photo" 3 | } 4 | -------------------------------------------------------------------------------- /package_handpose_mask/pages/photo/photo.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /package_handpose_mask/pages/photo/photo.wxss: -------------------------------------------------------------------------------- 1 | .page__bd{ 2 | padding-bottom:0; 3 | } 4 | .page__bd_spacing{ 5 | padding-top:10px; 6 | } 7 | 8 | camera{ 9 | width: 375px; 10 | height: 500px; 11 | z-index: 1; 12 | margin:auto; 13 | } 14 | 15 | .canvasWebGL{ 16 | width: 375px; 17 | height: 500px; 18 | z-index: 3; 19 | margin:auto; 20 | } 21 | 22 | .canvas1{ 23 | width: 375px; 24 | height: 500px; 25 | z-index: 2; 26 | margin:auto; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /package_handpose_mask/utils/cat_beard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sanyuered/WeChat-MiniProgram-AR-TFJS/5152b650c33a5fadc4be16496441ec0e02c79329/package_handpose_mask/utils/cat_beard.png -------------------------------------------------------------------------------- /package_handpose_mask/utils/handBusiness.js: -------------------------------------------------------------------------------- 1 | const handpose = require('@tensorflow-models/handpose'); 2 | require('@tensorflow/tfjs-backend-webgl'); 3 | var model; 4 | const flipHorizontal = false; 5 | 6 | async function loadModel() { 7 | model = await handpose.load(); 8 | console.log('handpose model is loaded.'); 9 | } 10 | 11 | async function detect(frame) { 12 | if (!model) { 13 | console.log('handpose model has not been loaded.'); 14 | return; 15 | } 16 | var start = new Date(); 17 | const predictions = await model.estimateHands( 18 | frame, 19 | flipHorizontal 20 | ); 21 | var end = new Date() - start; 22 | console.log('detect', end, 'ms'); 23 | 24 | return { prediction: predictions }; 25 | } 26 | 27 | module.exports = { loadModel, detect }; 28 | -------------------------------------------------------------------------------- /package_handpose_mask/utils/modelBusiness.js: -------------------------------------------------------------------------------- 1 | const { createScopedThreejs } = require('threejs-miniprogram'); 2 | // the scale of the image 3 | const initScale = 240; 4 | // index of the track points of the face 5 | const trackPointA = 8; 6 | var camera, scene, renderer; 7 | var canvas; 8 | var THREE; 9 | var mainModel, requestId; 10 | var canvasWidth, canvasHeight; 11 | 12 | function initThree(canvasId, modelUrl) { 13 | wx.createSelectorQuery() 14 | .select('#' + canvasId) 15 | .node() 16 | .exec((res) => { 17 | canvas = res[0].node; 18 | THREE = createScopedThreejs(canvas); 19 | 20 | initScene(); 21 | loadModel(modelUrl); 22 | }); 23 | } 24 | 25 | function initScene() { 26 | camera = new THREE.OrthographicCamera(1, 1, 1, 1, -1000, 1000); 27 | // set the camera 28 | setSize(); 29 | scene = new THREE.Scene(); 30 | // ambient light 31 | scene.add(new THREE.AmbientLight(0xffffff)); 32 | // direction light 33 | var directionallight = new THREE.DirectionalLight(0xffffff, 1); 34 | directionallight.position.set(0, 0, 1000); 35 | scene.add(directionallight); 36 | 37 | // init render 38 | renderer = new THREE.WebGLRenderer({ 39 | antialias: true, 40 | alpha: true, 41 | }); 42 | const devicePixelRatio = wx.getSystemInfoSync().pixelRatio; 43 | console.log('device pixel ratio', devicePixelRatio); 44 | renderer.setPixelRatio(devicePixelRatio); 45 | renderer.setSize(canvas.width, canvas.height); 46 | 47 | animate(); 48 | } 49 | 50 | function loadModel(modelUrl) { 51 | wx.showLoading({ 52 | title: 'Loading Sprite...', 53 | }); 54 | const sprite_map = new THREE.TextureLoader().load(modelUrl); 55 | const sprite_material = new THREE.SpriteMaterial({ map: sprite_map }); 56 | var sprite = new THREE.Sprite(sprite_material); 57 | sprite.scale.setScalar(initScale); 58 | mainModel = sprite; 59 | scene.add(mainModel); 60 | wx.hideLoading(); 61 | console.log('loadSprite', 'success'); 62 | } 63 | 64 | function updateModel(modelUrl) { 65 | // loading 66 | wx.showLoading({ 67 | title: 'Loading Sprite...', 68 | }); 69 | // sprite 70 | const sprite_map = new THREE.TextureLoader().load(modelUrl); 71 | const sprite_material = new THREE.SpriteMaterial({ map: sprite_map }); 72 | var sprite = new THREE.Sprite(sprite_material); 73 | sprite.scale.setScalar(initScale); 74 | // remove old model 75 | scene.remove(mainModel); 76 | // save new model 77 | mainModel = sprite; 78 | // add new model 79 | scene.add(mainModel); 80 | wx.hideLoading(); 81 | console.log('updateSprite', 'success'); 82 | } 83 | 84 | function setSize() { 85 | if (!camera) { 86 | return; 87 | } 88 | const w = canvasWidth; 89 | const h = canvasHeight; 90 | camera.left = -0.5 * w; 91 | camera.right = 0.5 * w; 92 | camera.top = 0.5 * h; 93 | camera.bottom = -0.5 * h; 94 | camera.updateProjectionMatrix(); 95 | } 96 | 97 | function getPosition(prediction, id) { 98 | var p = prediction.landmarks[id]; 99 | var x = p[0] - 0.5 * canvasWidth; 100 | var y = 0.5 * canvasHeight - p[1]; 101 | var z = p[2]; 102 | return new THREE.Vector3(x, y, z); 103 | } 104 | 105 | function setModel(prediction, 106 | _canvasWidth, 107 | _canvasHeight) { 108 | 109 | if (!mainModel) { 110 | console.log('setModel', '3d model is not loaded.'); 111 | return; 112 | } 113 | 114 | if (_canvasWidth !== canvasWidth) { 115 | canvasWidth = _canvasWidth; 116 | canvasHeight = _canvasHeight; 117 | setSize(); 118 | } 119 | 120 | var position = getPosition(prediction, trackPointA); 121 | console.log('setModel', position); 122 | 123 | // position 124 | mainModel.position.copy(position); 125 | 126 | } 127 | 128 | function setSceneBackground(frame) { 129 | var texture = new THREE.DataTexture(frame.data, 130 | frame.width, 131 | frame.height, 132 | THREE.RGBAFormat); 133 | texture.flipY = true; 134 | texture.needsUpdate = true; 135 | scene.background = texture; 136 | } 137 | 138 | function clearSceneBackground() { 139 | scene.background = null; 140 | } 141 | 142 | function animate() { 143 | requestId = canvas.requestAnimationFrame(animate); 144 | renderer.render(scene, camera); 145 | } 146 | 147 | function stopAnimate() { 148 | if (canvas && requestId) { 149 | canvas.cancelAnimationFrame(requestId); 150 | } 151 | } 152 | 153 | function dispose() { 154 | camera = null; 155 | scene = null; 156 | renderer = null; 157 | canvas = null; 158 | THREE = null; 159 | mainModel = null; 160 | requestId = null; 161 | canvasWidth = null; 162 | canvasHeight = null; 163 | } 164 | 165 | module.exports = { 166 | initThree, 167 | stopAnimate, 168 | updateModel, 169 | setModel, 170 | setSceneBackground, 171 | clearSceneBackground, 172 | dispose, 173 | } -------------------------------------------------------------------------------- /package_mobilenet/pages/camera/camera.js: -------------------------------------------------------------------------------- 1 | const mobilenet = require('../../utils/mobilenet.js'); 2 | 3 | Page({ 4 | listener:null, 5 | // throttling 6 | intervalTimeout: 1000, 7 | mobilenetModel:null, 8 | data: { 9 | devicePosition: 'back', 10 | result:'', 11 | }, 12 | async onReady() { 13 | const system = wx.getSystemInfoSync().system; 14 | var _that = this; 15 | wx.showLoading({ 16 | title: 'Loading TFJS...', 17 | }); 18 | await this.initMobilenet() 19 | wx.hideLoading(); 20 | 21 | _that.startTacking(); 22 | }, 23 | onUnload: function () { 24 | this.stopTacking(); 25 | console.log('onUnload', 'listener is stop'); 26 | }, 27 | startTacking() { 28 | var _that = this; 29 | const context = wx.createCameraContext(); 30 | 31 | // real-time 32 | var frameData; 33 | var canvasWidth; 34 | var canvasHeight; 35 | this.listener = context.onCameraFrame(function (res) { 36 | frameData = { 37 | data: new Uint8Array(res.data), 38 | width: res.width, 39 | height: res.height, 40 | };; 41 | canvasWidth = res.width; 42 | canvasHeight = res.height; 43 | // console.log('onCameraFrame:', res.width, res.height); 44 | }); 45 | 46 | this.intervalId = setInterval(async function () { 47 | if (frameData) { 48 | // process 49 | await _that.executeMobilenet(frameData); 50 | } 51 | }, this.intervalTimeout); 52 | 53 | // start 54 | this.listener.start(); 55 | console.log('startTacking', 'listener is running'); 56 | }, 57 | stopTacking() { 58 | if (this.listener) { 59 | this.listener.stop(); 60 | this.listener = null; 61 | } 62 | clearInterval(this.intervalId); 63 | }, 64 | changeDirection() { 65 | var status = this.data.devicePosition; 66 | if (status === 'back') { 67 | status = 'front'; 68 | } else { 69 | status = 'back'; 70 | } 71 | this.setData({ 72 | devicePosition: status, 73 | }); 74 | }, 75 | async initMobilenet() { 76 | // Load the model. 77 | this.mobilenetModel = await mobilenet.load({ 78 | version:2, 79 | alpha:0.5, 80 | }); 81 | this.setData({ result: 'model loaded.' }); 82 | console.log('initMobilenet', 'model loaded'); 83 | }, 84 | async executeMobilenet(frame) { 85 | if (!this.mobilenetModel) { 86 | return 87 | } 88 | const img = frame 89 | // Classify the image. 90 | const predictions = await this.mobilenetModel.classify(img); 91 | this.setData({ result: predictions[0].className+",概率:"+predictions[0].probability.toFixed(2) }); 92 | console.log('executeMobilenet', predictions); 93 | }, 94 | }) 95 | -------------------------------------------------------------------------------- /package_mobilenet/pages/camera/camera.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "Access a camera" 3 | } 4 | -------------------------------------------------------------------------------- /package_mobilenet/pages/camera/camera.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 13 | 14 | 15 | 16 | 17 | {{result}} 18 | 19 | -------------------------------------------------------------------------------- /package_mobilenet/pages/camera/camera.wxss: -------------------------------------------------------------------------------- 1 | .page__bd{ 2 | padding-bottom:0; 3 | } 4 | .page__bd_spacing{ 5 | padding-top:10px; 6 | } 7 | camera{ 8 | width: 375px; 9 | height: 500px; 10 | z-index: 1; 11 | margin:auto; 12 | } 13 | 14 | .canvasWebGL{ 15 | width: 100%; 16 | height: 100%; 17 | z-index: 3; 18 | margin:auto; 19 | } 20 | -------------------------------------------------------------------------------- /package_mobilenet/pages/photo/photo.js: -------------------------------------------------------------------------------- 1 | const mobilenet = require('../../utils/mobilenet.js'); 2 | const canvasId = 'canvas2d' 3 | const maxCanvasWidth = 375; 4 | 5 | Page({ 6 | mobilenetModel: null, 7 | data: { 8 | btnText: 'Take a photo', 9 | devicePosition: 'back', 10 | // if it is taking photo 11 | isRunning: true, 12 | result: '', 13 | }, 14 | async onReady() { 15 | // var _that = this; 16 | // load tfjs model 17 | wx.showLoading({ 18 | title: 'Loading TFJS...', 19 | }); 20 | await this.initMobilenet() 21 | wx.hideLoading(); 22 | }, 23 | onUnload: function () { 24 | 25 | }, 26 | processPhoto(photoPath, imageWidth, imageHeight) { 27 | var _that = this; 28 | const ctx = wx.createCanvasContext(canvasId); 29 | // the width of the scale image 30 | var canvasWidth = imageWidth; 31 | if (canvasWidth > maxCanvasWidth) { 32 | canvasWidth = maxCanvasWidth; 33 | } 34 | // the height of the scale image 35 | var canvasHeight = Math.floor(canvasWidth * (imageHeight / imageWidth)); 36 | // draw image on canvas 37 | ctx.drawImage(photoPath, 0, 0, canvasWidth, canvasHeight); 38 | // waiting for drawing 39 | ctx.draw(false, function () { 40 | // get image data from canvas 41 | wx.canvasGetImageData({ 42 | canvasId: canvasId, 43 | x: 0, 44 | y: 0, 45 | width: canvasWidth, 46 | height: canvasHeight, 47 | async success(res) { 48 | console.log('size of frame:', res.width, res.height); 49 | const frame = { 50 | data: new Uint8Array(res.data), 51 | width: res.width, 52 | height: res.height, 53 | }; 54 | // process 55 | await _that.executeMobilenet(frame); 56 | 57 | } 58 | }); 59 | }); 60 | }, 61 | takePhoto() { 62 | var _that = this; 63 | const context = wx.createCameraContext(); 64 | const ctx = wx.createCanvasContext(canvasId); 65 | if (_that.data.isRunning) { 66 | _that.setData({ 67 | btnText: 'Retry', 68 | isRunning: false, 69 | }); 70 | // take a photo 71 | context.takePhoto({ 72 | quality: 'normal', 73 | success: (res) => { 74 | var photoPath = res.tempImagePath; 75 | //get size of image 76 | wx.getImageInfo({ 77 | src: photoPath, 78 | success(res) { 79 | console.log('size of image:', res.width, res.height); 80 | _that.processPhoto(photoPath, res.width, res.height); 81 | } 82 | }); 83 | } 84 | }); 85 | } 86 | else { 87 | _that.setData({ 88 | btnText: 'Take a photo', 89 | isRunning: true, 90 | }); 91 | // clear 2d canvas 92 | ctx.clearRect(0, 0); 93 | ctx.draw(); 94 | } 95 | }, 96 | changeDirection() { 97 | var status = this.data.devicePosition; 98 | if (status === 'back') { 99 | status = 'front'; 100 | } else { 101 | status = 'back'; 102 | } 103 | this.setData({ 104 | devicePosition: status, 105 | }); 106 | }, 107 | async initMobilenet() { 108 | // Load the model. 109 | this.mobilenetModel = await mobilenet.load({ 110 | version: 2, 111 | alpha: 0.5, 112 | }); 113 | this.setData({ result: 'model loaded.' }); 114 | console.log('initMobilenet', 'model loaded'); 115 | }, 116 | async executeMobilenet(frame) { 117 | if (!this.mobilenetModel) { 118 | return 119 | } 120 | const img = frame 121 | // Classify the image. 122 | const predictions = await this.mobilenetModel.classify(img); 123 | this.setData({ result: predictions[0].className + ",概率:" + predictions[0].probability.toFixed(2) }); 124 | console.log('executeMobilenet', predictions); 125 | }, 126 | }); 127 | -------------------------------------------------------------------------------- /package_mobilenet/pages/photo/photo.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "Take a photo" 3 | } 4 | -------------------------------------------------------------------------------- /package_mobilenet/pages/photo/photo.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | {{result}} 13 | 14 | 15 | -------------------------------------------------------------------------------- /package_mobilenet/pages/photo/photo.wxss: -------------------------------------------------------------------------------- 1 | .page__bd{ 2 | padding-bottom:0; 3 | } 4 | .page__bd_spacing{ 5 | padding-top:10px; 6 | } 7 | 8 | camera{ 9 | width: 375px; 10 | height: 500px; 11 | z-index: 1; 12 | margin:auto; 13 | } 14 | 15 | .canvas1{ 16 | width: 375px; 17 | height: 500px; 18 | z-index: 2; 19 | margin:auto; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /package_mobilenet/utils/mobilenet.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2019 Google LLC. All Rights Reserved. 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * ============================================================================= 16 | */ 17 | 18 | import * as tfconv from '@tensorflow/tfjs-converter'; 19 | import * as tf from '@tensorflow/tfjs-core'; 20 | 21 | import { IMAGENET_CLASSES } from './imagenet_classes.js'; 22 | 23 | const IMAGE_SIZE = 224; 24 | 25 | const EMBEDDING_NODES = { 26 | '1.00': 'module_apply_default/MobilenetV1/Logits/global_pool', 27 | '2.00': 'module_apply_default/MobilenetV2/Logits/AvgPool' 28 | }; 29 | 30 | const MODEL_INFO = { 31 | '1.00': { 32 | '0.25': { 33 | url: 34 | 'https://tfhub.dev/google/imagenet/mobilenet_v1_025_224/classification/1', 35 | inputRange: [0, 1] 36 | }, 37 | '0.50': { 38 | url: 39 | 'https://tfhub.dev/google/imagenet/mobilenet_v1_050_224/classification/1', 40 | inputRange: [0, 1] 41 | }, 42 | '0.75': { 43 | url: 44 | 'https://tfhub.dev/google/imagenet/mobilenet_v1_075_224/classification/1', 45 | inputRange: [0, 1] 46 | }, 47 | '1.00': { 48 | url: 49 | 'https://tfhub.dev/google/imagenet/mobilenet_v1_100_224/classification/1', 50 | inputRange: [0, 1] 51 | } 52 | }, // mobilenet_model_path 53 | '2.00': { 54 | '0.50': { 55 | url: 56 | 'https://m.sanyue.red/demo/tfjs/mobilenet_v2_050_224', 57 | inputRange: [0, 1] 58 | }, 59 | '0.75': { 60 | url: 61 | 'https://tfhub.dev/google/imagenet/mobilenet_v2_075_224/classification/2', 62 | inputRange: [0, 1] 63 | }, 64 | '1.00': { 65 | url: 66 | 'https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/classification/2', 67 | inputRange: [0, 1] 68 | } 69 | } 70 | }; 71 | 72 | const STORAGE_KEY = 'mobilenet_model'; 73 | 74 | // See ModelConfig documentation for expectations of provided fields. 75 | export async function load(modelConfig = { 76 | version: 1, 77 | alpha: 1.0 78 | }) { 79 | if (tf == null) { 80 | throw new Error( 81 | `Cannot find TensorFlow.js. If you are using a